omdev 0.0.0.dev427__py3-none-any.whl → 0.0.0.dev428__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/ci/docker/dataserver.py +2 -2
- omdev/interp/inspect.py +2 -1
- omdev/interp/providers/system.py +2 -2
- omdev/interp/pyenv/provider.py +2 -2
- omdev/interp/uv/provider.py +3 -2
- omdev/interp/uv/uv.py +2 -2
- omdev/interp/venvs.py +2 -2
- omdev/scripts/ci.py +4338 -3240
- omdev/scripts/interp.py +111 -9
- omdev/scripts/pyproject.py +1346 -146
- {omdev-0.0.0.dev427.dist-info → omdev-0.0.0.dev428.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev427.dist-info → omdev-0.0.0.dev428.dist-info}/RECORD +16 -16
- {omdev-0.0.0.dev427.dist-info → omdev-0.0.0.dev428.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev427.dist-info → omdev-0.0.0.dev428.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev427.dist-info → omdev-0.0.0.dev428.dist-info}/licenses/LICENSE +0 -0
- {omdev-0.0.0.dev427.dist-info → omdev-0.0.0.dev428.dist-info}/top_level.txt +0 -0
omdev/scripts/pyproject.py
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# @omlish-generated
|
6
6
|
# @omlish-amalg-output ../pyproject/cli.py
|
7
7
|
# @omlish-git-diff-omit
|
8
|
-
# ruff: noqa: N802 TC003 UP006 UP007 UP036 UP043 UP045
|
8
|
+
# ruff: noqa: N802 TC003 UP006 UP007 UP036 UP043 UP045 UP046
|
9
9
|
"""
|
10
10
|
TODO:
|
11
11
|
- check / tests, src dir sets
|
@@ -65,6 +65,7 @@ import tarfile
|
|
65
65
|
import tempfile
|
66
66
|
import threading
|
67
67
|
import time
|
68
|
+
import traceback
|
68
69
|
import types
|
69
70
|
import typing as ta
|
70
71
|
import uuid
|
@@ -112,6 +113,9 @@ A0 = ta.TypeVar('A0')
|
|
112
113
|
A1 = ta.TypeVar('A1')
|
113
114
|
A2 = ta.TypeVar('A2')
|
114
115
|
|
116
|
+
# ../../omlish/logs/levels.py
|
117
|
+
LogLevel = int # ta.TypeAlias
|
118
|
+
|
115
119
|
# ../packaging/specifiers.py
|
116
120
|
UnparsedVersion = ta.Union['Version', str]
|
117
121
|
UnparsedVersionVar = ta.TypeVar('UnparsedVersionVar', bound=UnparsedVersion)
|
@@ -135,6 +139,14 @@ InjectorProviderFn = ta.Callable[['Injector'], ta.Any]
|
|
135
139
|
InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
|
136
140
|
InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
137
141
|
|
142
|
+
# ../../omlish/logs/contexts.py
|
143
|
+
LoggingExcInfoTuple = ta.Tuple[ta.Type[BaseException], BaseException, ta.Optional[types.TracebackType]] # ta.TypeAlias
|
144
|
+
LoggingExcInfo = ta.Union[BaseException, LoggingExcInfoTuple] # ta.TypeAlias
|
145
|
+
LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
|
146
|
+
|
147
|
+
# ../../omlish/logs/base.py
|
148
|
+
LoggingMsgFn = ta.Callable[[], ta.Union[str, tuple]] # ta.TypeAlias
|
149
|
+
|
138
150
|
# ../../omlish/subprocesses/base.py
|
139
151
|
SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
|
140
152
|
|
@@ -2948,14 +2960,188 @@ def typing_annotations_attr() -> str:
|
|
2948
2960
|
|
2949
2961
|
|
2950
2962
|
########################################
|
2951
|
-
# ../../../omlish/logs/
|
2963
|
+
# ../../../omlish/logs/infos.py
|
2964
|
+
|
2965
|
+
|
2966
|
+
##
|
2967
|
+
|
2968
|
+
|
2969
|
+
class _LoggingContextInfo:
|
2970
|
+
def __mro_entries__(self, bases):
|
2971
|
+
return ()
|
2972
|
+
|
2973
|
+
|
2974
|
+
LoggingContextInfo: type = ta.cast(ta.Any, _LoggingContextInfo())
|
2975
|
+
|
2976
|
+
|
2977
|
+
##
|
2978
|
+
|
2979
|
+
|
2980
|
+
@ta.final
|
2981
|
+
class LoggingSourceFileInfo(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
|
2982
|
+
file_name: str
|
2983
|
+
module: str
|
2984
|
+
|
2985
|
+
@classmethod
|
2986
|
+
def build(cls, file_path: ta.Optional[str]) -> ta.Optional['LoggingSourceFileInfo']:
|
2987
|
+
if file_path is None:
|
2988
|
+
return None
|
2989
|
+
|
2990
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L331-L336 # noqa
|
2991
|
+
try:
|
2992
|
+
file_name = os.path.basename(file_path)
|
2993
|
+
module = os.path.splitext(file_name)[0]
|
2994
|
+
except (TypeError, ValueError, AttributeError):
|
2995
|
+
return None
|
2996
|
+
|
2997
|
+
return cls(
|
2998
|
+
file_name,
|
2999
|
+
module,
|
3000
|
+
)
|
3001
|
+
|
3002
|
+
|
3003
|
+
##
|
3004
|
+
|
3005
|
+
|
3006
|
+
@ta.final
|
3007
|
+
class LoggingThreadInfo(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
|
3008
|
+
ident: int
|
3009
|
+
native_id: ta.Optional[int]
|
3010
|
+
name: str
|
3011
|
+
|
3012
|
+
@classmethod
|
3013
|
+
def build(cls) -> 'LoggingThreadInfo':
|
3014
|
+
return cls(
|
3015
|
+
threading.get_ident(),
|
3016
|
+
threading.get_native_id() if hasattr(threading, 'get_native_id') else None,
|
3017
|
+
threading.current_thread().name,
|
3018
|
+
)
|
3019
|
+
|
3020
|
+
|
3021
|
+
##
|
3022
|
+
|
3023
|
+
|
3024
|
+
@ta.final
|
3025
|
+
class LoggingProcessInfo(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
|
3026
|
+
pid: int
|
3027
|
+
|
3028
|
+
@classmethod
|
3029
|
+
def build(cls) -> 'LoggingProcessInfo':
|
3030
|
+
return cls(
|
3031
|
+
os.getpid(),
|
3032
|
+
)
|
3033
|
+
|
3034
|
+
|
3035
|
+
##
|
3036
|
+
|
3037
|
+
|
3038
|
+
@ta.final
|
3039
|
+
class LoggingMultiprocessingInfo(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
|
3040
|
+
process_name: str
|
3041
|
+
|
3042
|
+
@classmethod
|
3043
|
+
def build(cls) -> ta.Optional['LoggingMultiprocessingInfo']:
|
3044
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L355-L364 # noqa
|
3045
|
+
if (mp := sys.modules.get('multiprocessing')) is None:
|
3046
|
+
return None
|
3047
|
+
|
3048
|
+
return cls(
|
3049
|
+
mp.current_process().name,
|
3050
|
+
)
|
3051
|
+
|
3052
|
+
|
3053
|
+
##
|
3054
|
+
|
3055
|
+
|
3056
|
+
@ta.final
|
3057
|
+
class LoggingAsyncioTaskInfo(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
|
3058
|
+
name: str
|
3059
|
+
|
3060
|
+
@classmethod
|
3061
|
+
def build(cls) -> ta.Optional['LoggingAsyncioTaskInfo']:
|
3062
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L372-L377 # noqa
|
3063
|
+
if (asyncio := sys.modules.get('asyncio')) is None:
|
3064
|
+
return None
|
3065
|
+
|
3066
|
+
try:
|
3067
|
+
task = asyncio.current_task()
|
3068
|
+
except Exception: # noqa
|
3069
|
+
return None
|
3070
|
+
|
3071
|
+
if task is None:
|
3072
|
+
return None
|
3073
|
+
|
3074
|
+
return cls(
|
3075
|
+
task.get_name(), # Always non-None
|
3076
|
+
)
|
3077
|
+
|
3078
|
+
|
3079
|
+
########################################
|
3080
|
+
# ../../../omlish/logs/levels.py
|
2952
3081
|
|
2953
3082
|
|
2954
3083
|
##
|
2955
3084
|
|
2956
3085
|
|
2957
|
-
|
2958
|
-
|
3086
|
+
@ta.final
|
3087
|
+
class NamedLogLevel(int):
|
3088
|
+
# logging.getLevelNamesMapping (or, as that is unavailable <3.11, logging._nameToLevel) includes the deprecated
|
3089
|
+
# aliases.
|
3090
|
+
_NAMES_BY_INT: ta.ClassVar[ta.Mapping[LogLevel, str]] = dict(sorted(logging._levelToName.items(), key=lambda t: -t[0])) # noqa
|
3091
|
+
|
3092
|
+
_INTS_BY_NAME: ta.ClassVar[ta.Mapping[str, LogLevel]] = {v: k for k, v in _NAMES_BY_INT.items()}
|
3093
|
+
|
3094
|
+
_NAME_INT_PAIRS: ta.ClassVar[ta.Sequence[ta.Tuple[str, LogLevel]]] = list(_INTS_BY_NAME.items())
|
3095
|
+
|
3096
|
+
#
|
3097
|
+
|
3098
|
+
@property
|
3099
|
+
def exact_name(self) -> ta.Optional[str]:
|
3100
|
+
return self._NAMES_BY_INT.get(self)
|
3101
|
+
|
3102
|
+
_effective_name: ta.Optional[str]
|
3103
|
+
|
3104
|
+
@property
|
3105
|
+
def effective_name(self) -> ta.Optional[str]:
|
3106
|
+
try:
|
3107
|
+
return self._effective_name
|
3108
|
+
except AttributeError:
|
3109
|
+
pass
|
3110
|
+
|
3111
|
+
if (n := self.exact_name) is None:
|
3112
|
+
for n, i in self._NAME_INT_PAIRS: # noqa
|
3113
|
+
if self >= i:
|
3114
|
+
break
|
3115
|
+
else:
|
3116
|
+
n = None
|
3117
|
+
|
3118
|
+
self._effective_name = n
|
3119
|
+
return n
|
3120
|
+
|
3121
|
+
#
|
3122
|
+
|
3123
|
+
def __repr__(self) -> str:
|
3124
|
+
return f'{self.__class__.__name__}({int(self)})'
|
3125
|
+
|
3126
|
+
def __str__(self) -> str:
|
3127
|
+
return self.exact_name or f'{self.effective_name or "INVALID"}:{int(self)}'
|
3128
|
+
|
3129
|
+
#
|
3130
|
+
|
3131
|
+
CRITICAL: ta.ClassVar['NamedLogLevel']
|
3132
|
+
ERROR: ta.ClassVar['NamedLogLevel']
|
3133
|
+
WARNING: ta.ClassVar['NamedLogLevel']
|
3134
|
+
INFO: ta.ClassVar['NamedLogLevel']
|
3135
|
+
DEBUG: ta.ClassVar['NamedLogLevel']
|
3136
|
+
NOTSET: ta.ClassVar['NamedLogLevel']
|
3137
|
+
|
3138
|
+
|
3139
|
+
NamedLogLevel.CRITICAL = NamedLogLevel(logging.CRITICAL)
|
3140
|
+
NamedLogLevel.ERROR = NamedLogLevel(logging.ERROR)
|
3141
|
+
NamedLogLevel.WARNING = NamedLogLevel(logging.WARNING)
|
3142
|
+
NamedLogLevel.INFO = NamedLogLevel(logging.INFO)
|
3143
|
+
NamedLogLevel.DEBUG = NamedLogLevel(logging.DEBUG)
|
3144
|
+
NamedLogLevel.NOTSET = NamedLogLevel(logging.NOTSET)
|
2959
3145
|
|
2960
3146
|
|
2961
3147
|
########################################
|
@@ -3078,6 +3264,17 @@ class ProxyLoggingHandler(ProxyLoggingFilterer, logging.Handler):
|
|
3078
3264
|
self._underlying.handleError(record)
|
3079
3265
|
|
3080
3266
|
|
3267
|
+
########################################
|
3268
|
+
# ../../../omlish/logs/warnings.py
|
3269
|
+
|
3270
|
+
|
3271
|
+
##
|
3272
|
+
|
3273
|
+
|
3274
|
+
class LoggingSetupWarning(Warning):
|
3275
|
+
pass
|
3276
|
+
|
3277
|
+
|
3081
3278
|
########################################
|
3082
3279
|
# ../../cexts/magic.py
|
3083
3280
|
|
@@ -3832,90 +4029,6 @@ class SpecifierSet(BaseSpecifier):
|
|
3832
4029
|
return iter(filtered)
|
3833
4030
|
|
3834
4031
|
|
3835
|
-
########################################
|
3836
|
-
# ../reqs.py
|
3837
|
-
"""
|
3838
|
-
TODO:
|
3839
|
-
- embed pip._internal.req.parse_requirements, add additional env stuff? breaks compat with raw pip
|
3840
|
-
"""
|
3841
|
-
|
3842
|
-
|
3843
|
-
log = get_module_logger(globals()) # noqa
|
3844
|
-
|
3845
|
-
|
3846
|
-
##
|
3847
|
-
|
3848
|
-
|
3849
|
-
class RequirementsRewriter:
|
3850
|
-
def __init__(
|
3851
|
-
self,
|
3852
|
-
venv: ta.Optional[str] = None,
|
3853
|
-
) -> None:
|
3854
|
-
super().__init__()
|
3855
|
-
|
3856
|
-
self._venv = venv
|
3857
|
-
|
3858
|
-
@cached_nullary
|
3859
|
-
def _tmp_dir(self) -> str:
|
3860
|
-
return tempfile.mkdtemp('-omlish-reqs')
|
3861
|
-
|
3862
|
-
VENV_MAGIC = '# @omlish-venv'
|
3863
|
-
|
3864
|
-
def rewrite_file(self, in_file: str) -> str:
|
3865
|
-
with open(in_file) as f:
|
3866
|
-
src = f.read()
|
3867
|
-
|
3868
|
-
in_lines = src.splitlines(keepends=True)
|
3869
|
-
out_lines = []
|
3870
|
-
|
3871
|
-
for l in in_lines:
|
3872
|
-
if self.VENV_MAGIC in l:
|
3873
|
-
lp, _, rp = l.partition(self.VENV_MAGIC)
|
3874
|
-
rp = rp.partition('#')[0]
|
3875
|
-
omit = False
|
3876
|
-
for v in rp.split():
|
3877
|
-
if v[0] == '!':
|
3878
|
-
if self._venv is not None and self._venv == v[1:]:
|
3879
|
-
omit = True
|
3880
|
-
break
|
3881
|
-
else:
|
3882
|
-
raise NotImplementedError
|
3883
|
-
|
3884
|
-
if omit:
|
3885
|
-
out_lines.append('# OMITTED: ' + l)
|
3886
|
-
continue
|
3887
|
-
|
3888
|
-
out_req = self.rewrite(l.rstrip('\n'), for_file=True)
|
3889
|
-
out_lines.append(out_req + '\n')
|
3890
|
-
|
3891
|
-
out_file = os.path.join(self._tmp_dir(), os.path.basename(in_file))
|
3892
|
-
if os.path.exists(out_file):
|
3893
|
-
raise Exception(f'file exists: {out_file}')
|
3894
|
-
|
3895
|
-
with open(out_file, 'w') as f:
|
3896
|
-
f.write(''.join(out_lines))
|
3897
|
-
log.info('Rewrote requirements file %s to %s', in_file, out_file)
|
3898
|
-
return out_file
|
3899
|
-
|
3900
|
-
def rewrite(self, in_req: str, *, for_file: bool = False) -> str:
|
3901
|
-
if in_req.strip().startswith('-r'):
|
3902
|
-
l = in_req.strip()
|
3903
|
-
lp, _, rp = l.partition(' ')
|
3904
|
-
if lp == '-r':
|
3905
|
-
inc_in_file, _, rest = rp.partition(' ')
|
3906
|
-
else:
|
3907
|
-
inc_in_file, rest = lp[2:], rp
|
3908
|
-
|
3909
|
-
inc_out_file = self.rewrite_file(inc_in_file)
|
3910
|
-
if for_file:
|
3911
|
-
return ' '.join(['-r ', inc_out_file, rest])
|
3912
|
-
else:
|
3913
|
-
return '-r' + inc_out_file
|
3914
|
-
|
3915
|
-
else:
|
3916
|
-
return in_req
|
3917
|
-
|
3918
|
-
|
3919
4032
|
########################################
|
3920
4033
|
# ../../../omlish/argparse/cli.py
|
3921
4034
|
"""
|
@@ -5412,6 +5525,104 @@ class PredicateTimeout(Timeout):
|
|
5412
5525
|
return self()
|
5413
5526
|
|
5414
5527
|
|
5528
|
+
########################################
|
5529
|
+
# ../../../omlish/logs/callers.py
|
5530
|
+
|
5531
|
+
|
5532
|
+
##
|
5533
|
+
|
5534
|
+
|
5535
|
+
class LoggingCaller(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
|
5536
|
+
file_path: str
|
5537
|
+
line_no: int
|
5538
|
+
name: str
|
5539
|
+
stack_info: ta.Optional[str]
|
5540
|
+
|
5541
|
+
@classmethod
|
5542
|
+
def is_internal_frame(cls, frame: types.FrameType) -> bool:
|
5543
|
+
file_path = os.path.normcase(frame.f_code.co_filename)
|
5544
|
+
|
5545
|
+
# Yes, really.
|
5546
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L204
|
5547
|
+
# https://github.com/python/cpython/commit/5ca6d7469be53960843df39bb900e9c3359f127f
|
5548
|
+
if 'importlib' in file_path and '_bootstrap' in file_path:
|
5549
|
+
return True
|
5550
|
+
|
5551
|
+
return False
|
5552
|
+
|
5553
|
+
@classmethod
|
5554
|
+
def find_frame(cls, ofs: int = 0) -> ta.Optional[types.FrameType]:
|
5555
|
+
f: ta.Optional[types.FrameType] = sys._getframe(2 + ofs) # noqa
|
5556
|
+
|
5557
|
+
while f is not None:
|
5558
|
+
# NOTE: We don't check __file__ like stdlib since we may be running amalgamated - we rely on careful, manual
|
5559
|
+
# stack_offset management.
|
5560
|
+
if hasattr(f, 'f_code'):
|
5561
|
+
return f
|
5562
|
+
|
5563
|
+
f = f.f_back
|
5564
|
+
|
5565
|
+
return None
|
5566
|
+
|
5567
|
+
@classmethod
|
5568
|
+
def find(
|
5569
|
+
cls,
|
5570
|
+
ofs: int = 0,
|
5571
|
+
*,
|
5572
|
+
stack_info: bool = False,
|
5573
|
+
) -> ta.Optional['LoggingCaller']:
|
5574
|
+
if (f := cls.find_frame(ofs + 1)) is None:
|
5575
|
+
return None
|
5576
|
+
|
5577
|
+
# https://github.com/python/cpython/blob/08e9794517063c8cd92c48714071b1d3c60b71bd/Lib/logging/__init__.py#L1616-L1623 # noqa
|
5578
|
+
sinfo = None
|
5579
|
+
if stack_info:
|
5580
|
+
sio = io.StringIO()
|
5581
|
+
traceback.print_stack(f, file=sio)
|
5582
|
+
sinfo = sio.getvalue()
|
5583
|
+
sio.close()
|
5584
|
+
if sinfo[-1] == '\n':
|
5585
|
+
sinfo = sinfo[:-1]
|
5586
|
+
|
5587
|
+
return cls(
|
5588
|
+
f.f_code.co_filename,
|
5589
|
+
f.f_lineno or 0,
|
5590
|
+
f.f_code.co_name,
|
5591
|
+
sinfo,
|
5592
|
+
)
|
5593
|
+
|
5594
|
+
|
5595
|
+
########################################
|
5596
|
+
# ../../../omlish/logs/protocols.py
|
5597
|
+
|
5598
|
+
|
5599
|
+
##
|
5600
|
+
|
5601
|
+
|
5602
|
+
class LoggerLike(ta.Protocol):
|
5603
|
+
"""Satisfied by both our Logger and stdlib logging.Logger."""
|
5604
|
+
|
5605
|
+
def isEnabledFor(self, level: LogLevel) -> bool: ... # noqa
|
5606
|
+
|
5607
|
+
def getEffectiveLevel(self) -> LogLevel: ... # noqa
|
5608
|
+
|
5609
|
+
#
|
5610
|
+
|
5611
|
+
def log(self, level: LogLevel, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5612
|
+
|
5613
|
+
def debug(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5614
|
+
|
5615
|
+
def info(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5616
|
+
|
5617
|
+
def warning(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5618
|
+
|
5619
|
+
def error(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5620
|
+
|
5621
|
+
def exception(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5622
|
+
|
5623
|
+
def critical(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
5624
|
+
|
5625
|
+
|
5415
5626
|
########################################
|
5416
5627
|
# ../../../omlish/logs/std/json.py
|
5417
5628
|
"""
|
@@ -5470,17 +5681,100 @@ class JsonLoggingFormatter(logging.Formatter):
|
|
5470
5681
|
|
5471
5682
|
|
5472
5683
|
########################################
|
5473
|
-
#
|
5684
|
+
# ../../../omlish/logs/times.py
|
5474
5685
|
|
5475
5686
|
|
5476
5687
|
##
|
5477
5688
|
|
5478
5689
|
|
5479
|
-
#
|
5480
|
-
|
5481
|
-
|
5482
|
-
|
5483
|
-
|
5690
|
+
class LoggingTimeFields(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
|
5691
|
+
"""Maps directly to stdlib `logging.LogRecord` fields, and must be kept in sync with it."""
|
5692
|
+
|
5693
|
+
created: float
|
5694
|
+
msecs: float
|
5695
|
+
relative_created: float
|
5696
|
+
|
5697
|
+
@classmethod
|
5698
|
+
def get_std_start_time_ns(cls) -> int:
|
5699
|
+
x: ta.Any = logging._startTime # type: ignore[attr-defined] # noqa
|
5700
|
+
|
5701
|
+
# Before 3.13.0b1 this will be `time.time()`, a float of seconds. After that, it will be `time.time_ns()`, an
|
5702
|
+
# int.
|
5703
|
+
#
|
5704
|
+
# See:
|
5705
|
+
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
5706
|
+
#
|
5707
|
+
if isinstance(x, float):
|
5708
|
+
return int(x * 1e9)
|
5709
|
+
else:
|
5710
|
+
return x
|
5711
|
+
|
5712
|
+
@classmethod
|
5713
|
+
def build(
|
5714
|
+
cls,
|
5715
|
+
time_ns: int,
|
5716
|
+
*,
|
5717
|
+
start_time_ns: ta.Optional[int] = None,
|
5718
|
+
) -> 'LoggingTimeFields':
|
5719
|
+
# https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
5720
|
+
created = time_ns / 1e9 # ns to float seconds
|
5721
|
+
|
5722
|
+
# Get the number of whole milliseconds (0-999) in the fractional part of seconds.
|
5723
|
+
# Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
|
5724
|
+
# Convert to float by adding 0.0 for historical reasons. See gh-89047
|
5725
|
+
msecs = (time_ns % 1_000_000_000) // 1_000_000 + 0.0
|
5726
|
+
|
5727
|
+
# https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
5728
|
+
if msecs == 999.0 and int(created) != time_ns // 1_000_000_000:
|
5729
|
+
# ns -> sec conversion can round up, e.g:
|
5730
|
+
# 1_677_903_920_999_999_900 ns --> 1_677_903_921.0 sec
|
5731
|
+
msecs = 0.0
|
5732
|
+
|
5733
|
+
if start_time_ns is None:
|
5734
|
+
start_time_ns = cls.get_std_start_time_ns()
|
5735
|
+
relative_created = (time_ns - start_time_ns) / 1e6
|
5736
|
+
|
5737
|
+
return cls(
|
5738
|
+
created,
|
5739
|
+
msecs,
|
5740
|
+
relative_created,
|
5741
|
+
)
|
5742
|
+
|
5743
|
+
|
5744
|
+
##
|
5745
|
+
|
5746
|
+
|
5747
|
+
class UnexpectedLoggingStartTimeWarning(LoggingSetupWarning):
|
5748
|
+
pass
|
5749
|
+
|
5750
|
+
|
5751
|
+
def _check_logging_start_time() -> None:
|
5752
|
+
if (x := LoggingTimeFields.get_std_start_time_ns()) < (t := time.time()):
|
5753
|
+
import warnings # noqa
|
5754
|
+
|
5755
|
+
warnings.warn(
|
5756
|
+
f'Unexpected logging start time detected: '
|
5757
|
+
f'get_std_start_time_ns={x}, '
|
5758
|
+
f'time.time()={t}',
|
5759
|
+
UnexpectedLoggingStartTimeWarning,
|
5760
|
+
)
|
5761
|
+
|
5762
|
+
|
5763
|
+
_check_logging_start_time()
|
5764
|
+
|
5765
|
+
|
5766
|
+
########################################
|
5767
|
+
# ../../interp/types.py
|
5768
|
+
|
5769
|
+
|
5770
|
+
##
|
5771
|
+
|
5772
|
+
|
5773
|
+
# See https://peps.python.org/pep-3149/
|
5774
|
+
INTERP_OPT_GLYPHS_BY_ATTR: ta.Mapping[str, str] = collections.OrderedDict([
|
5775
|
+
('debug', 'd'),
|
5776
|
+
('threaded', 't'),
|
5777
|
+
])
|
5484
5778
|
|
5485
5779
|
INTERP_OPT_ATTRS_BY_GLYPH: ta.Mapping[str, str] = collections.OrderedDict(
|
5486
5780
|
(g, a) for a, g in INTERP_OPT_GLYPHS_BY_ATTR.items()
|
@@ -6674,6 +6968,263 @@ class InjectionApi:
|
|
6674
6968
|
inj = InjectionApi()
|
6675
6969
|
|
6676
6970
|
|
6971
|
+
########################################
|
6972
|
+
# ../../../omlish/logs/contexts.py
|
6973
|
+
|
6974
|
+
|
6975
|
+
##
|
6976
|
+
|
6977
|
+
|
6978
|
+
class LoggingContext(Abstract):
|
6979
|
+
@property
|
6980
|
+
@abc.abstractmethod
|
6981
|
+
def level(self) -> NamedLogLevel:
|
6982
|
+
raise NotImplementedError
|
6983
|
+
|
6984
|
+
#
|
6985
|
+
|
6986
|
+
@property
|
6987
|
+
@abc.abstractmethod
|
6988
|
+
def time_ns(self) -> int:
|
6989
|
+
raise NotImplementedError
|
6990
|
+
|
6991
|
+
@property
|
6992
|
+
@abc.abstractmethod
|
6993
|
+
def times(self) -> LoggingTimeFields:
|
6994
|
+
raise NotImplementedError
|
6995
|
+
|
6996
|
+
#
|
6997
|
+
|
6998
|
+
@property
|
6999
|
+
@abc.abstractmethod
|
7000
|
+
def exc_info(self) -> ta.Optional[LoggingExcInfo]:
|
7001
|
+
raise NotImplementedError
|
7002
|
+
|
7003
|
+
@property
|
7004
|
+
@abc.abstractmethod
|
7005
|
+
def exc_info_tuple(self) -> ta.Optional[LoggingExcInfoTuple]:
|
7006
|
+
raise NotImplementedError
|
7007
|
+
|
7008
|
+
#
|
7009
|
+
|
7010
|
+
@abc.abstractmethod
|
7011
|
+
def caller(self) -> ta.Optional[LoggingCaller]:
|
7012
|
+
raise NotImplementedError
|
7013
|
+
|
7014
|
+
@abc.abstractmethod
|
7015
|
+
def source_file(self) -> ta.Optional[LoggingSourceFileInfo]:
|
7016
|
+
raise NotImplementedError
|
7017
|
+
|
7018
|
+
#
|
7019
|
+
|
7020
|
+
@abc.abstractmethod
|
7021
|
+
def thread(self) -> ta.Optional[LoggingThreadInfo]:
|
7022
|
+
raise NotImplementedError
|
7023
|
+
|
7024
|
+
@abc.abstractmethod
|
7025
|
+
def process(self) -> ta.Optional[LoggingProcessInfo]:
|
7026
|
+
raise NotImplementedError
|
7027
|
+
|
7028
|
+
@abc.abstractmethod
|
7029
|
+
def multiprocessing(self) -> ta.Optional[LoggingMultiprocessingInfo]:
|
7030
|
+
raise NotImplementedError
|
7031
|
+
|
7032
|
+
@abc.abstractmethod
|
7033
|
+
def asyncio_task(self) -> ta.Optional[LoggingAsyncioTaskInfo]:
|
7034
|
+
raise NotImplementedError
|
7035
|
+
|
7036
|
+
|
7037
|
+
##
|
7038
|
+
|
7039
|
+
|
7040
|
+
class CaptureLoggingContext(LoggingContext, Abstract):
|
7041
|
+
class AlreadyCapturedError(Exception):
|
7042
|
+
pass
|
7043
|
+
|
7044
|
+
class NotCapturedError(Exception):
|
7045
|
+
pass
|
7046
|
+
|
7047
|
+
@abc.abstractmethod
|
7048
|
+
def capture(self) -> None:
|
7049
|
+
"""Must be cooperatively called only from the expected locations."""
|
7050
|
+
|
7051
|
+
raise NotImplementedError
|
7052
|
+
|
7053
|
+
|
7054
|
+
@ta.final
|
7055
|
+
class CaptureLoggingContextImpl(CaptureLoggingContext):
|
7056
|
+
@ta.final
|
7057
|
+
class NOT_SET: # noqa
|
7058
|
+
def __new__(cls, *args, **kwargs): # noqa
|
7059
|
+
raise TypeError
|
7060
|
+
|
7061
|
+
#
|
7062
|
+
|
7063
|
+
def __init__(
|
7064
|
+
self,
|
7065
|
+
level: LogLevel,
|
7066
|
+
*,
|
7067
|
+
time_ns: ta.Optional[int] = None,
|
7068
|
+
|
7069
|
+
exc_info: LoggingExcInfoArg = False,
|
7070
|
+
|
7071
|
+
caller: ta.Union[LoggingCaller, ta.Type[NOT_SET], None] = NOT_SET,
|
7072
|
+
stack_offset: int = 0,
|
7073
|
+
stack_info: bool = False,
|
7074
|
+
) -> None:
|
7075
|
+
self._level: NamedLogLevel = level if level.__class__ is NamedLogLevel else NamedLogLevel(level) # type: ignore[assignment] # noqa
|
7076
|
+
|
7077
|
+
#
|
7078
|
+
|
7079
|
+
if time_ns is None:
|
7080
|
+
time_ns = time.time_ns()
|
7081
|
+
self._time_ns: int = time_ns
|
7082
|
+
|
7083
|
+
#
|
7084
|
+
|
7085
|
+
if exc_info is True:
|
7086
|
+
sys_exc_info = sys.exc_info()
|
7087
|
+
if sys_exc_info[0] is not None:
|
7088
|
+
exc_info = sys_exc_info
|
7089
|
+
else:
|
7090
|
+
exc_info = None
|
7091
|
+
elif exc_info is False:
|
7092
|
+
exc_info = None
|
7093
|
+
|
7094
|
+
if exc_info is not None:
|
7095
|
+
self._exc_info: ta.Optional[LoggingExcInfo] = exc_info
|
7096
|
+
if isinstance(exc_info, BaseException):
|
7097
|
+
self._exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = (type(exc_info), exc_info, exc_info.__traceback__) # noqa
|
7098
|
+
else:
|
7099
|
+
self._exc_info_tuple = exc_info
|
7100
|
+
|
7101
|
+
#
|
7102
|
+
|
7103
|
+
if caller is not CaptureLoggingContextImpl.NOT_SET:
|
7104
|
+
self._caller = caller # type: ignore[assignment]
|
7105
|
+
else:
|
7106
|
+
self._stack_offset = stack_offset
|
7107
|
+
self._stack_info = stack_info
|
7108
|
+
|
7109
|
+
##
|
7110
|
+
|
7111
|
+
@property
|
7112
|
+
def level(self) -> NamedLogLevel:
|
7113
|
+
return self._level
|
7114
|
+
|
7115
|
+
#
|
7116
|
+
|
7117
|
+
@property
|
7118
|
+
def time_ns(self) -> int:
|
7119
|
+
return self._time_ns
|
7120
|
+
|
7121
|
+
_times: LoggingTimeFields
|
7122
|
+
|
7123
|
+
@property
|
7124
|
+
def times(self) -> LoggingTimeFields:
|
7125
|
+
try:
|
7126
|
+
return self._times
|
7127
|
+
except AttributeError:
|
7128
|
+
pass
|
7129
|
+
|
7130
|
+
times = self._times = LoggingTimeFields.build(self.time_ns)
|
7131
|
+
return times
|
7132
|
+
|
7133
|
+
#
|
7134
|
+
|
7135
|
+
_exc_info: ta.Optional[LoggingExcInfo] = None
|
7136
|
+
_exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = None
|
7137
|
+
|
7138
|
+
@property
|
7139
|
+
def exc_info(self) -> ta.Optional[LoggingExcInfo]:
|
7140
|
+
return self._exc_info
|
7141
|
+
|
7142
|
+
@property
|
7143
|
+
def exc_info_tuple(self) -> ta.Optional[LoggingExcInfoTuple]:
|
7144
|
+
return self._exc_info_tuple
|
7145
|
+
|
7146
|
+
##
|
7147
|
+
|
7148
|
+
_stack_offset: int
|
7149
|
+
_stack_info: bool
|
7150
|
+
|
7151
|
+
def inc_stack_offset(self, ofs: int = 1) -> 'CaptureLoggingContext':
|
7152
|
+
if hasattr(self, '_stack_offset'):
|
7153
|
+
self._stack_offset += ofs
|
7154
|
+
return self
|
7155
|
+
|
7156
|
+
_has_captured: bool = False
|
7157
|
+
|
7158
|
+
_caller: ta.Optional[LoggingCaller]
|
7159
|
+
_source_file: ta.Optional[LoggingSourceFileInfo]
|
7160
|
+
|
7161
|
+
_thread: ta.Optional[LoggingThreadInfo]
|
7162
|
+
_process: ta.Optional[LoggingProcessInfo]
|
7163
|
+
_multiprocessing: ta.Optional[LoggingMultiprocessingInfo]
|
7164
|
+
_asyncio_task: ta.Optional[LoggingAsyncioTaskInfo]
|
7165
|
+
|
7166
|
+
def capture(self) -> None:
|
7167
|
+
if self._has_captured:
|
7168
|
+
raise CaptureLoggingContextImpl.AlreadyCapturedError
|
7169
|
+
self._has_captured = True
|
7170
|
+
|
7171
|
+
if not hasattr(self, '_caller'):
|
7172
|
+
self._caller = LoggingCaller.find(
|
7173
|
+
self._stack_offset + 1,
|
7174
|
+
stack_info=self._stack_info,
|
7175
|
+
)
|
7176
|
+
|
7177
|
+
if (caller := self._caller) is not None:
|
7178
|
+
self._source_file = LoggingSourceFileInfo.build(caller.file_path)
|
7179
|
+
else:
|
7180
|
+
self._source_file = None
|
7181
|
+
|
7182
|
+
self._thread = LoggingThreadInfo.build()
|
7183
|
+
self._process = LoggingProcessInfo.build()
|
7184
|
+
self._multiprocessing = LoggingMultiprocessingInfo.build()
|
7185
|
+
self._asyncio_task = LoggingAsyncioTaskInfo.build()
|
7186
|
+
|
7187
|
+
#
|
7188
|
+
|
7189
|
+
def caller(self) -> ta.Optional[LoggingCaller]:
|
7190
|
+
try:
|
7191
|
+
return self._caller
|
7192
|
+
except AttributeError:
|
7193
|
+
raise CaptureLoggingContext.NotCapturedError from None
|
7194
|
+
|
7195
|
+
def source_file(self) -> ta.Optional[LoggingSourceFileInfo]:
|
7196
|
+
try:
|
7197
|
+
return self._source_file
|
7198
|
+
except AttributeError:
|
7199
|
+
raise CaptureLoggingContext.NotCapturedError from None
|
7200
|
+
|
7201
|
+
#
|
7202
|
+
|
7203
|
+
def thread(self) -> ta.Optional[LoggingThreadInfo]:
|
7204
|
+
try:
|
7205
|
+
return self._thread
|
7206
|
+
except AttributeError:
|
7207
|
+
raise CaptureLoggingContext.NotCapturedError from None
|
7208
|
+
|
7209
|
+
def process(self) -> ta.Optional[LoggingProcessInfo]:
|
7210
|
+
try:
|
7211
|
+
return self._process
|
7212
|
+
except AttributeError:
|
7213
|
+
raise CaptureLoggingContext.NotCapturedError from None
|
7214
|
+
|
7215
|
+
def multiprocessing(self) -> ta.Optional[LoggingMultiprocessingInfo]:
|
7216
|
+
try:
|
7217
|
+
return self._multiprocessing
|
7218
|
+
except AttributeError:
|
7219
|
+
raise CaptureLoggingContext.NotCapturedError from None
|
7220
|
+
|
7221
|
+
def asyncio_task(self) -> ta.Optional[LoggingAsyncioTaskInfo]:
|
7222
|
+
try:
|
7223
|
+
return self._asyncio_task
|
7224
|
+
except AttributeError:
|
7225
|
+
raise CaptureLoggingContext.NotCapturedError from None
|
7226
|
+
|
7227
|
+
|
6677
7228
|
########################################
|
6678
7229
|
# ../../../omlish/logs/standard.py
|
6679
7230
|
"""
|
@@ -7018,70 +7569,585 @@ InterpProviders = ta.NewType('InterpProviders', ta.Sequence[InterpProvider])
|
|
7018
7569
|
|
7019
7570
|
|
7020
7571
|
########################################
|
7021
|
-
# ../../../omlish/
|
7572
|
+
# ../../../omlish/logs/base.py
|
7022
7573
|
|
7023
7574
|
|
7024
7575
|
##
|
7025
7576
|
|
7026
7577
|
|
7027
|
-
|
7028
|
-
|
7029
|
-
|
7030
|
-
# - A file-like object
|
7031
|
-
# - None
|
7578
|
+
class AnyLogger(Abstract, ta.Generic[T]):
|
7579
|
+
def is_enabled_for(self, level: LogLevel) -> bool:
|
7580
|
+
return self.get_effective_level() >= level
|
7032
7581
|
|
7033
|
-
|
7034
|
-
|
7035
|
-
|
7036
|
-
'devnull': subprocess.DEVNULL,
|
7037
|
-
}
|
7582
|
+
@abc.abstractmethod
|
7583
|
+
def get_effective_level(self) -> LogLevel:
|
7584
|
+
raise NotImplementedError
|
7038
7585
|
|
7586
|
+
#
|
7039
7587
|
|
7040
|
-
|
7588
|
+
@ta.final
|
7589
|
+
def isEnabledFor(self, level: LogLevel) -> bool: # noqa
|
7590
|
+
return self.is_enabled_for(level)
|
7041
7591
|
|
7592
|
+
@ta.final
|
7593
|
+
def getEffectiveLevel(self) -> LogLevel: # noqa
|
7594
|
+
return self.get_effective_level()
|
7042
7595
|
|
7043
|
-
|
7044
|
-
@classmethod
|
7045
|
-
def from_std(cls, e: subprocess.CalledProcessError) -> 'VerboseCalledProcessError':
|
7046
|
-
return cls(
|
7047
|
-
e.returncode,
|
7048
|
-
e.cmd,
|
7049
|
-
output=e.output,
|
7050
|
-
stderr=e.stderr,
|
7051
|
-
)
|
7596
|
+
##
|
7052
7597
|
|
7053
|
-
|
7054
|
-
|
7055
|
-
|
7056
|
-
msg += f' Output: {self.output!r}'
|
7057
|
-
if self.stderr is not None:
|
7058
|
-
msg += f' Stderr: {self.stderr!r}'
|
7059
|
-
return msg
|
7598
|
+
@ta.overload
|
7599
|
+
def log(self, level: LogLevel, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
7600
|
+
...
|
7060
7601
|
|
7602
|
+
@ta.overload
|
7603
|
+
def log(self, level: LogLevel, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
|
7604
|
+
...
|
7061
7605
|
|
7062
|
-
|
7063
|
-
|
7606
|
+
@ta.overload
|
7607
|
+
def log(self, level: LogLevel, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
|
7608
|
+
...
|
7064
7609
|
|
7065
|
-
|
7066
|
-
|
7067
|
-
|
7068
|
-
log: ta.Optional[logging.Logger] = None,
|
7069
|
-
try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
|
7070
|
-
) -> None:
|
7071
|
-
super().__init__()
|
7610
|
+
@ta.final
|
7611
|
+
def log(self, level: LogLevel, *args, **kwargs):
|
7612
|
+
return self._log(CaptureLoggingContextImpl(level, stack_offset=1), *args, **kwargs)
|
7072
7613
|
|
7073
|
-
|
7074
|
-
self._try_exceptions = try_exceptions if try_exceptions is not None else self.DEFAULT_TRY_EXCEPTIONS
|
7614
|
+
#
|
7075
7615
|
|
7076
|
-
|
7077
|
-
|
7616
|
+
@ta.overload
|
7617
|
+
def debug(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
7618
|
+
...
|
7619
|
+
|
7620
|
+
@ta.overload
|
7621
|
+
def debug(self, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
|
7622
|
+
...
|
7623
|
+
|
7624
|
+
@ta.overload
|
7625
|
+
def debug(self, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
|
7626
|
+
...
|
7627
|
+
|
7628
|
+
@ta.final
|
7629
|
+
def debug(self, *args, **kwargs):
|
7630
|
+
return self._log(CaptureLoggingContextImpl(NamedLogLevel.DEBUG, stack_offset=1), *args, **kwargs)
|
7078
7631
|
|
7079
7632
|
#
|
7080
7633
|
|
7081
|
-
|
7082
|
-
|
7083
|
-
|
7084
|
-
|
7634
|
+
@ta.overload
|
7635
|
+
def info(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
7636
|
+
...
|
7637
|
+
|
7638
|
+
@ta.overload
|
7639
|
+
def info(self, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
|
7640
|
+
...
|
7641
|
+
|
7642
|
+
@ta.overload
|
7643
|
+
def info(self, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
|
7644
|
+
...
|
7645
|
+
|
7646
|
+
@ta.final
|
7647
|
+
def info(self, *args, **kwargs):
|
7648
|
+
return self._log(CaptureLoggingContextImpl(NamedLogLevel.INFO, stack_offset=1), *args, **kwargs)
|
7649
|
+
|
7650
|
+
#
|
7651
|
+
|
7652
|
+
@ta.overload
|
7653
|
+
def warning(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
7654
|
+
...
|
7655
|
+
|
7656
|
+
@ta.overload
|
7657
|
+
def warning(self, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
|
7658
|
+
...
|
7659
|
+
|
7660
|
+
@ta.overload
|
7661
|
+
def warning(self, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
|
7662
|
+
...
|
7663
|
+
|
7664
|
+
@ta.final
|
7665
|
+
def warning(self, *args, **kwargs):
|
7666
|
+
return self._log(CaptureLoggingContextImpl(NamedLogLevel.WARNING, stack_offset=1), *args, **kwargs)
|
7667
|
+
|
7668
|
+
#
|
7669
|
+
|
7670
|
+
@ta.overload
|
7671
|
+
def error(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
7672
|
+
...
|
7673
|
+
|
7674
|
+
@ta.overload
|
7675
|
+
def error(self, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
|
7676
|
+
...
|
7677
|
+
|
7678
|
+
@ta.overload
|
7679
|
+
def error(self, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
|
7680
|
+
...
|
7681
|
+
|
7682
|
+
@ta.final
|
7683
|
+
def error(self, *args, **kwargs):
|
7684
|
+
return self._log(CaptureLoggingContextImpl(NamedLogLevel.ERROR, stack_offset=1), *args, **kwargs)
|
7685
|
+
|
7686
|
+
#
|
7687
|
+
|
7688
|
+
@ta.overload
|
7689
|
+
def exception(self, msg: str, *args: ta.Any, exc_info: LoggingExcInfoArg = True, **kwargs: ta.Any) -> T:
|
7690
|
+
...
|
7691
|
+
|
7692
|
+
@ta.overload
|
7693
|
+
def exception(self, msg: ta.Tuple[ta.Any, ...], *, exc_info: LoggingExcInfoArg = True, **kwargs: ta.Any) -> T:
|
7694
|
+
...
|
7695
|
+
|
7696
|
+
@ta.overload
|
7697
|
+
def exception(self, msg_fn: LoggingMsgFn, *, exc_info: LoggingExcInfoArg = True, **kwargs: ta.Any) -> T:
|
7698
|
+
...
|
7699
|
+
|
7700
|
+
@ta.final
|
7701
|
+
def exception(self, *args, exc_info: LoggingExcInfoArg = True, **kwargs):
|
7702
|
+
return self._log(CaptureLoggingContextImpl(NamedLogLevel.ERROR, exc_info=exc_info, stack_offset=1), *args, **kwargs) # noqa
|
7703
|
+
|
7704
|
+
#
|
7705
|
+
|
7706
|
+
@ta.overload
|
7707
|
+
def critical(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
7708
|
+
...
|
7709
|
+
|
7710
|
+
@ta.overload
|
7711
|
+
def critical(self, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
|
7712
|
+
...
|
7713
|
+
|
7714
|
+
@ta.overload
|
7715
|
+
def critical(self, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
|
7716
|
+
...
|
7717
|
+
|
7718
|
+
@ta.final
|
7719
|
+
def critical(self, *args, **kwargs):
|
7720
|
+
return self._log(CaptureLoggingContextImpl(NamedLogLevel.CRITICAL, stack_offset=1), *args, **kwargs)
|
7721
|
+
|
7722
|
+
##
|
7723
|
+
|
7724
|
+
@classmethod
|
7725
|
+
def _prepare_msg_args(cls, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> ta.Tuple[str, tuple]:
|
7726
|
+
if callable(msg):
|
7727
|
+
if args:
|
7728
|
+
raise TypeError(f'Must not provide both a message function and args: {msg=} {args=}')
|
7729
|
+
x = msg()
|
7730
|
+
if isinstance(x, str):
|
7731
|
+
return x, ()
|
7732
|
+
elif isinstance(x, tuple):
|
7733
|
+
if x:
|
7734
|
+
return x[0], x[1:]
|
7735
|
+
else:
|
7736
|
+
return '', ()
|
7737
|
+
else:
|
7738
|
+
raise TypeError(x)
|
7739
|
+
|
7740
|
+
elif isinstance(msg, tuple):
|
7741
|
+
if args:
|
7742
|
+
raise TypeError(f'Must not provide both a tuple message and args: {msg=} {args=}')
|
7743
|
+
if msg:
|
7744
|
+
return msg[0], msg[1:]
|
7745
|
+
else:
|
7746
|
+
return '', ()
|
7747
|
+
|
7748
|
+
elif isinstance(msg, str):
|
7749
|
+
return msg, args
|
7750
|
+
|
7751
|
+
else:
|
7752
|
+
raise TypeError(msg)
|
7753
|
+
|
7754
|
+
@abc.abstractmethod
|
7755
|
+
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> T: # noqa
|
7756
|
+
raise NotImplementedError
|
7757
|
+
|
7758
|
+
|
7759
|
+
class Logger(AnyLogger[None], Abstract):
|
7760
|
+
@abc.abstractmethod
|
7761
|
+
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
|
7762
|
+
raise NotImplementedError
|
7763
|
+
|
7764
|
+
|
7765
|
+
class AsyncLogger(AnyLogger[ta.Awaitable[None]], Abstract):
|
7766
|
+
@abc.abstractmethod
|
7767
|
+
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> ta.Awaitable[None]: # noqa
|
7768
|
+
raise NotImplementedError
|
7769
|
+
|
7770
|
+
|
7771
|
+
##
|
7772
|
+
|
7773
|
+
|
7774
|
+
class AnyNopLogger(AnyLogger[T], Abstract):
|
7775
|
+
@ta.final
|
7776
|
+
def get_effective_level(self) -> LogLevel:
|
7777
|
+
return 999
|
7778
|
+
|
7779
|
+
|
7780
|
+
@ta.final
|
7781
|
+
class NopLogger(AnyNopLogger[None], Logger):
|
7782
|
+
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
|
7783
|
+
pass
|
7784
|
+
|
7785
|
+
|
7786
|
+
@ta.final
|
7787
|
+
class AsyncNopLogger(AnyNopLogger[ta.Awaitable[None]], AsyncLogger):
|
7788
|
+
async def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
|
7789
|
+
pass
|
7790
|
+
|
7791
|
+
|
7792
|
+
########################################
|
7793
|
+
# ../../../omlish/logs/std/records.py
|
7794
|
+
|
7795
|
+
|
7796
|
+
##
|
7797
|
+
|
7798
|
+
|
7799
|
+
# Ref:
|
7800
|
+
# - https://docs.python.org/3/library/logging.html#logrecord-attributes
|
7801
|
+
#
|
7802
|
+
# LogRecord:
|
7803
|
+
# - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L276 (3.8)
|
7804
|
+
# - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L286 (~3.14) # noqa
|
7805
|
+
#
|
7806
|
+
# LogRecord.__init__ args:
|
7807
|
+
# - name: str
|
7808
|
+
# - level: int
|
7809
|
+
# - pathname: str - Confusingly referred to as `fn` before the LogRecord ctor. May be empty or "(unknown file)".
|
7810
|
+
# - lineno: int - May be 0.
|
7811
|
+
# - msg: str
|
7812
|
+
# - args: tuple | dict | 1-tuple[dict]
|
7813
|
+
# - exc_info: LoggingExcInfoTuple | None
|
7814
|
+
# - func: str | None = None -> funcName
|
7815
|
+
# - sinfo: str | None = None -> stack_info
|
7816
|
+
#
|
7817
|
+
KNOWN_STD_LOGGING_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
|
7818
|
+
# Name of the logger used to log the call. Unmodified by ctor.
|
7819
|
+
name=str,
|
7820
|
+
|
7821
|
+
# The format string passed in the original logging call. Merged with args to produce message, or an arbitrary object
|
7822
|
+
# (see Using arbitrary objects as messages). Unmodified by ctor.
|
7823
|
+
msg=str,
|
7824
|
+
|
7825
|
+
# The tuple of arguments merged into msg to produce message, or a dict whose values are used for the merge (when
|
7826
|
+
# there is only one argument, and it is a dictionary). Ctor will transform a 1-tuple containing a Mapping into just
|
7827
|
+
# the mapping, but is otherwise unmodified.
|
7828
|
+
args=ta.Union[tuple, dict],
|
7829
|
+
|
7830
|
+
#
|
7831
|
+
|
7832
|
+
# Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). Set to
|
7833
|
+
# `getLevelName(level)`.
|
7834
|
+
levelname=str,
|
7835
|
+
|
7836
|
+
# Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL). Unmodified by ctor.
|
7837
|
+
levelno=int,
|
7838
|
+
|
7839
|
+
#
|
7840
|
+
|
7841
|
+
# Full pathname of the source file where the logging call was issued (if available). Unmodified by ctor. May default
|
7842
|
+
# to "(unknown file)" by Logger.findCaller / Logger._log.
|
7843
|
+
pathname=str,
|
7844
|
+
|
7845
|
+
# Filename portion of pathname. Set to `os.path.basename(pathname)` if successful, otherwise defaults to pathname.
|
7846
|
+
filename=str,
|
7847
|
+
|
7848
|
+
# Module (name portion of filename). Set to `os.path.splitext(filename)[0]`, otherwise defaults to
|
7849
|
+
# "Unknown module".
|
7850
|
+
module=str,
|
7851
|
+
|
7852
|
+
#
|
7853
|
+
|
7854
|
+
# Exception tuple (à la sys.exc_info) or, if no exception has occurred, None. Unmodified by ctor.
|
7855
|
+
exc_info=ta.Optional[LoggingExcInfoTuple],
|
7856
|
+
|
7857
|
+
# Used to cache the traceback text. Simply set to None by ctor, later set by Formatter.format.
|
7858
|
+
exc_text=ta.Optional[str],
|
7859
|
+
|
7860
|
+
#
|
7861
|
+
|
7862
|
+
# Stack frame information (where available) from the bottom of the stack in the current thread, up to and including
|
7863
|
+
# the stack frame of the logging call which resulted in the creation of this record. Set by ctor to `sinfo` arg,
|
7864
|
+
# unmodified. Mostly set, if requested, by `Logger.findCaller`, to `traceback.print_stack(f)`, but prepended with
|
7865
|
+
# the literal "Stack (most recent call last):\n", and stripped of exactly one trailing `\n` if present.
|
7866
|
+
stack_info=ta.Optional[str],
|
7867
|
+
|
7868
|
+
# Source line number where the logging call was issued (if available). Unmodified by ctor. May default to 0 by
|
7869
|
+
# Logger.findCaller / Logger._log.
|
7870
|
+
lineno=int,
|
7871
|
+
|
7872
|
+
# Name of function containing the logging call. Set by ctor to `func` arg, unmodified. May default to
|
7873
|
+
# "(unknown function)" by Logger.findCaller / Logger._log.
|
7874
|
+
funcName=str,
|
7875
|
+
|
7876
|
+
#
|
7877
|
+
|
7878
|
+
# Time when the LogRecord was created. Set to `time.time_ns() / 1e9` for >=3.13.0b1, otherwise simply `time.time()`.
|
7879
|
+
#
|
7880
|
+
# See:
|
7881
|
+
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
7882
|
+
# - https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
7883
|
+
#
|
7884
|
+
created=float,
|
7885
|
+
|
7886
|
+
# Millisecond portion of the time when the LogRecord was created.
|
7887
|
+
msecs=float,
|
7888
|
+
|
7889
|
+
# Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
|
7890
|
+
relativeCreated=float,
|
7891
|
+
|
7892
|
+
#
|
7893
|
+
|
7894
|
+
# Thread ID if available, and `logging.logThreads` is truthy.
|
7895
|
+
thread=ta.Optional[int],
|
7896
|
+
|
7897
|
+
# Thread name if available, and `logging.logThreads` is truthy.
|
7898
|
+
threadName=ta.Optional[str],
|
7899
|
+
|
7900
|
+
#
|
7901
|
+
|
7902
|
+
# Process name if available. Set to None if `logging.logMultiprocessing` is not truthy. Otherwise, set to
|
7903
|
+
# 'MainProcess', then `sys.modules.get('multiprocessing').current_process().name` if that works, otherwise remains
|
7904
|
+
# as 'MainProcess'.
|
7905
|
+
#
|
7906
|
+
# As noted by stdlib:
|
7907
|
+
#
|
7908
|
+
# Errors may occur if multiprocessing has not finished loading yet - e.g. if a custom import hook causes
|
7909
|
+
# third-party code to run when multiprocessing calls import. See issue 8200 for an example
|
7910
|
+
#
|
7911
|
+
processName=ta.Optional[str],
|
7912
|
+
|
7913
|
+
# Process ID if available - that is, if `hasattr(os, 'getpid')` - and `logging.logProcesses` is truthy, otherwise
|
7914
|
+
# None.
|
7915
|
+
process=ta.Optional[int],
|
7916
|
+
|
7917
|
+
#
|
7918
|
+
|
7919
|
+
# Absent <3.12, otherwise asyncio.Task name if available, and `logging.logAsyncioTasks` is truthy. Set to
|
7920
|
+
# `sys.modules.get('asyncio').current_task().get_name()`, otherwise None.
|
7921
|
+
taskName=ta.Optional[str],
|
7922
|
+
)
|
7923
|
+
|
7924
|
+
KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(KNOWN_STD_LOGGING_RECORD_ATTRS)
|
7925
|
+
|
7926
|
+
|
7927
|
+
# Formatter:
|
7928
|
+
# - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L514 (3.8)
|
7929
|
+
# - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L554 (~3.14) # noqa
|
7930
|
+
#
|
7931
|
+
KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
|
7932
|
+
# The logged message, computed as msg % args. Set to `record.getMessage()`.
|
7933
|
+
message=str,
|
7934
|
+
|
7935
|
+
# Human-readable time when the LogRecord was created. By default this is of the form '2003-07-08 16:49:45,896' (the
|
7936
|
+
# numbers after the comma are millisecond portion of the time). Set to `self.formatTime(record, self.datefmt)` if
|
7937
|
+
# `self.usesTime()`, otherwise unset.
|
7938
|
+
asctime=str,
|
7939
|
+
|
7940
|
+
# Used to cache the traceback text. If unset (falsey) on the record and `exc_info` is truthy, set to
|
7941
|
+
# `self.formatException(record.exc_info)` - otherwise unmodified.
|
7942
|
+
exc_text=ta.Optional[str],
|
7943
|
+
)
|
7944
|
+
|
7945
|
+
KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
|
7946
|
+
|
7947
|
+
|
7948
|
+
##
|
7949
|
+
|
7950
|
+
|
7951
|
+
class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
|
7952
|
+
pass
|
7953
|
+
|
7954
|
+
|
7955
|
+
def _check_std_logging_record_attrs() -> None:
|
7956
|
+
rec_dct = dict(logging.makeLogRecord({}).__dict__)
|
7957
|
+
|
7958
|
+
if (unk_rec_fields := frozenset(rec_dct) - KNOWN_STD_LOGGING_RECORD_ATTR_SET):
|
7959
|
+
import warnings # noqa
|
7960
|
+
|
7961
|
+
warnings.warn(
|
7962
|
+
f'Unknown log record attrs detected: {sorted(unk_rec_fields)!r}',
|
7963
|
+
UnknownStdLoggingRecordAttrsWarning,
|
7964
|
+
)
|
7965
|
+
|
7966
|
+
|
7967
|
+
_check_std_logging_record_attrs()
|
7968
|
+
|
7969
|
+
|
7970
|
+
##
|
7971
|
+
|
7972
|
+
|
7973
|
+
class LoggingContextLogRecord(logging.LogRecord):
|
7974
|
+
_SHOULD_ADD_TASK_NAME: ta.ClassVar[bool] = sys.version_info >= (3, 12)
|
7975
|
+
|
7976
|
+
_UNKNOWN_PATH_NAME: ta.ClassVar[str] = '(unknown file)'
|
7977
|
+
_UNKNOWN_FUNC_NAME: ta.ClassVar[str] = '(unknown function)'
|
7978
|
+
_UNKNOWN_MODULE: ta.ClassVar[str] = 'Unknown module'
|
7979
|
+
|
7980
|
+
_STACK_INFO_PREFIX: ta.ClassVar[str] = 'Stack (most recent call last):\n'
|
7981
|
+
|
7982
|
+
def __init__( # noqa
|
7983
|
+
self,
|
7984
|
+
# name,
|
7985
|
+
# level,
|
7986
|
+
# pathname,
|
7987
|
+
# lineno,
|
7988
|
+
# msg,
|
7989
|
+
# args,
|
7990
|
+
# exc_info,
|
7991
|
+
# func=None,
|
7992
|
+
# sinfo=None,
|
7993
|
+
# **kwargs,
|
7994
|
+
*,
|
7995
|
+
name: str,
|
7996
|
+
msg: str,
|
7997
|
+
args: ta.Union[tuple, dict],
|
7998
|
+
|
7999
|
+
_logging_context: LoggingContext,
|
8000
|
+
) -> None:
|
8001
|
+
ctx = _logging_context
|
8002
|
+
|
8003
|
+
self.name: str = name
|
8004
|
+
|
8005
|
+
self.msg: str = msg
|
8006
|
+
|
8007
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L307
|
8008
|
+
if args and len(args) == 1 and isinstance(args[0], collections.abc.Mapping) and args[0]:
|
8009
|
+
args = args[0] # type: ignore[assignment]
|
8010
|
+
self.args: ta.Union[tuple, dict] = args
|
8011
|
+
|
8012
|
+
self.levelname: str = logging.getLevelName(ctx.level)
|
8013
|
+
self.levelno: int = ctx.level
|
8014
|
+
|
8015
|
+
if (caller := ctx.caller()) is not None:
|
8016
|
+
self.pathname: str = caller.file_path
|
8017
|
+
else:
|
8018
|
+
self.pathname = self._UNKNOWN_PATH_NAME
|
8019
|
+
|
8020
|
+
if (src_file := ctx.source_file()) is not None:
|
8021
|
+
self.filename: str = src_file.file_name
|
8022
|
+
self.module: str = src_file.module
|
8023
|
+
else:
|
8024
|
+
self.filename = self.pathname
|
8025
|
+
self.module = self._UNKNOWN_MODULE
|
8026
|
+
|
8027
|
+
self.exc_info: ta.Optional[LoggingExcInfoTuple] = ctx.exc_info_tuple
|
8028
|
+
self.exc_text: ta.Optional[str] = None
|
8029
|
+
|
8030
|
+
# If ctx.build_caller() was never called, we simply don't have a stack trace.
|
8031
|
+
if caller is not None:
|
8032
|
+
if (sinfo := caller.stack_info) is not None:
|
8033
|
+
self.stack_info: ta.Optional[str] = '\n'.join([
|
8034
|
+
self._STACK_INFO_PREFIX,
|
8035
|
+
sinfo[1:] if sinfo.endswith('\n') else sinfo,
|
8036
|
+
])
|
8037
|
+
else:
|
8038
|
+
self.stack_info = None
|
8039
|
+
|
8040
|
+
self.lineno: int = caller.line_no
|
8041
|
+
self.funcName: str = caller.name
|
8042
|
+
|
8043
|
+
else:
|
8044
|
+
self.stack_info = None
|
8045
|
+
|
8046
|
+
self.lineno = 0
|
8047
|
+
self.funcName = self._UNKNOWN_FUNC_NAME
|
8048
|
+
|
8049
|
+
times = ctx.times
|
8050
|
+
self.created: float = times.created
|
8051
|
+
self.msecs: float = times.msecs
|
8052
|
+
self.relativeCreated: float = times.relative_created
|
8053
|
+
|
8054
|
+
if logging.logThreads:
|
8055
|
+
thread = check.not_none(ctx.thread())
|
8056
|
+
self.thread: ta.Optional[int] = thread.ident
|
8057
|
+
self.threadName: ta.Optional[str] = thread.name
|
8058
|
+
else:
|
8059
|
+
self.thread = None
|
8060
|
+
self.threadName = None
|
8061
|
+
|
8062
|
+
if logging.logProcesses:
|
8063
|
+
process = check.not_none(ctx.process())
|
8064
|
+
self.process: ta.Optional[int] = process.pid
|
8065
|
+
else:
|
8066
|
+
self.process = None
|
8067
|
+
|
8068
|
+
if logging.logMultiprocessing:
|
8069
|
+
if (mp := ctx.multiprocessing()) is not None:
|
8070
|
+
self.processName: ta.Optional[str] = mp.process_name
|
8071
|
+
else:
|
8072
|
+
self.processName = None
|
8073
|
+
else:
|
8074
|
+
self.processName = None
|
8075
|
+
|
8076
|
+
# Absent <3.12
|
8077
|
+
if getattr(logging, 'logAsyncioTasks', None):
|
8078
|
+
if (at := ctx.asyncio_task()) is not None:
|
8079
|
+
self.taskName: ta.Optional[str] = at.name
|
8080
|
+
else:
|
8081
|
+
self.taskName = None
|
8082
|
+
else:
|
8083
|
+
self.taskName = None
|
8084
|
+
|
8085
|
+
|
8086
|
+
########################################
|
8087
|
+
# ../../../omlish/subprocesses/base.py
|
8088
|
+
|
8089
|
+
|
8090
|
+
##
|
8091
|
+
|
8092
|
+
|
8093
|
+
# Valid channel type kwarg values:
|
8094
|
+
# - A special flag negative int
|
8095
|
+
# - A positive fd int
|
8096
|
+
# - A file-like object
|
8097
|
+
# - None
|
8098
|
+
|
8099
|
+
SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
|
8100
|
+
'pipe': subprocess.PIPE,
|
8101
|
+
'stdout': subprocess.STDOUT,
|
8102
|
+
'devnull': subprocess.DEVNULL,
|
8103
|
+
}
|
8104
|
+
|
8105
|
+
|
8106
|
+
##
|
8107
|
+
|
8108
|
+
|
8109
|
+
class VerboseCalledProcessError(subprocess.CalledProcessError):
|
8110
|
+
@classmethod
|
8111
|
+
def from_std(cls, e: subprocess.CalledProcessError) -> 'VerboseCalledProcessError':
|
8112
|
+
return cls(
|
8113
|
+
e.returncode,
|
8114
|
+
e.cmd,
|
8115
|
+
output=e.output,
|
8116
|
+
stderr=e.stderr,
|
8117
|
+
)
|
8118
|
+
|
8119
|
+
def __str__(self) -> str:
|
8120
|
+
msg = super().__str__()
|
8121
|
+
if self.output is not None:
|
8122
|
+
msg += f' Output: {self.output!r}'
|
8123
|
+
if self.stderr is not None:
|
8124
|
+
msg += f' Stderr: {self.stderr!r}'
|
8125
|
+
return msg
|
8126
|
+
|
8127
|
+
|
8128
|
+
class BaseSubprocesses(Abstract):
|
8129
|
+
DEFAULT_LOGGER: ta.ClassVar[ta.Optional[LoggerLike]] = None
|
8130
|
+
|
8131
|
+
def __init__(
|
8132
|
+
self,
|
8133
|
+
*,
|
8134
|
+
log: ta.Optional[LoggerLike] = None,
|
8135
|
+
try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
|
8136
|
+
) -> None:
|
8137
|
+
super().__init__()
|
8138
|
+
|
8139
|
+
self._log = log if log is not None else self.DEFAULT_LOGGER
|
8140
|
+
self._try_exceptions = try_exceptions if try_exceptions is not None else self.DEFAULT_TRY_EXCEPTIONS
|
8141
|
+
|
8142
|
+
def set_logger(self, log: ta.Optional[LoggerLike]) -> None:
|
8143
|
+
self._log = log
|
8144
|
+
|
8145
|
+
#
|
8146
|
+
|
8147
|
+
def prepare_args(
|
8148
|
+
self,
|
8149
|
+
*cmd: str,
|
8150
|
+
env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
7085
8151
|
extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
7086
8152
|
quiet: bool = False,
|
7087
8153
|
shell: bool = False,
|
@@ -7317,6 +8383,45 @@ class InterpResolver:
|
|
7317
8383
|
print(f' {si}')
|
7318
8384
|
|
7319
8385
|
|
8386
|
+
########################################
|
8387
|
+
# ../../../omlish/logs/std/adapters.py
|
8388
|
+
|
8389
|
+
|
8390
|
+
##
|
8391
|
+
|
8392
|
+
|
8393
|
+
class StdLogger(Logger):
|
8394
|
+
def __init__(self, std: logging.Logger) -> None:
|
8395
|
+
super().__init__()
|
8396
|
+
|
8397
|
+
self._std = std
|
8398
|
+
|
8399
|
+
@property
|
8400
|
+
def std(self) -> logging.Logger:
|
8401
|
+
return self._std
|
8402
|
+
|
8403
|
+
def get_effective_level(self) -> LogLevel:
|
8404
|
+
return self._std.getEffectiveLevel()
|
8405
|
+
|
8406
|
+
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> None:
|
8407
|
+
if not self.is_enabled_for(ctx.level):
|
8408
|
+
return
|
8409
|
+
|
8410
|
+
ctx.capture()
|
8411
|
+
|
8412
|
+
ms, args = self._prepare_msg_args(msg, *args)
|
8413
|
+
|
8414
|
+
rec = LoggingContextLogRecord(
|
8415
|
+
name=self._std.name,
|
8416
|
+
msg=ms,
|
8417
|
+
args=args,
|
8418
|
+
|
8419
|
+
_logging_context=ctx,
|
8420
|
+
)
|
8421
|
+
|
8422
|
+
self._std.handle(rec)
|
8423
|
+
|
8424
|
+
|
7320
8425
|
########################################
|
7321
8426
|
# ../../../omlish/subprocesses/asyncs.py
|
7322
8427
|
|
@@ -7628,7 +8733,7 @@ class AsyncioProcessCommunicator:
|
|
7628
8733
|
proc: asyncio.subprocess.Process,
|
7629
8734
|
loop: ta.Optional[ta.Any] = None,
|
7630
8735
|
*,
|
7631
|
-
log: ta.Optional[
|
8736
|
+
log: ta.Optional[LoggerLike] = None,
|
7632
8737
|
) -> None:
|
7633
8738
|
super().__init__()
|
7634
8739
|
|
@@ -7811,6 +8916,17 @@ class AsyncioSubprocesses(AbstractAsyncSubprocesses):
|
|
7811
8916
|
asyncio_subprocesses = AsyncioSubprocesses()
|
7812
8917
|
|
7813
8918
|
|
8919
|
+
########################################
|
8920
|
+
# ../../../omlish/logs/modules.py
|
8921
|
+
|
8922
|
+
|
8923
|
+
##
|
8924
|
+
|
8925
|
+
|
8926
|
+
def get_module_logger(mod_globals: ta.Mapping[str, ta.Any]) -> Logger:
|
8927
|
+
return StdLogger(logging.getLogger(mod_globals.get('__name__'))) # noqa
|
8928
|
+
|
8929
|
+
|
7814
8930
|
########################################
|
7815
8931
|
# ../../interp/inspect.py
|
7816
8932
|
|
@@ -7851,7 +8967,7 @@ class InterpInspector:
|
|
7851
8967
|
def __init__(
|
7852
8968
|
self,
|
7853
8969
|
*,
|
7854
|
-
log: ta.Optional[
|
8970
|
+
log: ta.Optional[LoggerLike] = None,
|
7855
8971
|
) -> None:
|
7856
8972
|
super().__init__()
|
7857
8973
|
|
@@ -8015,7 +9131,7 @@ class Uv:
|
|
8015
9131
|
self,
|
8016
9132
|
config: UvConfig = UvConfig(),
|
8017
9133
|
*,
|
8018
|
-
log: ta.Optional[
|
9134
|
+
log: ta.Optional[LoggerLike] = None,
|
8019
9135
|
) -> None:
|
8020
9136
|
super().__init__()
|
8021
9137
|
|
@@ -8182,6 +9298,90 @@ class GitRevisionAdder:
|
|
8182
9298
|
#
|
8183
9299
|
|
8184
9300
|
|
9301
|
+
########################################
|
9302
|
+
# ../reqs.py
|
9303
|
+
"""
|
9304
|
+
TODO:
|
9305
|
+
- embed pip._internal.req.parse_requirements, add additional env stuff? breaks compat with raw pip
|
9306
|
+
"""
|
9307
|
+
|
9308
|
+
|
9309
|
+
log = get_module_logger(globals()) # noqa
|
9310
|
+
|
9311
|
+
|
9312
|
+
##
|
9313
|
+
|
9314
|
+
|
9315
|
+
class RequirementsRewriter:
|
9316
|
+
def __init__(
|
9317
|
+
self,
|
9318
|
+
venv: ta.Optional[str] = None,
|
9319
|
+
) -> None:
|
9320
|
+
super().__init__()
|
9321
|
+
|
9322
|
+
self._venv = venv
|
9323
|
+
|
9324
|
+
@cached_nullary
|
9325
|
+
def _tmp_dir(self) -> str:
|
9326
|
+
return tempfile.mkdtemp('-omlish-reqs')
|
9327
|
+
|
9328
|
+
VENV_MAGIC = '# @omlish-venv'
|
9329
|
+
|
9330
|
+
def rewrite_file(self, in_file: str) -> str:
|
9331
|
+
with open(in_file) as f:
|
9332
|
+
src = f.read()
|
9333
|
+
|
9334
|
+
in_lines = src.splitlines(keepends=True)
|
9335
|
+
out_lines = []
|
9336
|
+
|
9337
|
+
for l in in_lines:
|
9338
|
+
if self.VENV_MAGIC in l:
|
9339
|
+
lp, _, rp = l.partition(self.VENV_MAGIC)
|
9340
|
+
rp = rp.partition('#')[0]
|
9341
|
+
omit = False
|
9342
|
+
for v in rp.split():
|
9343
|
+
if v[0] == '!':
|
9344
|
+
if self._venv is not None and self._venv == v[1:]:
|
9345
|
+
omit = True
|
9346
|
+
break
|
9347
|
+
else:
|
9348
|
+
raise NotImplementedError
|
9349
|
+
|
9350
|
+
if omit:
|
9351
|
+
out_lines.append('# OMITTED: ' + l)
|
9352
|
+
continue
|
9353
|
+
|
9354
|
+
out_req = self.rewrite(l.rstrip('\n'), for_file=True)
|
9355
|
+
out_lines.append(out_req + '\n')
|
9356
|
+
|
9357
|
+
out_file = os.path.join(self._tmp_dir(), os.path.basename(in_file))
|
9358
|
+
if os.path.exists(out_file):
|
9359
|
+
raise Exception(f'file exists: {out_file}')
|
9360
|
+
|
9361
|
+
with open(out_file, 'w') as f:
|
9362
|
+
f.write(''.join(out_lines))
|
9363
|
+
log.info('Rewrote requirements file %s to %s', in_file, out_file)
|
9364
|
+
return out_file
|
9365
|
+
|
9366
|
+
def rewrite(self, in_req: str, *, for_file: bool = False) -> str:
|
9367
|
+
if in_req.strip().startswith('-r'):
|
9368
|
+
l = in_req.strip()
|
9369
|
+
lp, _, rp = l.partition(' ')
|
9370
|
+
if lp == '-r':
|
9371
|
+
inc_in_file, _, rest = rp.partition(' ')
|
9372
|
+
else:
|
9373
|
+
inc_in_file, rest = lp[2:], rp
|
9374
|
+
|
9375
|
+
inc_out_file = self.rewrite_file(inc_in_file)
|
9376
|
+
if for_file:
|
9377
|
+
return ' '.join(['-r ', inc_out_file, rest])
|
9378
|
+
else:
|
9379
|
+
return '-r' + inc_out_file
|
9380
|
+
|
9381
|
+
else:
|
9382
|
+
return in_req
|
9383
|
+
|
9384
|
+
|
8185
9385
|
########################################
|
8186
9386
|
# ../../interp/providers/running.py
|
8187
9387
|
|
@@ -8231,7 +9431,7 @@ class SystemInterpProvider(InterpProvider):
|
|
8231
9431
|
options: Options = Options(),
|
8232
9432
|
*,
|
8233
9433
|
inspector: ta.Optional[InterpInspector] = None,
|
8234
|
-
log: ta.Optional[
|
9434
|
+
log: ta.Optional[LoggerLike] = None,
|
8235
9435
|
) -> None:
|
8236
9436
|
super().__init__()
|
8237
9437
|
|
@@ -8614,7 +9814,7 @@ class UvInterpProvider(InterpProvider):
|
|
8614
9814
|
*,
|
8615
9815
|
pyenv: Uv,
|
8616
9816
|
inspector: InterpInspector,
|
8617
|
-
log: ta.Optional[
|
9817
|
+
log: ta.Optional[LoggerLike] = None,
|
8618
9818
|
) -> None:
|
8619
9819
|
super().__init__()
|
8620
9820
|
|
@@ -9233,7 +10433,7 @@ class PyenvInterpProvider(InterpProvider):
|
|
9233
10433
|
*,
|
9234
10434
|
pyenv: Pyenv,
|
9235
10435
|
inspector: InterpInspector,
|
9236
|
-
log: ta.Optional[
|
10436
|
+
log: ta.Optional[LoggerLike] = None,
|
9237
10437
|
) -> None:
|
9238
10438
|
super().__init__()
|
9239
10439
|
|
@@ -9465,7 +10665,7 @@ class InterpVenv:
|
|
9465
10665
|
cfg: InterpVenvConfig,
|
9466
10666
|
*,
|
9467
10667
|
requirements_processor: ta.Optional[InterpVenvRequirementsProcessor] = None,
|
9468
|
-
log: ta.Optional[
|
10668
|
+
log: ta.Optional[LoggerLike] = None,
|
9469
10669
|
) -> None:
|
9470
10670
|
super().__init__()
|
9471
10671
|
|