Skip to content

Commit d387260

Browse files
committed
refactor: move 3.8+ compat code to compat.py
1 parent 6f82ea9 commit d387260

File tree

2 files changed

+65
-59
lines changed

2 files changed

+65
-59
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""Maintenance: We can drop this upon Py3.7 EOL. It's a backport for "location" key to work."""
2+
from __future__ import annotations
3+
4+
import io
5+
import logging
6+
import os
7+
import traceback
8+
9+
10+
def findCaller(stack_info=False, stacklevel=2): # pragma: no cover
11+
"""
12+
Find the stack frame of the caller so that we can note the source
13+
file name, line number and function name.
14+
"""
15+
f = logging.currentframe() # noqa: VNE001
16+
# On some versions of IronPython, currentframe() returns None if
17+
# IronPython isn't run with -X:Frames.
18+
if f is None:
19+
return "(unknown file)", 0, "(unknown function)", None
20+
while stacklevel > 0:
21+
next_f = f.f_back
22+
if next_f is None:
23+
## We've got options here.
24+
## If we want to use the last (deepest) frame:
25+
break
26+
## If we want to mimic the warnings module:
27+
# return ("sys", 1, "(unknown function)", None) # noqa: E800
28+
## If we want to be pedantic: # noqa: E800
29+
# raise ValueError("call stack is not deep enough") # noqa: E800
30+
f = next_f # noqa: VNE001
31+
if not _is_internal_frame(f):
32+
stacklevel -= 1
33+
co = f.f_code
34+
sinfo = None
35+
if stack_info:
36+
with io.StringIO() as sio:
37+
sio.write("Stack (most recent call last):\n")
38+
traceback.print_stack(f, file=sio)
39+
sinfo = sio.getvalue()
40+
if sinfo[-1] == "\n":
41+
sinfo = sinfo[:-1]
42+
return co.co_filename, f.f_lineno, co.co_name, sinfo
43+
44+
45+
# The following is based on warnings._is_internal_frame. It makes sure that
46+
# frames of the import mechanism are skipped when logging at module level and
47+
# using a stacklevel value greater than one.
48+
def _is_internal_frame(frame): # pragma: no cover
49+
"""Signal whether the frame is a CPython or logging module internal."""
50+
filename = os.path.normcase(frame.f_code.co_filename)
51+
return filename == logging._srcfile or ("importlib" in filename and "_bootstrap" in filename)

aws_lambda_powertools/logging/logger.py

Lines changed: 14 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22

33
import functools
44
import inspect
5-
import io
65
import logging
76
import os
87
import random
98
import sys
10-
import traceback
119
from typing import (
1210
IO,
1311
TYPE_CHECKING,
@@ -25,6 +23,8 @@
2523

2624
import jmespath
2725

26+
from aws_lambda_powertools.logging import compat
27+
2828
from ..shared import constants
2929
from ..shared.functions import (
3030
extract_event_from_common_models,
@@ -270,7 +270,7 @@ def _get_logger(self):
270270
"""Returns a Logger named {self.service}, or {self.service.filename} for child loggers"""
271271
logger_name = self.service
272272
if self.child:
273-
logger_name = f"{self.service}.{self._get_caller_filename()}"
273+
logger_name = f"{self.service}.{_get_caller_filename()}"
274274

275275
return logging.getLogger(logger_name)
276276

@@ -292,7 +292,7 @@ def _init_logger(self, formatter_options: Optional[Dict] = None, **kwargs):
292292
self.structure_logs(formatter_options=formatter_options, **kwargs)
293293

294294
# Maintenance: We can drop this upon Py3.7 EOL. It's a backport for "location" key to work
295-
self._logger.findCaller = self.findCaller
295+
self._logger.findCaller = compat.findCaller
296296

297297
# Pytest Live Log feature duplicates log records for colored output
298298
# but we explicitly add a filter for log deduplication.
@@ -655,51 +655,6 @@ def _get_log_level(level: Union[str, int, None]) -> Union[str, int]:
655655

656656
return log_level.upper()
657657

658-
@staticmethod
659-
def _get_caller_filename():
660-
"""Return caller filename by finding the caller frame"""
661-
# Current frame => _get_logger()
662-
# Previous frame => logger.py
663-
# Before previous frame => Caller
664-
frame = inspect.currentframe()
665-
caller_frame = frame.f_back.f_back.f_back
666-
return caller_frame.f_globals["__name__"]
667-
668-
# Maintenance: We can drop this upon Py3.7 EOL. It's a backport for "location" key to work
669-
def findCaller(self, stack_info=False, stacklevel=2): # pragma: no cover
670-
"""
671-
Find the stack frame of the caller so that we can note the source
672-
file name, line number and function name.
673-
"""
674-
f = logging.currentframe() # noqa: VNE001
675-
# On some versions of IronPython, currentframe() returns None if
676-
# IronPython isn't run with -X:Frames.
677-
if f is None:
678-
return "(unknown file)", 0, "(unknown function)", None
679-
while stacklevel > 0:
680-
next_f = f.f_back
681-
if next_f is None:
682-
## We've got options here.
683-
## If we want to use the last (deepest) frame:
684-
break
685-
## If we want to mimic the warnings module:
686-
# return ("sys", 1, "(unknown function)", None) # noqa: E800
687-
## If we want to be pedantic: # noqa: E800
688-
# raise ValueError("call stack is not deep enough") # noqa: E800
689-
f = next_f # noqa: VNE001
690-
if not _is_internal_frame(f):
691-
stacklevel -= 1
692-
co = f.f_code
693-
sinfo = None
694-
if stack_info:
695-
with io.StringIO() as sio:
696-
sio.write("Stack (most recent call last):\n")
697-
traceback.print_stack(f, file=sio)
698-
sinfo = sio.getvalue()
699-
if sinfo[-1] == "\n":
700-
sinfo = sinfo[:-1]
701-
return co.co_filename, f.f_lineno, co.co_name, sinfo
702-
703658

704659
def set_package_logger(
705660
level: Union[str, int] = logging.DEBUG,
@@ -740,16 +695,16 @@ def set_package_logger(
740695
logger.addHandler(handler)
741696

742697

743-
# Maintenance: We can drop this upon Py3.7 EOL. It's a backport for "location" key to work
744-
# The following is based on warnings._is_internal_frame. It makes sure that
745-
# frames of the import mechanism are skipped when logging at module level and
746-
# using a stacklevel value greater than one.
747-
def _is_internal_frame(frame): # pragma: no cover
748-
"""Signal whether the frame is a CPython or logging module internal."""
749-
filename = os.path.normcase(frame.f_code.co_filename)
750-
return filename == logging._srcfile or ("importlib" in filename and "_bootstrap" in filename)
751-
752-
753698
def log_uncaught_exception_hook(exc_type, exc_value, exc_traceback, logger: Logger):
754699
"""Callback function for sys.excepthook to use Logger to log uncaught exceptions"""
755700
logger.exception(exc_value, exc_info=(exc_type, exc_value, exc_traceback)) # pragma: no cover
701+
702+
703+
def _get_caller_filename():
704+
"""Return caller filename by finding the caller frame"""
705+
# Current frame => _get_logger()
706+
# Previous frame => logger.py
707+
# Before previous frame => Caller
708+
frame = inspect.currentframe()
709+
caller_frame = frame.f_back.f_back.f_back
710+
return caller_frame.f_globals["__name__"]

0 commit comments

Comments
 (0)