omdev 0.0.0.dev429__py3-none-any.whl → 0.0.0.dev431__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omdev/scripts/ci.py +1016 -719
- omdev/scripts/interp.py +47 -13
- omdev/scripts/pyproject.py +999 -702
- {omdev-0.0.0.dev429.dist-info → omdev-0.0.0.dev431.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev429.dist-info → omdev-0.0.0.dev431.dist-info}/RECORD +9 -9
- {omdev-0.0.0.dev429.dist-info → omdev-0.0.0.dev431.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev429.dist-info → omdev-0.0.0.dev431.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev429.dist-info → omdev-0.0.0.dev431.dist-info}/licenses/LICENSE +0 -0
- {omdev-0.0.0.dev429.dist-info → omdev-0.0.0.dev431.dist-info}/top_level.txt +0 -0
omdev/scripts/ci.py
CHANGED
@@ -126,6 +126,13 @@ U = ta.TypeVar('U')
|
|
126
126
|
# ../../omlish/lite/timeouts.py
|
127
127
|
TimeoutLike = ta.Union['Timeout', ta.Type['Timeout.DEFAULT'], ta.Iterable['TimeoutLike'], float, None] # ta.TypeAlias
|
128
128
|
|
129
|
+
# ../../omlish/logs/infos.py
|
130
|
+
LoggingMsgFn = ta.Callable[[], ta.Union[str, tuple]] # ta.TypeAlias
|
131
|
+
LoggingExcInfoTuple = ta.Tuple[ta.Type[BaseException], BaseException, ta.Optional[types.TracebackType]] # ta.TypeAlias
|
132
|
+
LoggingExcInfo = ta.Union[BaseException, LoggingExcInfoTuple] # ta.TypeAlias
|
133
|
+
LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
|
134
|
+
LoggingContextInfo = ta.Any # ta.TypeAlias
|
135
|
+
|
129
136
|
# ../../omlish/sockets/bind.py
|
130
137
|
SocketBinderT = ta.TypeVar('SocketBinderT', bound='SocketBinder')
|
131
138
|
SocketBinderConfigT = ta.TypeVar('SocketBinderConfigT', bound='SocketBinder.Config')
|
@@ -149,9 +156,7 @@ InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
|
|
149
156
|
InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
150
157
|
|
151
158
|
# ../../omlish/logs/contexts.py
|
152
|
-
|
153
|
-
LoggingExcInfo = ta.Union[BaseException, LoggingExcInfoTuple] # ta.TypeAlias
|
154
|
-
LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
|
159
|
+
LoggingContextInfoT = ta.TypeVar('LoggingContextInfoT', bound=LoggingContextInfo)
|
155
160
|
|
156
161
|
# ../../omlish/sockets/server/handlers.py
|
157
162
|
SocketServerHandler = ta.Callable[['SocketAndAddress'], None] # ta.TypeAlias
|
@@ -162,9 +167,6 @@ DataServerTargetT = ta.TypeVar('DataServerTargetT', bound='DataServerTarget')
|
|
162
167
|
# ../../omlish/http/coro/server/server.py
|
163
168
|
CoroHttpServerFactory = ta.Callable[[SocketAddress], 'CoroHttpServer']
|
164
169
|
|
165
|
-
# ../../omlish/logs/base.py
|
166
|
-
LoggingMsgFn = ta.Callable[[], ta.Union[str, tuple]] # ta.TypeAlias
|
167
|
-
|
168
170
|
# ../../omlish/subprocesses/base.py
|
169
171
|
SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
|
170
172
|
|
@@ -1800,124 +1802,6 @@ def format_num_bytes(num_bytes: int) -> str:
|
|
1800
1802
|
return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
|
1801
1803
|
|
1802
1804
|
|
1803
|
-
########################################
|
1804
|
-
# ../../../omlish/logs/infos.py
|
1805
|
-
|
1806
|
-
|
1807
|
-
##
|
1808
|
-
|
1809
|
-
|
1810
|
-
def logging_context_info(cls):
|
1811
|
-
return cls
|
1812
|
-
|
1813
|
-
|
1814
|
-
##
|
1815
|
-
|
1816
|
-
|
1817
|
-
@logging_context_info
|
1818
|
-
@ta.final
|
1819
|
-
class LoggingSourceFileInfo(ta.NamedTuple):
|
1820
|
-
file_name: str
|
1821
|
-
module: str
|
1822
|
-
|
1823
|
-
@classmethod
|
1824
|
-
def build(cls, file_path: ta.Optional[str]) -> ta.Optional['LoggingSourceFileInfo']:
|
1825
|
-
if file_path is None:
|
1826
|
-
return None
|
1827
|
-
|
1828
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L331-L336 # noqa
|
1829
|
-
try:
|
1830
|
-
file_name = os.path.basename(file_path)
|
1831
|
-
module = os.path.splitext(file_name)[0]
|
1832
|
-
except (TypeError, ValueError, AttributeError):
|
1833
|
-
return None
|
1834
|
-
|
1835
|
-
return cls(
|
1836
|
-
file_name,
|
1837
|
-
module,
|
1838
|
-
)
|
1839
|
-
|
1840
|
-
|
1841
|
-
##
|
1842
|
-
|
1843
|
-
|
1844
|
-
@logging_context_info
|
1845
|
-
@ta.final
|
1846
|
-
class LoggingThreadInfo(ta.NamedTuple):
|
1847
|
-
ident: int
|
1848
|
-
native_id: ta.Optional[int]
|
1849
|
-
name: str
|
1850
|
-
|
1851
|
-
@classmethod
|
1852
|
-
def build(cls) -> 'LoggingThreadInfo':
|
1853
|
-
return cls(
|
1854
|
-
threading.get_ident(),
|
1855
|
-
threading.get_native_id() if hasattr(threading, 'get_native_id') else None,
|
1856
|
-
threading.current_thread().name,
|
1857
|
-
)
|
1858
|
-
|
1859
|
-
|
1860
|
-
##
|
1861
|
-
|
1862
|
-
|
1863
|
-
@logging_context_info
|
1864
|
-
@ta.final
|
1865
|
-
class LoggingProcessInfo(ta.NamedTuple):
|
1866
|
-
pid: int
|
1867
|
-
|
1868
|
-
@classmethod
|
1869
|
-
def build(cls) -> 'LoggingProcessInfo':
|
1870
|
-
return cls(
|
1871
|
-
os.getpid(),
|
1872
|
-
)
|
1873
|
-
|
1874
|
-
|
1875
|
-
##
|
1876
|
-
|
1877
|
-
|
1878
|
-
@logging_context_info
|
1879
|
-
@ta.final
|
1880
|
-
class LoggingMultiprocessingInfo(ta.NamedTuple):
|
1881
|
-
process_name: str
|
1882
|
-
|
1883
|
-
@classmethod
|
1884
|
-
def build(cls) -> ta.Optional['LoggingMultiprocessingInfo']:
|
1885
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L355-L364 # noqa
|
1886
|
-
if (mp := sys.modules.get('multiprocessing')) is None:
|
1887
|
-
return None
|
1888
|
-
|
1889
|
-
return cls(
|
1890
|
-
mp.current_process().name,
|
1891
|
-
)
|
1892
|
-
|
1893
|
-
|
1894
|
-
##
|
1895
|
-
|
1896
|
-
|
1897
|
-
@logging_context_info
|
1898
|
-
@ta.final
|
1899
|
-
class LoggingAsyncioTaskInfo(ta.NamedTuple):
|
1900
|
-
name: str
|
1901
|
-
|
1902
|
-
@classmethod
|
1903
|
-
def build(cls) -> ta.Optional['LoggingAsyncioTaskInfo']:
|
1904
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L372-L377 # noqa
|
1905
|
-
if (asyncio := sys.modules.get('asyncio')) is None:
|
1906
|
-
return None
|
1907
|
-
|
1908
|
-
try:
|
1909
|
-
task = asyncio.current_task()
|
1910
|
-
except Exception: # noqa
|
1911
|
-
return None
|
1912
|
-
|
1913
|
-
if task is None:
|
1914
|
-
return None
|
1915
|
-
|
1916
|
-
return cls(
|
1917
|
-
task.get_name(), # Always non-None
|
1918
|
-
)
|
1919
|
-
|
1920
|
-
|
1921
1805
|
########################################
|
1922
1806
|
# ../../../omlish/logs/levels.py
|
1923
1807
|
|
@@ -1937,36 +1821,66 @@ class NamedLogLevel(int):
|
|
1937
1821
|
|
1938
1822
|
#
|
1939
1823
|
|
1940
|
-
|
1941
|
-
|
1942
|
-
|
1824
|
+
_CACHE: ta.ClassVar[ta.MutableMapping[int, 'NamedLogLevel']] = {}
|
1825
|
+
|
1826
|
+
@ta.overload
|
1827
|
+
def __new__(cls, name: str, offset: int = 0, /) -> 'NamedLogLevel':
|
1828
|
+
...
|
1829
|
+
|
1830
|
+
@ta.overload
|
1831
|
+
def __new__(cls, i: int, /) -> 'NamedLogLevel':
|
1832
|
+
...
|
1943
1833
|
|
1944
|
-
|
1834
|
+
def __new__(cls, x, offset=0, /):
|
1835
|
+
if isinstance(x, str):
|
1836
|
+
return cls(cls._INTS_BY_NAME[x.upper()] + offset)
|
1837
|
+
elif not offset and (c := cls._CACHE.get(x)) is not None:
|
1838
|
+
return c
|
1839
|
+
else:
|
1840
|
+
return super().__new__(cls, x + offset)
|
1841
|
+
|
1842
|
+
#
|
1843
|
+
|
1844
|
+
_name_and_offset: ta.Tuple[str, int]
|
1945
1845
|
|
1946
1846
|
@property
|
1947
|
-
def
|
1847
|
+
def name_and_offset(self) -> ta.Tuple[str, int]:
|
1948
1848
|
try:
|
1949
|
-
return self.
|
1849
|
+
return self._name_and_offset
|
1950
1850
|
except AttributeError:
|
1951
1851
|
pass
|
1952
1852
|
|
1953
|
-
if (n := self.
|
1853
|
+
if (n := self._NAMES_BY_INT.get(self)) is not None:
|
1854
|
+
t = (n, 0)
|
1855
|
+
else:
|
1954
1856
|
for n, i in self._NAME_INT_PAIRS: # noqa
|
1955
1857
|
if self >= i:
|
1858
|
+
t = (n, (self - i))
|
1956
1859
|
break
|
1957
1860
|
else:
|
1958
|
-
|
1861
|
+
t = ('NOTSET', int(self))
|
1862
|
+
|
1863
|
+
self._name_and_offset = t
|
1864
|
+
return t
|
1865
|
+
|
1866
|
+
@property
|
1867
|
+
def exact_name(self) -> ta.Optional[str]:
|
1868
|
+
n, o = self.name_and_offset
|
1869
|
+
return n if not o else None
|
1959
1870
|
|
1960
|
-
|
1871
|
+
@property
|
1872
|
+
def effective_name(self) -> str:
|
1873
|
+
n, _ = self.name_and_offset
|
1961
1874
|
return n
|
1962
1875
|
|
1963
1876
|
#
|
1964
1877
|
|
1965
|
-
def __repr__(self) -> str:
|
1966
|
-
return f'{self.__class__.__name__}({int(self)})'
|
1967
|
-
|
1968
1878
|
def __str__(self) -> str:
|
1969
|
-
return self.exact_name or f'{self.effective_name
|
1879
|
+
return self.exact_name or f'{self.effective_name}{int(self):+}'
|
1880
|
+
|
1881
|
+
def __repr__(self) -> str:
|
1882
|
+
n, o = self.name_and_offset
|
1883
|
+
return f'{self.__class__.__name__}({n!r}{f", {int(o)}" if o else ""})'
|
1970
1884
|
|
1971
1885
|
#
|
1972
1886
|
|
@@ -1986,6 +1900,9 @@ NamedLogLevel.DEBUG = NamedLogLevel(logging.DEBUG)
|
|
1986
1900
|
NamedLogLevel.NOTSET = NamedLogLevel(logging.NOTSET)
|
1987
1901
|
|
1988
1902
|
|
1903
|
+
NamedLogLevel._CACHE.update({i: NamedLogLevel(i) for i in NamedLogLevel._NAMES_BY_INT}) # noqa
|
1904
|
+
|
1905
|
+
|
1989
1906
|
########################################
|
1990
1907
|
# ../../../omlish/logs/std/filters.py
|
1991
1908
|
|
@@ -4947,223 +4864,338 @@ class PredicateTimeout(Timeout):
|
|
4947
4864
|
|
4948
4865
|
|
4949
4866
|
########################################
|
4950
|
-
# ../../../omlish/logs/
|
4867
|
+
# ../../../omlish/logs/infos.py
|
4868
|
+
"""
|
4869
|
+
TODO:
|
4870
|
+
- remove redundant info fields only present for std adaptation (Level.name, ...)
|
4871
|
+
"""
|
4951
4872
|
|
4952
4873
|
|
4953
4874
|
##
|
4954
4875
|
|
4955
4876
|
|
4956
|
-
|
4957
|
-
|
4958
|
-
class LoggingCaller(ta.NamedTuple):
|
4959
|
-
file_path: str
|
4960
|
-
line_no: int
|
4961
|
-
name: str
|
4962
|
-
stack_info: ta.Optional[str]
|
4877
|
+
def logging_context_info(cls):
|
4878
|
+
return cls
|
4963
4879
|
|
4964
|
-
@classmethod
|
4965
|
-
def is_internal_frame(cls, frame: types.FrameType) -> bool:
|
4966
|
-
file_path = os.path.normcase(frame.f_code.co_filename)
|
4967
4880
|
|
4968
|
-
|
4969
|
-
|
4970
|
-
|
4971
|
-
|
4972
|
-
return True
|
4881
|
+
@ta.final
|
4882
|
+
class LoggingContextInfos:
|
4883
|
+
def __new__(cls, *args, **kwargs): # noqa
|
4884
|
+
raise TypeError
|
4973
4885
|
|
4974
|
-
|
4886
|
+
#
|
4975
4887
|
|
4976
|
-
@
|
4977
|
-
|
4978
|
-
|
4888
|
+
@logging_context_info
|
4889
|
+
@ta.final
|
4890
|
+
class Name(ta.NamedTuple):
|
4891
|
+
name: str
|
4979
4892
|
|
4980
|
-
|
4981
|
-
|
4982
|
-
|
4983
|
-
|
4984
|
-
|
4893
|
+
@logging_context_info
|
4894
|
+
@ta.final
|
4895
|
+
class Level(ta.NamedTuple):
|
4896
|
+
level: NamedLogLevel
|
4897
|
+
name: str
|
4985
4898
|
|
4986
|
-
|
4899
|
+
@classmethod
|
4900
|
+
def build(cls, level: int) -> 'LoggingContextInfos.Level':
|
4901
|
+
nl: NamedLogLevel = level if level.__class__ is NamedLogLevel else NamedLogLevel(level) # type: ignore[assignment] # noqa
|
4902
|
+
return cls(
|
4903
|
+
level=nl,
|
4904
|
+
name=logging.getLevelName(nl),
|
4905
|
+
)
|
4987
4906
|
|
4988
|
-
|
4907
|
+
@logging_context_info
|
4908
|
+
@ta.final
|
4909
|
+
class Msg(ta.NamedTuple):
|
4910
|
+
msg: str
|
4911
|
+
args: ta.Union[tuple, ta.Mapping[ta.Any, ta.Any], None]
|
4989
4912
|
|
4990
|
-
|
4991
|
-
|
4992
|
-
|
4993
|
-
|
4994
|
-
|
4995
|
-
|
4996
|
-
|
4997
|
-
|
4998
|
-
|
4913
|
+
@classmethod
|
4914
|
+
def build(
|
4915
|
+
cls,
|
4916
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
4917
|
+
*args: ta.Any,
|
4918
|
+
) -> 'LoggingContextInfos.Msg':
|
4919
|
+
s: str
|
4920
|
+
a: ta.Any
|
4921
|
+
|
4922
|
+
if callable(msg):
|
4923
|
+
if args:
|
4924
|
+
raise TypeError(f'Must not provide both a message function and args: {msg=} {args=}')
|
4925
|
+
x = msg()
|
4926
|
+
if isinstance(x, str):
|
4927
|
+
s, a = x, ()
|
4928
|
+
elif isinstance(x, tuple):
|
4929
|
+
if x:
|
4930
|
+
s, a = x[0], x[1:]
|
4931
|
+
else:
|
4932
|
+
s, a = '', ()
|
4933
|
+
else:
|
4934
|
+
raise TypeError(x)
|
4999
4935
|
|
5000
|
-
|
5001
|
-
|
5002
|
-
|
5003
|
-
|
5004
|
-
|
5005
|
-
|
5006
|
-
|
5007
|
-
if sinfo[-1] == '\n':
|
5008
|
-
sinfo = sinfo[:-1]
|
4936
|
+
elif isinstance(msg, tuple):
|
4937
|
+
if args:
|
4938
|
+
raise TypeError(f'Must not provide both a tuple message and args: {msg=} {args=}')
|
4939
|
+
if msg:
|
4940
|
+
s, a = msg[0], msg[1:]
|
4941
|
+
else:
|
4942
|
+
s, a = '', ()
|
5009
4943
|
|
5010
|
-
|
5011
|
-
|
5012
|
-
f.f_lineno or 0,
|
5013
|
-
f.f_code.co_name,
|
5014
|
-
sinfo,
|
5015
|
-
)
|
4944
|
+
elif isinstance(msg, str):
|
4945
|
+
s, a = msg, args
|
5016
4946
|
|
4947
|
+
else:
|
4948
|
+
raise TypeError(msg)
|
5017
4949
|
|
5018
|
-
|
5019
|
-
|
4950
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L307 # noqa
|
4951
|
+
if a and len(a) == 1 and isinstance(a[0], collections.abc.Mapping) and a[0]:
|
4952
|
+
a = a[0]
|
5020
4953
|
|
4954
|
+
return cls(
|
4955
|
+
msg=s,
|
4956
|
+
args=a,
|
4957
|
+
)
|
5021
4958
|
|
5022
|
-
|
4959
|
+
@logging_context_info
|
4960
|
+
@ta.final
|
4961
|
+
class Extra(ta.NamedTuple):
|
4962
|
+
extra: ta.Mapping[ta.Any, ta.Any]
|
5023
4963
|
|
4964
|
+
@logging_context_info
|
4965
|
+
@ta.final
|
4966
|
+
class Time(ta.NamedTuple):
|
4967
|
+
ns: int
|
4968
|
+
secs: float
|
4969
|
+
msecs: float
|
4970
|
+
relative_secs: float
|
5024
4971
|
|
5025
|
-
|
5026
|
-
|
4972
|
+
@classmethod
|
4973
|
+
def get_std_start_ns(cls) -> int:
|
4974
|
+
x: ta.Any = logging._startTime # type: ignore[attr-defined] # noqa
|
5027
4975
|
|
5028
|
-
|
4976
|
+
# Before 3.13.0b1 this will be `time.time()`, a float of seconds. After that, it will be `time.time_ns()`,
|
4977
|
+
# an int.
|
4978
|
+
#
|
4979
|
+
# See:
|
4980
|
+
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
4981
|
+
#
|
4982
|
+
if isinstance(x, float):
|
4983
|
+
return int(x * 1e9)
|
4984
|
+
else:
|
4985
|
+
return x
|
5029
4986
|
|
5030
|
-
|
4987
|
+
@classmethod
|
4988
|
+
def build(
|
4989
|
+
cls,
|
4990
|
+
ns: int,
|
4991
|
+
*,
|
4992
|
+
start_ns: ta.Optional[int] = None,
|
4993
|
+
) -> 'LoggingContextInfos.Time':
|
4994
|
+
# https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
4995
|
+
secs = ns / 1e9 # ns to float seconds
|
4996
|
+
|
4997
|
+
# Get the number of whole milliseconds (0-999) in the fractional part of seconds.
|
4998
|
+
# Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
|
4999
|
+
# Convert to float by adding 0.0 for historical reasons. See gh-89047
|
5000
|
+
msecs = (ns % 1_000_000_000) // 1_000_000 + 0.0
|
5001
|
+
|
5002
|
+
# https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
5003
|
+
if msecs == 999.0 and int(secs) != ns // 1_000_000_000:
|
5004
|
+
# ns -> sec conversion can round up, e.g:
|
5005
|
+
# 1_677_903_920_999_999_900 ns --> 1_677_903_921.0 sec
|
5006
|
+
msecs = 0.0
|
5007
|
+
|
5008
|
+
if start_ns is None:
|
5009
|
+
start_ns = cls.get_std_start_ns()
|
5010
|
+
relative_secs = (ns - start_ns) / 1e6
|
5031
5011
|
|
5032
|
-
|
5012
|
+
return cls(
|
5013
|
+
ns=ns,
|
5014
|
+
secs=secs,
|
5015
|
+
msecs=msecs,
|
5016
|
+
relative_secs=relative_secs,
|
5017
|
+
)
|
5033
5018
|
|
5034
|
-
|
5019
|
+
@logging_context_info
|
5020
|
+
@ta.final
|
5021
|
+
class Exc(ta.NamedTuple):
|
5022
|
+
info: LoggingExcInfo
|
5023
|
+
info_tuple: LoggingExcInfoTuple
|
5035
5024
|
|
5036
|
-
|
5025
|
+
@classmethod
|
5026
|
+
def build(
|
5027
|
+
cls,
|
5028
|
+
arg: LoggingExcInfoArg = False,
|
5029
|
+
) -> ta.Optional['LoggingContextInfos.Exc']:
|
5030
|
+
if arg is True:
|
5031
|
+
sys_exc_info = sys.exc_info()
|
5032
|
+
if sys_exc_info[0] is not None:
|
5033
|
+
arg = sys_exc_info
|
5034
|
+
else:
|
5035
|
+
arg = None
|
5036
|
+
elif arg is False:
|
5037
|
+
arg = None
|
5038
|
+
if arg is None:
|
5039
|
+
return None
|
5037
5040
|
|
5038
|
-
|
5041
|
+
info: LoggingExcInfo = arg
|
5042
|
+
if isinstance(info, BaseException):
|
5043
|
+
info_tuple: LoggingExcInfoTuple = (type(info), info, info.__traceback__) # noqa
|
5044
|
+
else:
|
5045
|
+
info_tuple = info
|
5039
5046
|
|
5040
|
-
|
5047
|
+
return cls(
|
5048
|
+
info=info,
|
5049
|
+
info_tuple=info_tuple,
|
5050
|
+
)
|
5041
5051
|
|
5042
|
-
|
5052
|
+
@logging_context_info
|
5053
|
+
@ta.final
|
5054
|
+
class Caller(ta.NamedTuple):
|
5055
|
+
file_path: str
|
5056
|
+
line_no: int
|
5057
|
+
func_name: str
|
5058
|
+
stack_info: ta.Optional[str]
|
5043
5059
|
|
5044
|
-
|
5060
|
+
@classmethod
|
5061
|
+
def is_internal_frame(cls, frame: types.FrameType) -> bool:
|
5062
|
+
file_path = os.path.normcase(frame.f_code.co_filename)
|
5045
5063
|
|
5046
|
-
|
5064
|
+
# Yes, really.
|
5065
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L204 # noqa
|
5066
|
+
# https://github.com/python/cpython/commit/5ca6d7469be53960843df39bb900e9c3359f127f
|
5067
|
+
if 'importlib' in file_path and '_bootstrap' in file_path:
|
5068
|
+
return True
|
5047
5069
|
|
5070
|
+
return False
|
5048
5071
|
|
5049
|
-
|
5050
|
-
|
5051
|
-
|
5052
|
-
TODO:
|
5053
|
-
- translate json keys
|
5054
|
-
"""
|
5072
|
+
@classmethod
|
5073
|
+
def find_frame(cls, stack_offset: int = 0) -> ta.Optional[types.FrameType]:
|
5074
|
+
f: ta.Optional[types.FrameType] = sys._getframe(2 + stack_offset) # noqa
|
5055
5075
|
|
5076
|
+
while f is not None:
|
5077
|
+
# NOTE: We don't check __file__ like stdlib since we may be running amalgamated - we rely on careful,
|
5078
|
+
# manual stack_offset management.
|
5079
|
+
if hasattr(f, 'f_code'):
|
5080
|
+
return f
|
5056
5081
|
|
5057
|
-
|
5082
|
+
f = f.f_back
|
5058
5083
|
|
5084
|
+
return None
|
5059
5085
|
|
5060
|
-
|
5061
|
-
|
5062
|
-
|
5063
|
-
|
5064
|
-
|
5065
|
-
|
5066
|
-
'
|
5067
|
-
|
5068
|
-
|
5069
|
-
'module': False,
|
5070
|
-
'exc_info': True,
|
5071
|
-
'exc_text': True,
|
5072
|
-
'stack_info': True,
|
5073
|
-
'lineno': False,
|
5074
|
-
'funcName': False,
|
5075
|
-
'created': False,
|
5076
|
-
'msecs': False,
|
5077
|
-
'relativeCreated': False,
|
5078
|
-
'thread': False,
|
5079
|
-
'threadName': False,
|
5080
|
-
'processName': False,
|
5081
|
-
'process': False,
|
5082
|
-
}
|
5086
|
+
@classmethod
|
5087
|
+
def build(
|
5088
|
+
cls,
|
5089
|
+
stack_offset: int = 0,
|
5090
|
+
*,
|
5091
|
+
stack_info: bool = False,
|
5092
|
+
) -> ta.Optional['LoggingContextInfos.Caller']:
|
5093
|
+
if (f := cls.find_frame(stack_offset + 1)) is None:
|
5094
|
+
return None
|
5083
5095
|
|
5084
|
-
|
5085
|
-
|
5086
|
-
|
5087
|
-
|
5088
|
-
|
5089
|
-
|
5090
|
-
|
5096
|
+
# https://github.com/python/cpython/blob/08e9794517063c8cd92c48714071b1d3c60b71bd/Lib/logging/__init__.py#L1616-L1623 # noqa
|
5097
|
+
sinfo = None
|
5098
|
+
if stack_info:
|
5099
|
+
sio = io.StringIO()
|
5100
|
+
traceback.print_stack(f, file=sio)
|
5101
|
+
sinfo = sio.getvalue()
|
5102
|
+
sio.close()
|
5103
|
+
if sinfo[-1] == '\n':
|
5104
|
+
sinfo = sinfo[:-1]
|
5091
5105
|
|
5092
|
-
|
5093
|
-
|
5094
|
-
|
5106
|
+
return cls(
|
5107
|
+
file_path=f.f_code.co_filename,
|
5108
|
+
line_no=f.f_lineno or 0,
|
5109
|
+
func_name=f.f_code.co_name,
|
5110
|
+
stack_info=sinfo,
|
5111
|
+
)
|
5095
5112
|
|
5096
|
-
|
5097
|
-
|
5098
|
-
|
5099
|
-
|
5100
|
-
|
5101
|
-
if not (o and v is None)
|
5102
|
-
}
|
5103
|
-
return self._json_dumps(dct)
|
5113
|
+
@logging_context_info
|
5114
|
+
@ta.final
|
5115
|
+
class SourceFile(ta.NamedTuple):
|
5116
|
+
file_name: str
|
5117
|
+
module: str
|
5104
5118
|
|
5119
|
+
@classmethod
|
5120
|
+
def build(cls, caller_file_path: ta.Optional[str]) -> ta.Optional['LoggingContextInfos.SourceFile']:
|
5121
|
+
if caller_file_path is None:
|
5122
|
+
return None
|
5105
5123
|
|
5106
|
-
|
5107
|
-
|
5124
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L331-L336 # noqa
|
5125
|
+
try:
|
5126
|
+
file_name = os.path.basename(caller_file_path)
|
5127
|
+
module = os.path.splitext(file_name)[0]
|
5128
|
+
except (TypeError, ValueError, AttributeError):
|
5129
|
+
return None
|
5108
5130
|
|
5131
|
+
return cls(
|
5132
|
+
file_name=file_name,
|
5133
|
+
module=module,
|
5134
|
+
)
|
5109
5135
|
|
5110
|
-
|
5136
|
+
@logging_context_info
|
5137
|
+
@ta.final
|
5138
|
+
class Thread(ta.NamedTuple):
|
5139
|
+
ident: int
|
5140
|
+
native_id: ta.Optional[int]
|
5141
|
+
name: str
|
5111
5142
|
|
5143
|
+
@classmethod
|
5144
|
+
def build(cls) -> 'LoggingContextInfos.Thread':
|
5145
|
+
return cls(
|
5146
|
+
ident=threading.get_ident(),
|
5147
|
+
native_id=threading.get_native_id() if hasattr(threading, 'get_native_id') else None,
|
5148
|
+
name=threading.current_thread().name,
|
5149
|
+
)
|
5112
5150
|
|
5113
|
-
@logging_context_info
|
5114
|
-
@ta.final
|
5115
|
-
class
|
5116
|
-
|
5151
|
+
@logging_context_info
|
5152
|
+
@ta.final
|
5153
|
+
class Process(ta.NamedTuple):
|
5154
|
+
pid: int
|
5117
5155
|
|
5118
|
-
|
5119
|
-
|
5120
|
-
|
5156
|
+
@classmethod
|
5157
|
+
def build(cls) -> 'LoggingContextInfos.Process':
|
5158
|
+
return cls(
|
5159
|
+
pid=os.getpid(),
|
5160
|
+
)
|
5121
5161
|
|
5122
|
-
@
|
5123
|
-
|
5124
|
-
|
5162
|
+
@logging_context_info
|
5163
|
+
@ta.final
|
5164
|
+
class Multiprocessing(ta.NamedTuple):
|
5165
|
+
process_name: str
|
5125
5166
|
|
5126
|
-
|
5127
|
-
|
5128
|
-
|
5129
|
-
|
5130
|
-
|
5131
|
-
#
|
5132
|
-
if isinstance(x, float):
|
5133
|
-
return int(x * 1e9)
|
5134
|
-
else:
|
5135
|
-
return x
|
5167
|
+
@classmethod
|
5168
|
+
def build(cls) -> ta.Optional['LoggingContextInfos.Multiprocessing']:
|
5169
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L355-L364 # noqa
|
5170
|
+
if (mp := sys.modules.get('multiprocessing')) is None:
|
5171
|
+
return None
|
5136
5172
|
|
5137
|
-
|
5138
|
-
|
5139
|
-
|
5140
|
-
time_ns: int,
|
5141
|
-
*,
|
5142
|
-
start_time_ns: ta.Optional[int] = None,
|
5143
|
-
) -> 'LoggingTimeFields':
|
5144
|
-
# https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
5145
|
-
created = time_ns / 1e9 # ns to float seconds
|
5146
|
-
|
5147
|
-
# Get the number of whole milliseconds (0-999) in the fractional part of seconds.
|
5148
|
-
# Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
|
5149
|
-
# Convert to float by adding 0.0 for historical reasons. See gh-89047
|
5150
|
-
msecs = (time_ns % 1_000_000_000) // 1_000_000 + 0.0
|
5151
|
-
|
5152
|
-
# https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
5153
|
-
if msecs == 999.0 and int(created) != time_ns // 1_000_000_000:
|
5154
|
-
# ns -> sec conversion can round up, e.g:
|
5155
|
-
# 1_677_903_920_999_999_900 ns --> 1_677_903_921.0 sec
|
5156
|
-
msecs = 0.0
|
5157
|
-
|
5158
|
-
if start_time_ns is None:
|
5159
|
-
start_time_ns = cls.get_std_start_time_ns()
|
5160
|
-
relative_created = (time_ns - start_time_ns) / 1e6
|
5173
|
+
return cls(
|
5174
|
+
process_name=mp.current_process().name,
|
5175
|
+
)
|
5161
5176
|
|
5162
|
-
|
5163
|
-
|
5164
|
-
|
5165
|
-
|
5166
|
-
|
5177
|
+
@logging_context_info
|
5178
|
+
@ta.final
|
5179
|
+
class AsyncioTask(ta.NamedTuple):
|
5180
|
+
name: str
|
5181
|
+
|
5182
|
+
@classmethod
|
5183
|
+
def build(cls) -> ta.Optional['LoggingContextInfos.AsyncioTask']:
|
5184
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L372-L377 # noqa
|
5185
|
+
if (asyncio := sys.modules.get('asyncio')) is None:
|
5186
|
+
return None
|
5187
|
+
|
5188
|
+
try:
|
5189
|
+
task = asyncio.current_task()
|
5190
|
+
except Exception: # noqa
|
5191
|
+
return None
|
5192
|
+
|
5193
|
+
if task is None:
|
5194
|
+
return None
|
5195
|
+
|
5196
|
+
return cls(
|
5197
|
+
name=task.get_name(), # Always non-None
|
5198
|
+
)
|
5167
5199
|
|
5168
5200
|
|
5169
5201
|
##
|
@@ -5174,12 +5206,12 @@ class UnexpectedLoggingStartTimeWarning(LoggingSetupWarning):
|
|
5174
5206
|
|
5175
5207
|
|
5176
5208
|
def _check_logging_start_time() -> None:
|
5177
|
-
if (x :=
|
5209
|
+
if (x := LoggingContextInfos.Time.get_std_start_ns()) < (t := time.time()):
|
5178
5210
|
import warnings # noqa
|
5179
5211
|
|
5180
5212
|
warnings.warn(
|
5181
5213
|
f'Unexpected logging start time detected: '
|
5182
|
-
f'
|
5214
|
+
f'get_std_start_ns={x}, '
|
5183
5215
|
f'time.time()={t}',
|
5184
5216
|
UnexpectedLoggingStartTimeWarning,
|
5185
5217
|
)
|
@@ -5188,6 +5220,95 @@ def _check_logging_start_time() -> None:
|
|
5188
5220
|
_check_logging_start_time()
|
5189
5221
|
|
5190
5222
|
|
5223
|
+
########################################
|
5224
|
+
# ../../../omlish/logs/protocols.py
|
5225
|
+
|
5226
|
+
|
5227
|
+
##
|
5228
|
+
|
5229
|
+
|
5230
|
+
@ta.runtime_checkable
|
5231
|
+
class LoggerLike(ta.Protocol):
|
5232
|
+
"""Satisfied by both our Logger and stdlib logging.Logger."""
|
5233
|
+
|
5234
|
+
def isEnabledFor(self, level: LogLevel) -> bool: ... # noqa
|
5235
|
+
|
5236
|
+
def getEffectiveLevel(self) -> LogLevel: ... # noqa
|
5237
|
+
|
5238
|
+
#
|
5239
|
+
|
5240
|
+
def log(self, level: LogLevel, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5241
|
+
|
5242
|
+
def debug(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5243
|
+
|
5244
|
+
def info(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5245
|
+
|
5246
|
+
def warning(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5247
|
+
|
5248
|
+
def error(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5249
|
+
|
5250
|
+
def exception(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5251
|
+
|
5252
|
+
def critical(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5253
|
+
|
5254
|
+
|
5255
|
+
########################################
|
5256
|
+
# ../../../omlish/logs/std/json.py
|
5257
|
+
"""
|
5258
|
+
TODO:
|
5259
|
+
- translate json keys
|
5260
|
+
"""
|
5261
|
+
|
5262
|
+
|
5263
|
+
##
|
5264
|
+
|
5265
|
+
|
5266
|
+
class JsonLoggingFormatter(logging.Formatter):
|
5267
|
+
KEYS: ta.Mapping[str, bool] = {
|
5268
|
+
'name': False,
|
5269
|
+
'msg': False,
|
5270
|
+
'args': False,
|
5271
|
+
'levelname': False,
|
5272
|
+
'levelno': False,
|
5273
|
+
'pathname': False,
|
5274
|
+
'filename': False,
|
5275
|
+
'module': False,
|
5276
|
+
'exc_info': True,
|
5277
|
+
'exc_text': True,
|
5278
|
+
'stack_info': True,
|
5279
|
+
'lineno': False,
|
5280
|
+
'funcName': False,
|
5281
|
+
'created': False,
|
5282
|
+
'msecs': False,
|
5283
|
+
'relativeCreated': False,
|
5284
|
+
'thread': False,
|
5285
|
+
'threadName': False,
|
5286
|
+
'processName': False,
|
5287
|
+
'process': False,
|
5288
|
+
}
|
5289
|
+
|
5290
|
+
def __init__(
|
5291
|
+
self,
|
5292
|
+
*args: ta.Any,
|
5293
|
+
json_dumps: ta.Optional[ta.Callable[[ta.Any], str]] = None,
|
5294
|
+
**kwargs: ta.Any,
|
5295
|
+
) -> None:
|
5296
|
+
super().__init__(*args, **kwargs)
|
5297
|
+
|
5298
|
+
if json_dumps is None:
|
5299
|
+
json_dumps = json_dumps_compact
|
5300
|
+
self._json_dumps = json_dumps
|
5301
|
+
|
5302
|
+
def format(self, record: logging.LogRecord) -> str:
|
5303
|
+
dct = {
|
5304
|
+
k: v
|
5305
|
+
for k, o in self.KEYS.items()
|
5306
|
+
for v in [getattr(record, k)]
|
5307
|
+
if not (o and v is None)
|
5308
|
+
}
|
5309
|
+
return self._json_dumps(dct)
|
5310
|
+
|
5311
|
+
|
5191
5312
|
########################################
|
5192
5313
|
# ../../../omlish/os/temp.py
|
5193
5314
|
|
@@ -7518,68 +7639,36 @@ inj = InjectionApi()
|
|
7518
7639
|
|
7519
7640
|
|
7520
7641
|
class LoggingContext(Abstract):
|
7521
|
-
@property
|
7522
7642
|
@abc.abstractmethod
|
7523
|
-
def
|
7643
|
+
def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
7524
7644
|
raise NotImplementedError
|
7525
7645
|
|
7526
|
-
|
7527
|
-
|
7528
|
-
|
7529
|
-
@abc.abstractmethod
|
7530
|
-
def time_ns(self) -> int:
|
7531
|
-
raise NotImplementedError
|
7532
|
-
|
7533
|
-
@property
|
7534
|
-
@abc.abstractmethod
|
7535
|
-
def times(self) -> LoggingTimeFields:
|
7536
|
-
raise NotImplementedError
|
7646
|
+
@ta.final
|
7647
|
+
def __getitem__(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
7648
|
+
return self.get_info(ty)
|
7537
7649
|
|
7538
|
-
|
7650
|
+
@ta.final
|
7651
|
+
def must_get_info(self, ty: ta.Type[LoggingContextInfoT]) -> LoggingContextInfoT:
|
7652
|
+
if (info := self.get_info(ty)) is None:
|
7653
|
+
raise TypeError(f'LoggingContextInfo absent: {ty}')
|
7654
|
+
return info
|
7539
7655
|
|
7540
|
-
|
7541
|
-
@abc.abstractmethod
|
7542
|
-
def exc_info(self) -> ta.Optional[LoggingExcInfo]:
|
7543
|
-
raise NotImplementedError
|
7656
|
+
##
|
7544
7657
|
|
7545
|
-
@property
|
7546
|
-
@abc.abstractmethod
|
7547
|
-
def exc_info_tuple(self) -> ta.Optional[LoggingExcInfoTuple]:
|
7548
|
-
raise NotImplementedError
|
7549
|
-
|
7550
|
-
#
|
7551
7658
|
|
7659
|
+
class CaptureLoggingContext(LoggingContext, Abstract):
|
7552
7660
|
@abc.abstractmethod
|
7553
|
-
def
|
7554
|
-
|
7661
|
+
def set_basic(
|
7662
|
+
self,
|
7663
|
+
name: str,
|
7555
7664
|
|
7556
|
-
|
7557
|
-
|
7665
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
7666
|
+
args: tuple,
|
7667
|
+
) -> 'CaptureLoggingContext':
|
7558
7668
|
raise NotImplementedError
|
7559
7669
|
|
7560
7670
|
#
|
7561
7671
|
|
7562
|
-
@abc.abstractmethod
|
7563
|
-
def thread(self) -> ta.Optional[LoggingThreadInfo]:
|
7564
|
-
raise NotImplementedError
|
7565
|
-
|
7566
|
-
@abc.abstractmethod
|
7567
|
-
def process(self) -> ta.Optional[LoggingProcessInfo]:
|
7568
|
-
raise NotImplementedError
|
7569
|
-
|
7570
|
-
@abc.abstractmethod
|
7571
|
-
def multiprocessing(self) -> ta.Optional[LoggingMultiprocessingInfo]:
|
7572
|
-
raise NotImplementedError
|
7573
|
-
|
7574
|
-
@abc.abstractmethod
|
7575
|
-
def asyncio_task(self) -> ta.Optional[LoggingAsyncioTaskInfo]:
|
7576
|
-
raise NotImplementedError
|
7577
|
-
|
7578
|
-
|
7579
|
-
##
|
7580
|
-
|
7581
|
-
|
7582
|
-
class CaptureLoggingContext(LoggingContext, Abstract):
|
7583
7672
|
class AlreadyCapturedError(Exception):
|
7584
7673
|
pass
|
7585
7674
|
|
@@ -7610,80 +7699,50 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
|
|
7610
7699
|
|
7611
7700
|
exc_info: LoggingExcInfoArg = False,
|
7612
7701
|
|
7613
|
-
caller: ta.Union[
|
7702
|
+
caller: ta.Union[LoggingContextInfos.Caller, ta.Type[NOT_SET], None] = NOT_SET,
|
7614
7703
|
stack_offset: int = 0,
|
7615
7704
|
stack_info: bool = False,
|
7616
7705
|
) -> None:
|
7617
|
-
|
7618
|
-
|
7619
|
-
#
|
7706
|
+
# TODO: Name, Msg, Extra
|
7620
7707
|
|
7621
7708
|
if time_ns is None:
|
7622
7709
|
time_ns = time.time_ns()
|
7623
|
-
self._time_ns: int = time_ns
|
7624
|
-
|
7625
|
-
#
|
7626
7710
|
|
7627
|
-
|
7628
|
-
|
7629
|
-
|
7630
|
-
|
7631
|
-
|
7632
|
-
|
7633
|
-
elif exc_info is False:
|
7634
|
-
exc_info = None
|
7635
|
-
|
7636
|
-
if exc_info is not None:
|
7637
|
-
self._exc_info: ta.Optional[LoggingExcInfo] = exc_info
|
7638
|
-
if isinstance(exc_info, BaseException):
|
7639
|
-
self._exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = (type(exc_info), exc_info, exc_info.__traceback__) # noqa
|
7640
|
-
else:
|
7641
|
-
self._exc_info_tuple = exc_info
|
7642
|
-
|
7643
|
-
#
|
7711
|
+
self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {}
|
7712
|
+
self._set_info(
|
7713
|
+
LoggingContextInfos.Level.build(level),
|
7714
|
+
LoggingContextInfos.Time.build(time_ns),
|
7715
|
+
LoggingContextInfos.Exc.build(exc_info),
|
7716
|
+
)
|
7644
7717
|
|
7645
7718
|
if caller is not CaptureLoggingContextImpl.NOT_SET:
|
7646
|
-
self.
|
7719
|
+
self._infos[LoggingContextInfos.Caller] = caller
|
7647
7720
|
else:
|
7648
7721
|
self._stack_offset = stack_offset
|
7649
7722
|
self._stack_info = stack_info
|
7650
7723
|
|
7651
|
-
|
7652
|
-
|
7653
|
-
|
7654
|
-
|
7655
|
-
return self
|
7656
|
-
|
7657
|
-
#
|
7658
|
-
|
7659
|
-
@property
|
7660
|
-
def time_ns(self) -> int:
|
7661
|
-
return self._time_ns
|
7662
|
-
|
7663
|
-
_times: LoggingTimeFields
|
7664
|
-
|
7665
|
-
@property
|
7666
|
-
def times(self) -> LoggingTimeFields:
|
7667
|
-
try:
|
7668
|
-
return self._times
|
7669
|
-
except AttributeError:
|
7670
|
-
pass
|
7671
|
-
|
7672
|
-
times = self._times = LoggingTimeFields.build(self.time_ns)
|
7673
|
-
return times
|
7724
|
+
def _set_info(self, *infos: ta.Optional[LoggingContextInfo]) -> 'CaptureLoggingContextImpl':
|
7725
|
+
for info in infos:
|
7726
|
+
if info is not None:
|
7727
|
+
self._infos[type(info)] = info
|
7728
|
+
return self
|
7674
7729
|
|
7675
|
-
|
7730
|
+
def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
7731
|
+
return self._infos.get(ty)
|
7676
7732
|
|
7677
|
-
|
7678
|
-
_exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = None
|
7733
|
+
##
|
7679
7734
|
|
7680
|
-
|
7681
|
-
|
7682
|
-
|
7735
|
+
def set_basic(
|
7736
|
+
self,
|
7737
|
+
name: str,
|
7683
7738
|
|
7684
|
-
|
7685
|
-
|
7686
|
-
|
7739
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
7740
|
+
args: tuple,
|
7741
|
+
) -> 'CaptureLoggingContextImpl':
|
7742
|
+
return self._set_info(
|
7743
|
+
LoggingContextInfos.Name(name),
|
7744
|
+
LoggingContextInfos.Msg.build(msg, *args),
|
7745
|
+
)
|
7687
7746
|
|
7688
7747
|
##
|
7689
7748
|
|
@@ -7697,74 +7756,28 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
|
|
7697
7756
|
|
7698
7757
|
_has_captured: bool = False
|
7699
7758
|
|
7700
|
-
_caller: ta.Optional[LoggingCaller]
|
7701
|
-
_source_file: ta.Optional[LoggingSourceFileInfo]
|
7702
|
-
|
7703
|
-
_thread: ta.Optional[LoggingThreadInfo]
|
7704
|
-
_process: ta.Optional[LoggingProcessInfo]
|
7705
|
-
_multiprocessing: ta.Optional[LoggingMultiprocessingInfo]
|
7706
|
-
_asyncio_task: ta.Optional[LoggingAsyncioTaskInfo]
|
7707
|
-
|
7708
7759
|
def capture(self) -> None:
|
7709
7760
|
if self._has_captured:
|
7710
7761
|
raise CaptureLoggingContextImpl.AlreadyCapturedError
|
7711
7762
|
self._has_captured = True
|
7712
7763
|
|
7713
|
-
if not
|
7714
|
-
self.
|
7764
|
+
if LoggingContextInfos.Caller not in self._infos:
|
7765
|
+
self._set_info(LoggingContextInfos.Caller.build(
|
7715
7766
|
self._stack_offset + 1,
|
7716
7767
|
stack_info=self._stack_info,
|
7717
|
-
)
|
7718
|
-
|
7719
|
-
if (caller := self._caller) is not None:
|
7720
|
-
self._source_file = LoggingSourceFileInfo.build(caller.file_path)
|
7721
|
-
else:
|
7722
|
-
self._source_file = None
|
7723
|
-
|
7724
|
-
self._thread = LoggingThreadInfo.build()
|
7725
|
-
self._process = LoggingProcessInfo.build()
|
7726
|
-
self._multiprocessing = LoggingMultiprocessingInfo.build()
|
7727
|
-
self._asyncio_task = LoggingAsyncioTaskInfo.build()
|
7728
|
-
|
7729
|
-
#
|
7730
|
-
|
7731
|
-
def caller(self) -> ta.Optional[LoggingCaller]:
|
7732
|
-
try:
|
7733
|
-
return self._caller
|
7734
|
-
except AttributeError:
|
7735
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
7736
|
-
|
7737
|
-
def source_file(self) -> ta.Optional[LoggingSourceFileInfo]:
|
7738
|
-
try:
|
7739
|
-
return self._source_file
|
7740
|
-
except AttributeError:
|
7741
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
7742
|
-
|
7743
|
-
#
|
7744
|
-
|
7745
|
-
def thread(self) -> ta.Optional[LoggingThreadInfo]:
|
7746
|
-
try:
|
7747
|
-
return self._thread
|
7748
|
-
except AttributeError:
|
7749
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
7750
|
-
|
7751
|
-
def process(self) -> ta.Optional[LoggingProcessInfo]:
|
7752
|
-
try:
|
7753
|
-
return self._process
|
7754
|
-
except AttributeError:
|
7755
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
7768
|
+
))
|
7756
7769
|
|
7757
|
-
|
7758
|
-
|
7759
|
-
|
7760
|
-
|
7761
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
7770
|
+
if (caller := self[LoggingContextInfos.Caller]) is not None:
|
7771
|
+
self._set_info(LoggingContextInfos.SourceFile.build(
|
7772
|
+
caller.file_path,
|
7773
|
+
))
|
7762
7774
|
|
7763
|
-
|
7764
|
-
|
7765
|
-
|
7766
|
-
|
7767
|
-
|
7775
|
+
self._set_info(
|
7776
|
+
LoggingContextInfos.Thread.build(),
|
7777
|
+
LoggingContextInfos.Process.build(),
|
7778
|
+
LoggingContextInfos.Multiprocessing.build(),
|
7779
|
+
LoggingContextInfos.AsyncioTask.build(),
|
7780
|
+
)
|
7768
7781
|
|
7769
7782
|
|
7770
7783
|
########################################
|
@@ -9436,7 +9449,6 @@ class CoroHttpServer:
|
|
9436
9449
|
|
9437
9450
|
|
9438
9451
|
class AnyLogger(Abstract, ta.Generic[T]):
|
9439
|
-
@ta.final
|
9440
9452
|
def is_enabled_for(self, level: LogLevel) -> bool:
|
9441
9453
|
return level >= self.get_effective_level()
|
9442
9454
|
|
@@ -9582,36 +9594,6 @@ class AnyLogger(Abstract, ta.Generic[T]):
|
|
9582
9594
|
|
9583
9595
|
##
|
9584
9596
|
|
9585
|
-
@classmethod
|
9586
|
-
def _prepare_msg_args(cls, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> ta.Tuple[str, tuple]:
|
9587
|
-
if callable(msg):
|
9588
|
-
if args:
|
9589
|
-
raise TypeError(f'Must not provide both a message function and args: {msg=} {args=}')
|
9590
|
-
x = msg()
|
9591
|
-
if isinstance(x, str):
|
9592
|
-
return x, ()
|
9593
|
-
elif isinstance(x, tuple):
|
9594
|
-
if x:
|
9595
|
-
return x[0], x[1:]
|
9596
|
-
else:
|
9597
|
-
return '', ()
|
9598
|
-
else:
|
9599
|
-
raise TypeError(x)
|
9600
|
-
|
9601
|
-
elif isinstance(msg, tuple):
|
9602
|
-
if args:
|
9603
|
-
raise TypeError(f'Must not provide both a tuple message and args: {msg=} {args=}')
|
9604
|
-
if msg:
|
9605
|
-
return msg[0], msg[1:]
|
9606
|
-
else:
|
9607
|
-
return '', ()
|
9608
|
-
|
9609
|
-
elif isinstance(msg, str):
|
9610
|
-
return msg, args
|
9611
|
-
|
9612
|
-
else:
|
9613
|
-
raise TypeError(msg)
|
9614
|
-
|
9615
9597
|
@abc.abstractmethod
|
9616
9598
|
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> T: # noqa
|
9617
9599
|
raise NotImplementedError
|
@@ -9635,7 +9617,7 @@ class AsyncLogger(AnyLogger[ta.Awaitable[None]], Abstract):
|
|
9635
9617
|
class AnyNopLogger(AnyLogger[T], Abstract):
|
9636
9618
|
@ta.final
|
9637
9619
|
def get_effective_level(self) -> LogLevel:
|
9638
|
-
return 999
|
9620
|
+
return -999
|
9639
9621
|
|
9640
9622
|
|
9641
9623
|
@ta.final
|
@@ -9652,137 +9634,543 @@ class AsyncNopLogger(AnyNopLogger[ta.Awaitable[None]], AsyncLogger):
|
|
9652
9634
|
|
9653
9635
|
########################################
|
9654
9636
|
# ../../../omlish/logs/std/records.py
|
9637
|
+
"""
|
9638
|
+
TODO:
|
9639
|
+
- TypedDict?
|
9640
|
+
"""
|
9655
9641
|
|
9656
9642
|
|
9657
9643
|
##
|
9658
9644
|
|
9659
9645
|
|
9660
|
-
|
9661
|
-
#
|
9662
|
-
#
|
9663
|
-
#
|
9664
|
-
#
|
9665
|
-
# - https://github.com/python/cpython/blob/
|
9666
|
-
#
|
9667
|
-
#
|
9668
|
-
# - name: str
|
9669
|
-
# - level: int
|
9670
|
-
# - pathname: str - Confusingly referred to as `fn` before the LogRecord ctor. May be empty or "(unknown file)".
|
9671
|
-
# - lineno: int - May be 0.
|
9672
|
-
# - msg: str
|
9673
|
-
# - args: tuple | dict | 1-tuple[dict]
|
9674
|
-
# - exc_info: LoggingExcInfoTuple | None
|
9675
|
-
# - func: str | None = None -> funcName
|
9676
|
-
# - sinfo: str | None = None -> stack_info
|
9677
|
-
#
|
9678
|
-
KNOWN_STD_LOGGING_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
|
9679
|
-
# Name of the logger used to log the call. Unmodified by ctor.
|
9680
|
-
name=str,
|
9646
|
+
class LoggingContextInfoRecordAdapters:
|
9647
|
+
# Ref:
|
9648
|
+
# - https://docs.python.org/3/library/logging.html#logrecord-attributes
|
9649
|
+
#
|
9650
|
+
# LogRecord:
|
9651
|
+
# - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L276 (3.8) # noqa
|
9652
|
+
# - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L286 (~3.14) # noqa
|
9653
|
+
#
|
9681
9654
|
|
9682
|
-
|
9683
|
-
|
9684
|
-
msg=str,
|
9655
|
+
def __new__(cls, *args, **kwargs): # noqa
|
9656
|
+
raise TypeError
|
9685
9657
|
|
9686
|
-
|
9687
|
-
|
9688
|
-
|
9689
|
-
|
9658
|
+
class Adapter(Abstract, ta.Generic[T]):
|
9659
|
+
@property
|
9660
|
+
@abc.abstractmethod
|
9661
|
+
def info_cls(self) -> ta.Type[LoggingContextInfo]:
|
9662
|
+
raise NotImplementedError
|
9690
9663
|
|
9691
|
-
|
9664
|
+
#
|
9692
9665
|
|
9693
|
-
|
9694
|
-
|
9695
|
-
|
9666
|
+
@ta.final
|
9667
|
+
class NOT_SET: # noqa
|
9668
|
+
def __new__(cls, *args, **kwargs): # noqa
|
9669
|
+
raise TypeError
|
9696
9670
|
|
9697
|
-
|
9698
|
-
|
9671
|
+
class RecordAttr(ta.NamedTuple):
|
9672
|
+
name: str
|
9673
|
+
type: ta.Any
|
9674
|
+
default: ta.Any
|
9699
9675
|
|
9700
|
-
|
9676
|
+
# @abc.abstractmethod
|
9677
|
+
record_attrs: ta.ClassVar[ta.Mapping[str, RecordAttr]]
|
9701
9678
|
|
9702
|
-
|
9703
|
-
|
9704
|
-
|
9679
|
+
@property
|
9680
|
+
@abc.abstractmethod
|
9681
|
+
def _record_attrs(self) -> ta.Union[
|
9682
|
+
ta.Mapping[str, ta.Any],
|
9683
|
+
ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]],
|
9684
|
+
]:
|
9685
|
+
raise NotImplementedError
|
9705
9686
|
|
9706
|
-
|
9707
|
-
filename=str,
|
9687
|
+
#
|
9708
9688
|
|
9709
|
-
|
9710
|
-
|
9711
|
-
|
9689
|
+
@abc.abstractmethod
|
9690
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
9691
|
+
raise NotImplementedError
|
9712
9692
|
|
9713
|
-
|
9693
|
+
#
|
9714
9694
|
|
9715
|
-
|
9716
|
-
|
9695
|
+
@abc.abstractmethod
|
9696
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[T]:
|
9697
|
+
raise NotImplementedError
|
9717
9698
|
|
9718
|
-
|
9719
|
-
exc_text=ta.Optional[str],
|
9699
|
+
#
|
9720
9700
|
|
9721
|
-
|
9701
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
9702
|
+
super().__init_subclass__(**kwargs)
|
9722
9703
|
|
9723
|
-
|
9724
|
-
|
9725
|
-
# unmodified. Mostly set, if requested, by `Logger.findCaller`, to `traceback.print_stack(f)`, but prepended with
|
9726
|
-
# the literal "Stack (most recent call last):\n", and stripped of exactly one trailing `\n` if present.
|
9727
|
-
stack_info=ta.Optional[str],
|
9704
|
+
if Abstract in cls.__bases__:
|
9705
|
+
return
|
9728
9706
|
|
9729
|
-
|
9730
|
-
|
9731
|
-
|
9707
|
+
if 'record_attrs' in cls.__dict__:
|
9708
|
+
raise TypeError(cls)
|
9709
|
+
if not isinstance(ra := cls.__dict__['_record_attrs'], collections.abc.Mapping):
|
9710
|
+
raise TypeError(ra)
|
9711
|
+
|
9712
|
+
rd: ta.Dict[str, LoggingContextInfoRecordAdapters.Adapter.RecordAttr] = {}
|
9713
|
+
for n, v in ra.items():
|
9714
|
+
if not n or not isinstance(n, str) or n in rd:
|
9715
|
+
raise AttributeError(n)
|
9716
|
+
if isinstance(v, tuple):
|
9717
|
+
t, d = v
|
9718
|
+
else:
|
9719
|
+
t, d = v, cls.NOT_SET
|
9720
|
+
rd[n] = cls.RecordAttr(
|
9721
|
+
name=n,
|
9722
|
+
type=t,
|
9723
|
+
default=d,
|
9724
|
+
)
|
9725
|
+
cls.record_attrs = rd
|
9732
9726
|
|
9733
|
-
|
9734
|
-
|
9735
|
-
|
9727
|
+
class RequiredAdapter(Adapter[T], Abstract):
|
9728
|
+
@property
|
9729
|
+
@abc.abstractmethod
|
9730
|
+
def _record_attrs(self) -> ta.Mapping[str, ta.Any]:
|
9731
|
+
raise NotImplementedError
|
9736
9732
|
|
9737
|
-
|
9733
|
+
#
|
9738
9734
|
|
9739
|
-
|
9740
|
-
|
9741
|
-
|
9742
|
-
|
9743
|
-
|
9744
|
-
|
9745
|
-
|
9735
|
+
@ta.final
|
9736
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
9737
|
+
if (info := ctx.get_info(self.info_cls)) is not None:
|
9738
|
+
return self._info_to_record(info)
|
9739
|
+
else:
|
9740
|
+
raise TypeError # FIXME: fallback?
|
9741
|
+
|
9742
|
+
@abc.abstractmethod
|
9743
|
+
def _info_to_record(self, info: T) -> ta.Mapping[str, ta.Any]:
|
9744
|
+
raise NotImplementedError
|
9746
9745
|
|
9747
|
-
|
9748
|
-
msecs=float,
|
9746
|
+
#
|
9749
9747
|
|
9750
|
-
|
9751
|
-
|
9748
|
+
@abc.abstractmethod
|
9749
|
+
def record_to_info(self, rec: logging.LogRecord) -> T:
|
9750
|
+
raise NotImplementedError
|
9752
9751
|
|
9753
|
-
|
9752
|
+
#
|
9754
9753
|
|
9755
|
-
|
9756
|
-
|
9754
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
9755
|
+
super().__init_subclass__(**kwargs)
|
9757
9756
|
|
9758
|
-
|
9759
|
-
|
9757
|
+
if any(a.default is not cls.NOT_SET for a in cls.record_attrs.values()):
|
9758
|
+
raise TypeError(cls.record_attrs)
|
9760
9759
|
|
9761
|
-
|
9760
|
+
class OptionalAdapter(Adapter[T], Abstract, ta.Generic[T]):
|
9761
|
+
@property
|
9762
|
+
@abc.abstractmethod
|
9763
|
+
def _record_attrs(self) -> ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]:
|
9764
|
+
raise NotImplementedError
|
9765
|
+
|
9766
|
+
record_defaults: ta.ClassVar[ta.Mapping[str, ta.Any]]
|
9767
|
+
|
9768
|
+
#
|
9769
|
+
|
9770
|
+
@ta.final
|
9771
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
9772
|
+
if (info := ctx.get_info(self.info_cls)) is not None:
|
9773
|
+
return self._info_to_record(info)
|
9774
|
+
else:
|
9775
|
+
return self.record_defaults
|
9776
|
+
|
9777
|
+
@abc.abstractmethod
|
9778
|
+
def _info_to_record(self, info: T) -> ta.Mapping[str, ta.Any]:
|
9779
|
+
raise NotImplementedError
|
9780
|
+
|
9781
|
+
#
|
9782
|
+
|
9783
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
9784
|
+
super().__init_subclass__(**kwargs)
|
9785
|
+
|
9786
|
+
dd: ta.Dict[str, ta.Any] = {a.name: a.default for a in cls.record_attrs.values()}
|
9787
|
+
if any(d is cls.NOT_SET for d in dd.values()):
|
9788
|
+
raise TypeError(cls.record_attrs)
|
9789
|
+
cls.record_defaults = dd
|
9762
9790
|
|
9763
|
-
# Process name if available. Set to None if `logging.logMultiprocessing` is not truthy. Otherwise, set to
|
9764
|
-
# 'MainProcess', then `sys.modules.get('multiprocessing').current_process().name` if that works, otherwise remains
|
9765
|
-
# as 'MainProcess'.
|
9766
9791
|
#
|
9767
|
-
|
9792
|
+
|
9793
|
+
class Name(RequiredAdapter[LoggingContextInfos.Name]):
|
9794
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Name]] = LoggingContextInfos.Name
|
9795
|
+
|
9796
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
9797
|
+
# Name of the logger used to log the call. Unmodified by ctor.
|
9798
|
+
name=str,
|
9799
|
+
)
|
9800
|
+
|
9801
|
+
def _info_to_record(self, info: LoggingContextInfos.Name) -> ta.Mapping[str, ta.Any]:
|
9802
|
+
return dict(
|
9803
|
+
name=info.name,
|
9804
|
+
)
|
9805
|
+
|
9806
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Name:
|
9807
|
+
return LoggingContextInfos.Name(
|
9808
|
+
name=rec.name,
|
9809
|
+
)
|
9810
|
+
|
9811
|
+
class Level(RequiredAdapter[LoggingContextInfos.Level]):
|
9812
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Level]] = LoggingContextInfos.Level
|
9813
|
+
|
9814
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
9815
|
+
# Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). Set to
|
9816
|
+
# `getLevelName(level)`.
|
9817
|
+
levelname=str,
|
9818
|
+
|
9819
|
+
# Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL). Unmodified by ctor.
|
9820
|
+
levelno=int,
|
9821
|
+
)
|
9822
|
+
|
9823
|
+
def _info_to_record(self, info: LoggingContextInfos.Level) -> ta.Mapping[str, ta.Any]:
|
9824
|
+
return dict(
|
9825
|
+
levelname=info.name,
|
9826
|
+
levelno=int(info.level),
|
9827
|
+
)
|
9828
|
+
|
9829
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Level:
|
9830
|
+
return LoggingContextInfos.Level.build(rec.levelno)
|
9831
|
+
|
9832
|
+
class Msg(RequiredAdapter[LoggingContextInfos.Msg]):
|
9833
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Msg]] = LoggingContextInfos.Msg
|
9834
|
+
|
9835
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
9836
|
+
# The format string passed in the original logging call. Merged with args to produce message, or an
|
9837
|
+
# arbitrary object (see Using arbitrary objects as messages). Unmodified by ctor.
|
9838
|
+
msg=str,
|
9839
|
+
|
9840
|
+
# The tuple of arguments merged into msg to produce message, or a dict whose values are used for the merge
|
9841
|
+
# (when there is only one argument, and it is a dictionary). Ctor will transform a 1-tuple containing a
|
9842
|
+
# Mapping into just the mapping, but is otherwise unmodified.
|
9843
|
+
args=ta.Union[tuple, dict, None],
|
9844
|
+
)
|
9845
|
+
|
9846
|
+
def _info_to_record(self, info: LoggingContextInfos.Msg) -> ta.Mapping[str, ta.Any]:
|
9847
|
+
return dict(
|
9848
|
+
msg=info.msg,
|
9849
|
+
args=info.args,
|
9850
|
+
)
|
9851
|
+
|
9852
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Msg:
|
9853
|
+
return LoggingContextInfos.Msg(
|
9854
|
+
msg=rec.msg,
|
9855
|
+
args=rec.args,
|
9856
|
+
)
|
9857
|
+
|
9858
|
+
# FIXME: handled specially - all unknown attrs on LogRecord
|
9859
|
+
# class Extra(Adapter[LoggingContextInfos.Extra]):
|
9860
|
+
# _record_attrs: ta.ClassVar[ta.Mapping[str, ta.Union[ta.Any, ta.Tuple[ta.Any, ta.Any]]]] = dict()
|
9768
9861
|
#
|
9769
|
-
#
|
9770
|
-
#
|
9862
|
+
# def info_to_record(self, info: ta.Optional[LoggingContextInfos.Extra]) -> ta.Mapping[str, ta.Any]:
|
9863
|
+
# # FIXME:
|
9864
|
+
# # if extra is not None:
|
9865
|
+
# # for key in extra:
|
9866
|
+
# # if (key in ["message", "asctime"]) or (key in rv.__dict__):
|
9867
|
+
# # raise KeyError("Attempt to overwrite %r in LogRecord" % key)
|
9868
|
+
# # rv.__dict__[key] = extra[key]
|
9869
|
+
# return dict()
|
9771
9870
|
#
|
9772
|
-
|
9871
|
+
# def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Extra]:
|
9872
|
+
# return None
|
9773
9873
|
|
9774
|
-
|
9775
|
-
|
9776
|
-
process=ta.Optional[int],
|
9874
|
+
class Time(RequiredAdapter[LoggingContextInfos.Time]):
|
9875
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Time]] = LoggingContextInfos.Time
|
9777
9876
|
|
9778
|
-
|
9877
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
9878
|
+
# Time when the LogRecord was created. Set to `time.time_ns() / 1e9` for >=3.13.0b1, otherwise simply
|
9879
|
+
# `time.time()`.
|
9880
|
+
#
|
9881
|
+
# See:
|
9882
|
+
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
9883
|
+
# - https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
9884
|
+
#
|
9885
|
+
created=float,
|
9779
9886
|
|
9780
|
-
|
9781
|
-
|
9782
|
-
|
9783
|
-
|
9887
|
+
# Millisecond portion of the time when the LogRecord was created.
|
9888
|
+
msecs=float,
|
9889
|
+
|
9890
|
+
# Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
|
9891
|
+
relativeCreated=float,
|
9892
|
+
)
|
9893
|
+
|
9894
|
+
def _info_to_record(self, info: LoggingContextInfos.Time) -> ta.Mapping[str, ta.Any]:
|
9895
|
+
return dict(
|
9896
|
+
created=info.secs,
|
9897
|
+
msecs=info.msecs,
|
9898
|
+
relativeCreated=info.relative_secs,
|
9899
|
+
)
|
9900
|
+
|
9901
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Time:
|
9902
|
+
return LoggingContextInfos.Time.build(
|
9903
|
+
int(rec.created * 1e9),
|
9904
|
+
)
|
9905
|
+
|
9906
|
+
class Exc(OptionalAdapter[LoggingContextInfos.Exc]):
|
9907
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Exc]] = LoggingContextInfos.Exc
|
9908
|
+
|
9909
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
9910
|
+
# Exception tuple (à la sys.exc_info) or, if no exception has occurred, None. Unmodified by ctor.
|
9911
|
+
exc_info=(ta.Optional[LoggingExcInfoTuple], None),
|
9912
|
+
|
9913
|
+
# Used to cache the traceback text. Simply set to None by ctor, later set by Formatter.format.
|
9914
|
+
exc_text=(ta.Optional[str], None),
|
9915
|
+
)
|
9916
|
+
|
9917
|
+
def _info_to_record(self, info: LoggingContextInfos.Exc) -> ta.Mapping[str, ta.Any]:
|
9918
|
+
return dict(
|
9919
|
+
exc_info=info.info_tuple,
|
9920
|
+
exc_text=None,
|
9921
|
+
)
|
9922
|
+
|
9923
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Exc]:
|
9924
|
+
# FIXME:
|
9925
|
+
# error: Argument 1 to "build" of "Exc" has incompatible type
|
9926
|
+
# "tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None] | None"; expected # noqa
|
9927
|
+
# "BaseException | tuple[type[BaseException], BaseException, TracebackType | None] | bool | None" [arg-type] # noqa
|
9928
|
+
return LoggingContextInfos.Exc.build(rec.exc_info) # type: ignore[arg-type]
|
9929
|
+
|
9930
|
+
class Caller(OptionalAdapter[LoggingContextInfos.Caller]):
|
9931
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Caller]] = LoggingContextInfos.Caller
|
9932
|
+
|
9933
|
+
_UNKNOWN_PATH_NAME: ta.ClassVar[str] = '(unknown file)'
|
9934
|
+
_UNKNOWN_FUNC_NAME: ta.ClassVar[str] = '(unknown function)'
|
9935
|
+
|
9936
|
+
_STACK_INFO_PREFIX: ta.ClassVar[str] = 'Stack (most recent call last):\n'
|
9937
|
+
|
9938
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
9939
|
+
# Full pathname of the source file where the logging call was issued (if available). Unmodified by ctor. May
|
9940
|
+
# default to "(unknown file)" by Logger.findCaller / Logger._log.
|
9941
|
+
pathname=(str, _UNKNOWN_PATH_NAME),
|
9942
|
+
|
9943
|
+
# Source line number where the logging call was issued (if available). Unmodified by ctor. May default to 0
|
9944
|
+
# y Logger.findCaller / Logger._log.
|
9945
|
+
lineno=(int, 0),
|
9946
|
+
|
9947
|
+
# Name of function containing the logging call. Set by ctor to `func` arg, unmodified. May default to
|
9948
|
+
# "(unknown function)" by Logger.findCaller / Logger._log.
|
9949
|
+
funcName=(str, _UNKNOWN_FUNC_NAME),
|
9950
|
+
|
9951
|
+
# Stack frame information (where available) from the bottom of the stack in the current thread, up to and
|
9952
|
+
# including the stack frame of the logging call which resulted in the creation of this record. Set by ctor
|
9953
|
+
# to `sinfo` arg, unmodified. Mostly set, if requested, by `Logger.findCaller`, to
|
9954
|
+
# `traceback.print_stack(f)`, but prepended with the literal "Stack (most recent call last):\n", and
|
9955
|
+
# stripped of exactly one trailing `\n` if present.
|
9956
|
+
stack_info=(ta.Optional[str], None),
|
9957
|
+
)
|
9958
|
+
|
9959
|
+
def _info_to_record(self, caller: LoggingContextInfos.Caller) -> ta.Mapping[str, ta.Any]:
|
9960
|
+
if (sinfo := caller.stack_info) is not None:
|
9961
|
+
stack_info: ta.Optional[str] = '\n'.join([
|
9962
|
+
self._STACK_INFO_PREFIX,
|
9963
|
+
sinfo[1:] if sinfo.endswith('\n') else sinfo,
|
9964
|
+
])
|
9965
|
+
else:
|
9966
|
+
stack_info = None
|
9967
|
+
|
9968
|
+
return dict(
|
9969
|
+
pathname=caller.file_path,
|
9970
|
+
|
9971
|
+
lineno=caller.line_no,
|
9972
|
+
funcName=caller.func_name,
|
9973
|
+
|
9974
|
+
stack_info=stack_info,
|
9975
|
+
)
|
9976
|
+
|
9977
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Caller]:
|
9978
|
+
# FIXME: piecemeal?
|
9979
|
+
# FIXME: strip _STACK_INFO_PREFIX
|
9980
|
+
raise NotImplementedError
|
9981
|
+
|
9982
|
+
class SourceFile(Adapter[LoggingContextInfos.SourceFile]):
|
9983
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.SourceFile]] = LoggingContextInfos.SourceFile
|
9984
|
+
|
9985
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
9986
|
+
# Filename portion of pathname. Set to `os.path.basename(pathname)` if successful, otherwise defaults to
|
9987
|
+
# pathname.
|
9988
|
+
filename=str,
|
9989
|
+
|
9990
|
+
# Module (name portion of filename). Set to `os.path.splitext(filename)[0]`, otherwise defaults to
|
9991
|
+
# "Unknown module".
|
9992
|
+
module=str,
|
9993
|
+
)
|
9994
|
+
|
9995
|
+
_UNKNOWN_MODULE: ta.ClassVar[str] = 'Unknown module'
|
9784
9996
|
|
9785
|
-
|
9997
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
9998
|
+
if (info := ctx.get_info(LoggingContextInfos.SourceFile)) is not None:
|
9999
|
+
return dict(
|
10000
|
+
filename=info.file_name,
|
10001
|
+
module=info.module,
|
10002
|
+
)
|
10003
|
+
|
10004
|
+
if (caller := ctx.get_info(LoggingContextInfos.Caller)) is not None:
|
10005
|
+
return dict(
|
10006
|
+
filename=caller.file_path,
|
10007
|
+
module=self._UNKNOWN_MODULE,
|
10008
|
+
)
|
10009
|
+
|
10010
|
+
return dict(
|
10011
|
+
filename=LoggingContextInfoRecordAdapters.Caller._UNKNOWN_PATH_NAME, # noqa
|
10012
|
+
module=self._UNKNOWN_MODULE,
|
10013
|
+
)
|
10014
|
+
|
10015
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.SourceFile]:
|
10016
|
+
if not (
|
10017
|
+
rec.module is None or
|
10018
|
+
rec.module == self._UNKNOWN_MODULE
|
10019
|
+
):
|
10020
|
+
return LoggingContextInfos.SourceFile(
|
10021
|
+
file_name=rec.filename,
|
10022
|
+
module=rec.module, # FIXME: piecemeal?
|
10023
|
+
)
|
10024
|
+
|
10025
|
+
return None
|
10026
|
+
|
10027
|
+
class Thread(OptionalAdapter[LoggingContextInfos.Thread]):
|
10028
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Thread]] = LoggingContextInfos.Thread
|
10029
|
+
|
10030
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
10031
|
+
# Thread ID if available, and `logging.logThreads` is truthy.
|
10032
|
+
thread=(ta.Optional[int], None),
|
10033
|
+
|
10034
|
+
# Thread name if available, and `logging.logThreads` is truthy.
|
10035
|
+
threadName=(ta.Optional[str], None),
|
10036
|
+
)
|
10037
|
+
|
10038
|
+
def _info_to_record(self, info: LoggingContextInfos.Thread) -> ta.Mapping[str, ta.Any]:
|
10039
|
+
if logging.logThreads:
|
10040
|
+
return dict(
|
10041
|
+
thread=info.ident,
|
10042
|
+
threadName=info.name,
|
10043
|
+
)
|
10044
|
+
|
10045
|
+
return self.record_defaults
|
10046
|
+
|
10047
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Thread]:
|
10048
|
+
if (
|
10049
|
+
(ident := rec.thread) is not None and
|
10050
|
+
(name := rec.threadName) is not None
|
10051
|
+
):
|
10052
|
+
return LoggingContextInfos.Thread(
|
10053
|
+
ident=ident,
|
10054
|
+
native_id=None,
|
10055
|
+
name=name,
|
10056
|
+
)
|
10057
|
+
|
10058
|
+
return None
|
10059
|
+
|
10060
|
+
class Process(OptionalAdapter[LoggingContextInfos.Process]):
|
10061
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Process]] = LoggingContextInfos.Process
|
10062
|
+
|
10063
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
10064
|
+
# Process ID if available - that is, if `hasattr(os, 'getpid')` - and `logging.logProcesses` is truthy,
|
10065
|
+
# otherwise None.
|
10066
|
+
process=(ta.Optional[int], None),
|
10067
|
+
)
|
10068
|
+
|
10069
|
+
def _info_to_record(self, info: LoggingContextInfos.Process) -> ta.Mapping[str, ta.Any]:
|
10070
|
+
if logging.logProcesses:
|
10071
|
+
return dict(
|
10072
|
+
process=info.pid,
|
10073
|
+
)
|
10074
|
+
|
10075
|
+
return self.record_defaults
|
10076
|
+
|
10077
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Process]:
|
10078
|
+
if (
|
10079
|
+
(pid := rec.process) is not None
|
10080
|
+
):
|
10081
|
+
return LoggingContextInfos.Process(
|
10082
|
+
pid=pid,
|
10083
|
+
)
|
10084
|
+
|
10085
|
+
return None
|
10086
|
+
|
10087
|
+
class Multiprocessing(OptionalAdapter[LoggingContextInfos.Multiprocessing]):
|
10088
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Multiprocessing]] = LoggingContextInfos.Multiprocessing
|
10089
|
+
|
10090
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
10091
|
+
# Process name if available. Set to None if `logging.logMultiprocessing` is not truthy. Otherwise, set to
|
10092
|
+
# 'MainProcess', then `sys.modules.get('multiprocessing').current_process().name` if that works, otherwise
|
10093
|
+
# remains as 'MainProcess'.
|
10094
|
+
#
|
10095
|
+
# As noted by stdlib:
|
10096
|
+
#
|
10097
|
+
# Errors may occur if multiprocessing has not finished loading yet - e.g. if a custom import hook causes
|
10098
|
+
# third-party code to run when multiprocessing calls import. See issue 8200 for an example
|
10099
|
+
#
|
10100
|
+
processName=(ta.Optional[str], None),
|
10101
|
+
)
|
10102
|
+
|
10103
|
+
def _info_to_record(self, info: LoggingContextInfos.Multiprocessing) -> ta.Mapping[str, ta.Any]:
|
10104
|
+
if logging.logMultiprocessing:
|
10105
|
+
return dict(
|
10106
|
+
processName=info.process_name,
|
10107
|
+
)
|
10108
|
+
|
10109
|
+
return self.record_defaults
|
10110
|
+
|
10111
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Multiprocessing]:
|
10112
|
+
if (
|
10113
|
+
(process_name := rec.processName) is not None
|
10114
|
+
):
|
10115
|
+
return LoggingContextInfos.Multiprocessing(
|
10116
|
+
process_name=process_name,
|
10117
|
+
)
|
10118
|
+
|
10119
|
+
return None
|
10120
|
+
|
10121
|
+
class AsyncioTask(OptionalAdapter[LoggingContextInfos.AsyncioTask]):
|
10122
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.AsyncioTask]] = LoggingContextInfos.AsyncioTask
|
10123
|
+
|
10124
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Union[ta.Any, ta.Tuple[ta.Any, ta.Any]]]] = dict(
|
10125
|
+
# Absent <3.12, otherwise asyncio.Task name if available, and `logging.logAsyncioTasks` is truthy. Set to
|
10126
|
+
# `sys.modules.get('asyncio').current_task().get_name()`, otherwise None.
|
10127
|
+
taskName=(ta.Optional[str], None),
|
10128
|
+
)
|
10129
|
+
|
10130
|
+
def _info_to_record(self, info: LoggingContextInfos.AsyncioTask) -> ta.Mapping[str, ta.Any]:
|
10131
|
+
if getattr(logging, 'logAsyncioTasks', None): # Absent <3.12
|
10132
|
+
return dict(
|
10133
|
+
taskName=info.name,
|
10134
|
+
)
|
10135
|
+
|
10136
|
+
return self.record_defaults
|
10137
|
+
|
10138
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.AsyncioTask]:
|
10139
|
+
if (
|
10140
|
+
(name := getattr(rec, 'taskName', None)) is not None
|
10141
|
+
):
|
10142
|
+
return LoggingContextInfos.AsyncioTask(
|
10143
|
+
name=name,
|
10144
|
+
)
|
10145
|
+
|
10146
|
+
return None
|
10147
|
+
|
10148
|
+
|
10149
|
+
_LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_: ta.Sequence[LoggingContextInfoRecordAdapters.Adapter] = [ # noqa
|
10150
|
+
LoggingContextInfoRecordAdapters.Name(),
|
10151
|
+
LoggingContextInfoRecordAdapters.Level(),
|
10152
|
+
LoggingContextInfoRecordAdapters.Msg(),
|
10153
|
+
LoggingContextInfoRecordAdapters.Time(),
|
10154
|
+
LoggingContextInfoRecordAdapters.Exc(),
|
10155
|
+
LoggingContextInfoRecordAdapters.Caller(),
|
10156
|
+
LoggingContextInfoRecordAdapters.SourceFile(),
|
10157
|
+
LoggingContextInfoRecordAdapters.Thread(),
|
10158
|
+
LoggingContextInfoRecordAdapters.Process(),
|
10159
|
+
LoggingContextInfoRecordAdapters.Multiprocessing(),
|
10160
|
+
LoggingContextInfoRecordAdapters.AsyncioTask(),
|
10161
|
+
]
|
10162
|
+
|
10163
|
+
_LOGGING_CONTEXT_INFO_RECORD_ADAPTERS: ta.Mapping[ta.Type[LoggingContextInfo], LoggingContextInfoRecordAdapters.Adapter] = { # noqa
|
10164
|
+
ad.info_cls: ad for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_
|
10165
|
+
}
|
10166
|
+
|
10167
|
+
|
10168
|
+
##
|
10169
|
+
|
10170
|
+
|
10171
|
+
KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(
|
10172
|
+
a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs
|
10173
|
+
)
|
9786
10174
|
|
9787
10175
|
|
9788
10176
|
# Formatter:
|
@@ -9806,14 +10194,17 @@ KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
|
|
9806
10194
|
KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
|
9807
10195
|
|
9808
10196
|
|
9809
|
-
##
|
9810
|
-
|
9811
|
-
|
9812
10197
|
class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
|
9813
10198
|
pass
|
9814
10199
|
|
9815
10200
|
|
9816
10201
|
def _check_std_logging_record_attrs() -> None:
|
10202
|
+
if (
|
10203
|
+
len([a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs]) !=
|
10204
|
+
len(KNOWN_STD_LOGGING_RECORD_ATTR_SET)
|
10205
|
+
):
|
10206
|
+
raise RuntimeError('Duplicate LoggingContextInfoRecordAdapter record attrs')
|
10207
|
+
|
9817
10208
|
rec_dct = dict(logging.makeLogRecord({}).__dict__)
|
9818
10209
|
|
9819
10210
|
if (unk_rec_fields := frozenset(rec_dct) - KNOWN_STD_LOGGING_RECORD_ATTR_SET):
|
@@ -9832,116 +10223,20 @@ _check_std_logging_record_attrs()
|
|
9832
10223
|
|
9833
10224
|
|
9834
10225
|
class LoggingContextLogRecord(logging.LogRecord):
|
9835
|
-
|
9836
|
-
|
9837
|
-
|
9838
|
-
|
9839
|
-
|
9840
|
-
|
9841
|
-
|
9842
|
-
|
9843
|
-
|
9844
|
-
|
9845
|
-
|
9846
|
-
|
9847
|
-
|
9848
|
-
|
9849
|
-
# msg,
|
9850
|
-
# args,
|
9851
|
-
# exc_info,
|
9852
|
-
# func=None,
|
9853
|
-
# sinfo=None,
|
9854
|
-
# **kwargs,
|
9855
|
-
*,
|
9856
|
-
name: str,
|
9857
|
-
msg: str,
|
9858
|
-
args: ta.Union[tuple, dict],
|
9859
|
-
|
9860
|
-
_logging_context: LoggingContext,
|
9861
|
-
) -> None:
|
9862
|
-
ctx = _logging_context
|
9863
|
-
|
9864
|
-
self.name: str = name
|
9865
|
-
|
9866
|
-
self.msg: str = msg
|
9867
|
-
|
9868
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L307
|
9869
|
-
if args and len(args) == 1 and isinstance(args[0], collections.abc.Mapping) and args[0]:
|
9870
|
-
args = args[0] # type: ignore[assignment]
|
9871
|
-
self.args: ta.Union[tuple, dict] = args
|
9872
|
-
|
9873
|
-
self.levelname: str = logging.getLevelName(ctx.level)
|
9874
|
-
self.levelno: int = ctx.level
|
9875
|
-
|
9876
|
-
if (caller := ctx.caller()) is not None:
|
9877
|
-
self.pathname: str = caller.file_path
|
9878
|
-
else:
|
9879
|
-
self.pathname = self._UNKNOWN_PATH_NAME
|
9880
|
-
|
9881
|
-
if (src_file := ctx.source_file()) is not None:
|
9882
|
-
self.filename: str = src_file.file_name
|
9883
|
-
self.module: str = src_file.module
|
9884
|
-
else:
|
9885
|
-
self.filename = self.pathname
|
9886
|
-
self.module = self._UNKNOWN_MODULE
|
9887
|
-
|
9888
|
-
self.exc_info: ta.Optional[LoggingExcInfoTuple] = ctx.exc_info_tuple
|
9889
|
-
self.exc_text: ta.Optional[str] = None
|
9890
|
-
|
9891
|
-
# If ctx.build_caller() was never called, we simply don't have a stack trace.
|
9892
|
-
if caller is not None:
|
9893
|
-
if (sinfo := caller.stack_info) is not None:
|
9894
|
-
self.stack_info: ta.Optional[str] = '\n'.join([
|
9895
|
-
self._STACK_INFO_PREFIX,
|
9896
|
-
sinfo[1:] if sinfo.endswith('\n') else sinfo,
|
9897
|
-
])
|
9898
|
-
else:
|
9899
|
-
self.stack_info = None
|
9900
|
-
|
9901
|
-
self.lineno: int = caller.line_no
|
9902
|
-
self.funcName: str = caller.name
|
9903
|
-
|
9904
|
-
else:
|
9905
|
-
self.stack_info = None
|
9906
|
-
|
9907
|
-
self.lineno = 0
|
9908
|
-
self.funcName = self._UNKNOWN_FUNC_NAME
|
9909
|
-
|
9910
|
-
times = ctx.times
|
9911
|
-
self.created: float = times.created
|
9912
|
-
self.msecs: float = times.msecs
|
9913
|
-
self.relativeCreated: float = times.relative_created
|
9914
|
-
|
9915
|
-
if logging.logThreads:
|
9916
|
-
thread = check.not_none(ctx.thread())
|
9917
|
-
self.thread: ta.Optional[int] = thread.ident
|
9918
|
-
self.threadName: ta.Optional[str] = thread.name
|
9919
|
-
else:
|
9920
|
-
self.thread = None
|
9921
|
-
self.threadName = None
|
9922
|
-
|
9923
|
-
if logging.logProcesses:
|
9924
|
-
process = check.not_none(ctx.process())
|
9925
|
-
self.process: ta.Optional[int] = process.pid
|
9926
|
-
else:
|
9927
|
-
self.process = None
|
9928
|
-
|
9929
|
-
if logging.logMultiprocessing:
|
9930
|
-
if (mp := ctx.multiprocessing()) is not None:
|
9931
|
-
self.processName: ta.Optional[str] = mp.process_name
|
9932
|
-
else:
|
9933
|
-
self.processName = None
|
9934
|
-
else:
|
9935
|
-
self.processName = None
|
9936
|
-
|
9937
|
-
# Absent <3.12
|
9938
|
-
if getattr(logging, 'logAsyncioTasks', None):
|
9939
|
-
if (at := ctx.asyncio_task()) is not None:
|
9940
|
-
self.taskName: ta.Optional[str] = at.name
|
9941
|
-
else:
|
9942
|
-
self.taskName = None
|
9943
|
-
else:
|
9944
|
-
self.taskName = None
|
10226
|
+
# LogRecord.__init__ args:
|
10227
|
+
# - name: str
|
10228
|
+
# - level: int
|
10229
|
+
# - pathname: str - Confusingly referred to as `fn` before the LogRecord ctor. May be empty or "(unknown file)".
|
10230
|
+
# - lineno: int - May be 0.
|
10231
|
+
# - msg: str
|
10232
|
+
# - args: tuple | dict | 1-tuple[dict]
|
10233
|
+
# - exc_info: LoggingExcInfoTuple | None
|
10234
|
+
# - func: str | None = None -> funcName
|
10235
|
+
# - sinfo: str | None = None -> stack_info
|
10236
|
+
|
10237
|
+
def __init__(self, *, _logging_context: LoggingContext) -> None: # noqa
|
10238
|
+
for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_:
|
10239
|
+
self.__dict__.update(ad.context_to_record(_logging_context))
|
9945
10240
|
|
9946
10241
|
|
9947
10242
|
########################################
|
@@ -11138,7 +11433,7 @@ class CoroHttpServerSocketHandler(SocketHandler_):
|
|
11138
11433
|
|
11139
11434
|
|
11140
11435
|
########################################
|
11141
|
-
# ../../../omlish/logs/std/
|
11436
|
+
# ../../../omlish/logs/std/loggers.py
|
11142
11437
|
|
11143
11438
|
|
11144
11439
|
##
|
@@ -11154,25 +11449,27 @@ class StdLogger(Logger):
|
|
11154
11449
|
def std(self) -> logging.Logger:
|
11155
11450
|
return self._std
|
11156
11451
|
|
11452
|
+
def is_enabled_for(self, level: LogLevel) -> bool:
|
11453
|
+
return self._std.isEnabledFor(level)
|
11454
|
+
|
11157
11455
|
def get_effective_level(self) -> LogLevel:
|
11158
11456
|
return self._std.getEffectiveLevel()
|
11159
11457
|
|
11160
11458
|
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> None:
|
11161
|
-
if not self.is_enabled_for(ctx.level):
|
11459
|
+
if not self.is_enabled_for(ctx.must_get_info(LoggingContextInfos.Level).level):
|
11162
11460
|
return
|
11163
11461
|
|
11164
|
-
ctx.
|
11165
|
-
|
11166
|
-
ms, args = self._prepare_msg_args(msg, *args)
|
11167
|
-
|
11168
|
-
rec = LoggingContextLogRecord(
|
11462
|
+
ctx.set_basic(
|
11169
11463
|
name=self._std.name,
|
11170
|
-
msg=ms,
|
11171
|
-
args=args,
|
11172
11464
|
|
11173
|
-
|
11465
|
+
msg=msg,
|
11466
|
+
args=args,
|
11174
11467
|
)
|
11175
11468
|
|
11469
|
+
ctx.capture()
|
11470
|
+
|
11471
|
+
rec = LoggingContextLogRecord(_logging_context=ctx)
|
11472
|
+
|
11176
11473
|
self._std.handle(rec)
|
11177
11474
|
|
11178
11475
|
|