omdev 0.0.0.dev419__py3-none-any.whl → 0.0.0.dev421__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/cache/compute/storage.py +3 -1
- omdev/ci/cache.py +3 -2
- omdev/ci/docker/buildcaching.py +3 -1
- omdev/ci/docker/cache.py +2 -1
- omdev/ci/docker/cacheserved/manifests.py +2 -2
- omdev/ci/docker/imagepulling.py +2 -1
- omdev/ci/docker/repositories.py +2 -1
- omdev/ci/github/api/clients.py +4 -3
- omdev/clipboard/clipboard.py +1 -1
- omdev/cmake.py +2 -1
- omdev/dataserver/handlers.py +3 -2
- omdev/dataserver/targets.py +2 -2
- omdev/interp/providers/base.py +3 -2
- omdev/interp/pyenv/install.py +2 -1
- omdev/manifests/_dumping.py +427 -127
- omdev/oci/data.py +2 -2
- omdev/oci/datarefs.py +2 -2
- omdev/oci/media.py +2 -2
- omdev/oci/repositories.py +3 -2
- omdev/packaging/specifiers.py +37 -39
- omdev/packaging/wheelfile.py +2 -2
- omdev/pyproject/pkg.py +2 -1
- omdev/scripts/ci.py +3969 -3667
- omdev/scripts/interp.py +1036 -934
- omdev/scripts/pyproject.py +2420 -2123
- omdev/tools/git/messages.py +2 -2
- {omdev-0.0.0.dev419.dist-info → omdev-0.0.0.dev421.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev419.dist-info → omdev-0.0.0.dev421.dist-info}/RECORD +32 -32
- {omdev-0.0.0.dev419.dist-info → omdev-0.0.0.dev421.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev419.dist-info → omdev-0.0.0.dev421.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev419.dist-info → omdev-0.0.0.dev421.dist-info}/licenses/LICENSE +0 -0
- {omdev-0.0.0.dev419.dist-info → omdev-0.0.0.dev421.dist-info}/top_level.txt +0 -0
omdev/scripts/interp.py
CHANGED
@@ -72,12 +72,6 @@ CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
|
|
72
72
|
CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
|
73
73
|
CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
|
74
74
|
|
75
|
-
# ../../omlish/lite/maybes.py
|
76
|
-
U = ta.TypeVar('U')
|
77
|
-
|
78
|
-
# ../../omlish/lite/timeouts.py
|
79
|
-
TimeoutLike = ta.Union['Timeout', ta.Type['Timeout.DEFAULT'], ta.Iterable['TimeoutLike'], float, None] # ta.TypeAlias
|
80
|
-
|
81
75
|
# ../packaging/specifiers.py
|
82
76
|
UnparsedVersion = ta.Union['Version', str]
|
83
77
|
UnparsedVersionVar = ta.TypeVar('UnparsedVersionVar', bound=UnparsedVersion)
|
@@ -86,6 +80,12 @@ CallableVersionOperator = ta.Callable[['Version', str], bool]
|
|
86
80
|
# ../../omlish/argparse/cli.py
|
87
81
|
ArgparseCmdFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
|
88
82
|
|
83
|
+
# ../../omlish/lite/maybes.py
|
84
|
+
U = ta.TypeVar('U')
|
85
|
+
|
86
|
+
# ../../omlish/lite/timeouts.py
|
87
|
+
TimeoutLike = ta.Union['Timeout', ta.Type['Timeout.DEFAULT'], ta.Iterable['TimeoutLike'], float, None] # ta.TypeAlias
|
88
|
+
|
89
89
|
# ../../omlish/asyncs/asyncio/timeouts.py
|
90
90
|
AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
|
91
91
|
|
@@ -506,6 +506,126 @@ def canonicalize_version(
|
|
506
506
|
return ''.join(parts)
|
507
507
|
|
508
508
|
|
509
|
+
########################################
|
510
|
+
# ../../../omlish/lite/abstract.py
|
511
|
+
|
512
|
+
|
513
|
+
##
|
514
|
+
|
515
|
+
|
516
|
+
_ABSTRACT_METHODS_ATTR = '__abstractmethods__'
|
517
|
+
_IS_ABSTRACT_METHOD_ATTR = '__isabstractmethod__'
|
518
|
+
|
519
|
+
|
520
|
+
def is_abstract_method(obj: ta.Any) -> bool:
|
521
|
+
return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
|
522
|
+
|
523
|
+
|
524
|
+
def update_abstracts(cls, *, force=False):
|
525
|
+
if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
|
526
|
+
# Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
|
527
|
+
# implementation (especially during testing), and we want to handle both cases.
|
528
|
+
return cls
|
529
|
+
|
530
|
+
abstracts: ta.Set[str] = set()
|
531
|
+
|
532
|
+
for scls in cls.__bases__:
|
533
|
+
for name in getattr(scls, _ABSTRACT_METHODS_ATTR, ()):
|
534
|
+
value = getattr(cls, name, None)
|
535
|
+
if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
|
536
|
+
abstracts.add(name)
|
537
|
+
|
538
|
+
for name, value in cls.__dict__.items():
|
539
|
+
if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
|
540
|
+
abstracts.add(name)
|
541
|
+
|
542
|
+
setattr(cls, _ABSTRACT_METHODS_ATTR, frozenset(abstracts))
|
543
|
+
return cls
|
544
|
+
|
545
|
+
|
546
|
+
#
|
547
|
+
|
548
|
+
|
549
|
+
class AbstractTypeError(TypeError):
|
550
|
+
pass
|
551
|
+
|
552
|
+
|
553
|
+
_FORCE_ABSTRACT_ATTR = '__forceabstract__'
|
554
|
+
|
555
|
+
|
556
|
+
class Abstract:
|
557
|
+
"""
|
558
|
+
Different from, but interoperable with, abc.ABC / abc.ABCMeta:
|
559
|
+
|
560
|
+
- This raises AbstractTypeError during class creation, not instance instantiation - unless Abstract or abc.ABC are
|
561
|
+
explicitly present in the class's direct bases.
|
562
|
+
- This will forbid instantiation of classes with Abstract in their direct bases even if there are no
|
563
|
+
abstractmethods left on the class.
|
564
|
+
- This is a mixin, not a metaclass.
|
565
|
+
- As it is not an ABCMeta, this does not support virtual base classes. As a result, operations like `isinstance`
|
566
|
+
and `issubclass` are ~7x faster.
|
567
|
+
- It additionally enforces a base class order of (Abstract, abc.ABC) to preemptively prevent common mro conflicts.
|
568
|
+
|
569
|
+
If not mixed-in with an ABCMeta, it will update __abstractmethods__ itself.
|
570
|
+
"""
|
571
|
+
|
572
|
+
__slots__ = ()
|
573
|
+
|
574
|
+
__abstractmethods__: ta.ClassVar[ta.FrozenSet[str]] = frozenset()
|
575
|
+
|
576
|
+
#
|
577
|
+
|
578
|
+
def __forceabstract__(self):
|
579
|
+
raise TypeError
|
580
|
+
|
581
|
+
# This is done manually, rather than through @abc.abstractmethod, to mask it from static analysis.
|
582
|
+
setattr(__forceabstract__, _IS_ABSTRACT_METHOD_ATTR, True)
|
583
|
+
|
584
|
+
#
|
585
|
+
|
586
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
587
|
+
setattr(
|
588
|
+
cls,
|
589
|
+
_FORCE_ABSTRACT_ATTR,
|
590
|
+
getattr(Abstract, _FORCE_ABSTRACT_ATTR) if Abstract in cls.__bases__ else False,
|
591
|
+
)
|
592
|
+
|
593
|
+
super().__init_subclass__(**kwargs)
|
594
|
+
|
595
|
+
if not (Abstract in cls.__bases__ or abc.ABC in cls.__bases__):
|
596
|
+
ams = {a: cls for a, o in cls.__dict__.items() if is_abstract_method(o)}
|
597
|
+
|
598
|
+
seen = set(cls.__dict__)
|
599
|
+
for b in cls.__bases__:
|
600
|
+
ams.update({a: b for a in set(getattr(b, _ABSTRACT_METHODS_ATTR, [])) - seen}) # noqa
|
601
|
+
seen.update(dir(b))
|
602
|
+
|
603
|
+
if ams:
|
604
|
+
raise AbstractTypeError(
|
605
|
+
f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
|
606
|
+
', '.join(sorted([
|
607
|
+
'.'.join([
|
608
|
+
*([m] if (m := getattr(c, '__module__')) else []),
|
609
|
+
getattr(c, '__qualname__', getattr(c, '__name__')),
|
610
|
+
a,
|
611
|
+
])
|
612
|
+
for a, c in ams.items()
|
613
|
+
])),
|
614
|
+
)
|
615
|
+
|
616
|
+
xbi = (Abstract, abc.ABC) # , ta.Generic ?
|
617
|
+
bis = [(cls.__bases__.index(b), b) for b in xbi if b in cls.__bases__]
|
618
|
+
if bis != sorted(bis):
|
619
|
+
raise TypeError(
|
620
|
+
f'Abstract subclass {cls.__name__} must have proper base class order of '
|
621
|
+
f'({", ".join(getattr(b, "__name__") for b in xbi)}), got: '
|
622
|
+
f'({", ".join(getattr(b, "__name__") for _, b in sorted(bis))})',
|
623
|
+
)
|
624
|
+
|
625
|
+
if not isinstance(cls, abc.ABCMeta):
|
626
|
+
update_abstracts(cls, force=True)
|
627
|
+
|
628
|
+
|
509
629
|
########################################
|
510
630
|
# ../../../omlish/lite/cached.py
|
511
631
|
|
@@ -1086,335 +1206,116 @@ json_dumps_compact: ta.Callable[..., str] = functools.partial(json.dumps, **JSON
|
|
1086
1206
|
|
1087
1207
|
|
1088
1208
|
########################################
|
1089
|
-
# ../../../omlish/lite/
|
1209
|
+
# ../../../omlish/lite/reflect.py
|
1090
1210
|
|
1091
1211
|
|
1092
1212
|
##
|
1093
1213
|
|
1094
1214
|
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1215
|
+
_GENERIC_ALIAS_TYPES = (
|
1216
|
+
ta._GenericAlias, # type: ignore # noqa
|
1217
|
+
*([ta._SpecialGenericAlias] if hasattr(ta, '_SpecialGenericAlias') else []), # noqa
|
1218
|
+
)
|
1099
1219
|
|
1100
|
-
#
|
1101
1220
|
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1221
|
+
def is_generic_alias(obj: ta.Any, *, origin: ta.Any = None) -> bool:
|
1222
|
+
return (
|
1223
|
+
isinstance(obj, _GENERIC_ALIAS_TYPES) and
|
1224
|
+
(origin is None or ta.get_origin(obj) is origin)
|
1225
|
+
)
|
1106
1226
|
|
1107
|
-
@abc.abstractmethod
|
1108
|
-
def must(self) -> T:
|
1109
|
-
raise NotImplementedError
|
1110
1227
|
|
1111
|
-
|
1228
|
+
is_callable_alias = functools.partial(is_generic_alias, origin=ta.Callable)
|
1112
1229
|
|
1113
|
-
@abc.abstractmethod
|
1114
|
-
def __repr__(self) -> str:
|
1115
|
-
raise NotImplementedError
|
1116
1230
|
|
1117
|
-
|
1118
|
-
def __hash__(self) -> int:
|
1119
|
-
raise NotImplementedError
|
1231
|
+
##
|
1120
1232
|
|
1121
|
-
@abc.abstractmethod
|
1122
|
-
def __eq__(self, other) -> bool:
|
1123
|
-
raise NotImplementedError
|
1124
1233
|
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1234
|
+
_UNION_ALIAS_ORIGINS = frozenset([
|
1235
|
+
ta.get_origin(ta.Optional[int]),
|
1236
|
+
*(
|
1237
|
+
[
|
1238
|
+
ta.get_origin(int | None),
|
1239
|
+
ta.get_origin(getattr(ta, 'TypeVar')('_T') | None),
|
1240
|
+
] if sys.version_info >= (3, 10) else ()
|
1241
|
+
),
|
1242
|
+
])
|
1128
1243
|
|
1129
|
-
#
|
1130
1244
|
|
1131
|
-
|
1132
|
-
|
1133
|
-
return not (self == other)
|
1245
|
+
def is_union_alias(obj: ta.Any) -> bool:
|
1246
|
+
return ta.get_origin(obj) in _UNION_ALIAS_ORIGINS
|
1134
1247
|
|
1135
|
-
@ta.final
|
1136
|
-
def __iter__(self) -> ta.Iterator[T]:
|
1137
|
-
if self.present:
|
1138
|
-
yield self.must()
|
1139
1248
|
|
1140
|
-
|
1141
|
-
def __bool__(self) -> ta.NoReturn:
|
1142
|
-
raise TypeError
|
1249
|
+
#
|
1143
1250
|
|
1144
|
-
#
|
1145
1251
|
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1252
|
+
def is_optional_alias(spec: ta.Any) -> bool:
|
1253
|
+
return (
|
1254
|
+
is_union_alias(spec) and
|
1255
|
+
len(ta.get_args(spec)) == 2 and
|
1256
|
+
any(a in (None, type(None)) for a in ta.get_args(spec))
|
1257
|
+
)
|
1150
1258
|
|
1151
|
-
@ta.final
|
1152
|
-
def filter(self, predicate: ta.Callable[[T], bool]) -> 'Maybe[T]':
|
1153
|
-
if self.present and predicate(self.must()):
|
1154
|
-
return self
|
1155
|
-
else:
|
1156
|
-
return Maybe.empty()
|
1157
1259
|
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
return Maybe.just(mapper(self.must()))
|
1162
|
-
else:
|
1163
|
-
return Maybe.empty()
|
1260
|
+
def get_optional_alias_arg(spec: ta.Any) -> ta.Any:
|
1261
|
+
[it] = [it for it in ta.get_args(spec) if it not in (None, type(None))]
|
1262
|
+
return it
|
1164
1263
|
|
1165
|
-
@ta.final
|
1166
|
-
def flat_map(self, mapper: ta.Callable[[T], 'Maybe[U]']) -> 'Maybe[U]':
|
1167
|
-
if self.present:
|
1168
|
-
if not isinstance(v := mapper(self.must()), Maybe):
|
1169
|
-
raise TypeError(v)
|
1170
|
-
return v
|
1171
|
-
else:
|
1172
|
-
return Maybe.empty()
|
1173
1264
|
|
1174
|
-
|
1175
|
-
def or_else(self, other: ta.Union[T, U]) -> ta.Union[T, U]:
|
1176
|
-
if self.present:
|
1177
|
-
return self.must()
|
1178
|
-
else:
|
1179
|
-
return other
|
1265
|
+
##
|
1180
1266
|
|
1181
|
-
@ta.final
|
1182
|
-
def or_else_get(self, supplier: ta.Callable[[], ta.Union[T, U]]) -> ta.Union[T, U]:
|
1183
|
-
if self.present:
|
1184
|
-
return self.must()
|
1185
|
-
else:
|
1186
|
-
return supplier()
|
1187
1267
|
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1268
|
+
def is_new_type(spec: ta.Any) -> bool:
|
1269
|
+
if isinstance(ta.NewType, type):
|
1270
|
+
return isinstance(spec, ta.NewType)
|
1271
|
+
else:
|
1272
|
+
# Before https://github.com/python/cpython/commit/c2f33dfc83ab270412bf243fb21f724037effa1a
|
1273
|
+
return isinstance(spec, types.FunctionType) and spec.__code__ is ta.NewType.__code__.co_consts[1] # type: ignore # noqa
|
1194
1274
|
|
1195
|
-
#
|
1196
1275
|
|
1197
|
-
|
1198
|
-
|
1199
|
-
if v is not None:
|
1200
|
-
return cls.just(v)
|
1201
|
-
else:
|
1202
|
-
return cls.empty()
|
1276
|
+
def get_new_type_supertype(spec: ta.Any) -> ta.Any:
|
1277
|
+
return spec.__supertype__
|
1203
1278
|
|
1204
|
-
@classmethod
|
1205
|
-
def just(cls, v: T) -> 'Maybe[T]':
|
1206
|
-
return _JustMaybe(v)
|
1207
1279
|
|
1208
|
-
|
1280
|
+
##
|
1209
1281
|
|
1210
|
-
@classmethod
|
1211
|
-
def empty(cls) -> 'Maybe[T]':
|
1212
|
-
return Maybe._empty
|
1213
1282
|
|
1283
|
+
def is_literal_type(spec: ta.Any) -> bool:
|
1284
|
+
if hasattr(ta, '_LiteralGenericAlias'):
|
1285
|
+
return isinstance(spec, ta._LiteralGenericAlias) # noqa
|
1286
|
+
else:
|
1287
|
+
return (
|
1288
|
+
isinstance(spec, ta._GenericAlias) and # type: ignore # noqa
|
1289
|
+
spec.__origin__ is ta.Literal
|
1290
|
+
)
|
1214
1291
|
|
1215
|
-
##
|
1216
1292
|
|
1293
|
+
def get_literal_type_args(spec: ta.Any) -> ta.Iterable[ta.Any]:
|
1294
|
+
return spec.__args__
|
1217
1295
|
|
1218
|
-
class _Maybe(Maybe[T], abc.ABC):
|
1219
|
-
def __lt__(self, other):
|
1220
|
-
if not isinstance(other, _Maybe):
|
1221
|
-
return NotImplemented
|
1222
|
-
sp = self.present
|
1223
|
-
op = other.present
|
1224
|
-
if self.present and other.present:
|
1225
|
-
return self.must() < other.must()
|
1226
|
-
else:
|
1227
|
-
return op and not sp
|
1228
1296
|
|
1297
|
+
########################################
|
1298
|
+
# ../../../omlish/lite/strings.py
|
1229
1299
|
|
1230
|
-
class _JustMaybe(_Maybe[T]):
|
1231
|
-
__slots__ = ('_v', '_hash')
|
1232
1300
|
|
1233
|
-
|
1234
|
-
super().__init__()
|
1301
|
+
##
|
1235
1302
|
|
1236
|
-
self._v = v
|
1237
1303
|
|
1238
|
-
|
1239
|
-
|
1240
|
-
return
|
1304
|
+
def camel_case(name: str, *, lower: bool = False) -> str:
|
1305
|
+
if not name:
|
1306
|
+
return ''
|
1307
|
+
s = ''.join(map(str.capitalize, name.split('_'))) # noqa
|
1308
|
+
if lower:
|
1309
|
+
s = s[0].lower() + s[1:]
|
1310
|
+
return s
|
1241
1311
|
|
1242
|
-
def must(self) -> T:
|
1243
|
-
return self._v
|
1244
1312
|
|
1245
|
-
|
1313
|
+
def snake_case(name: str) -> str:
|
1314
|
+
uppers: list[int | None] = [i for i, c in enumerate(name) if c.isupper()]
|
1315
|
+
return '_'.join([name[l:r].lower() for l, r in zip([None, *uppers], [*uppers, None])]).strip('_')
|
1246
1316
|
|
1247
|
-
def __repr__(self) -> str:
|
1248
|
-
return f'just({self._v!r})'
|
1249
1317
|
|
1250
|
-
|
1251
|
-
|
1252
|
-
def __hash__(self) -> int:
|
1253
|
-
try:
|
1254
|
-
return self._hash
|
1255
|
-
except AttributeError:
|
1256
|
-
pass
|
1257
|
-
h = self._hash = hash((_JustMaybe, self._v))
|
1258
|
-
return h
|
1259
|
-
|
1260
|
-
def __eq__(self, other):
|
1261
|
-
return (
|
1262
|
-
self.__class__ is other.__class__ and
|
1263
|
-
self._v == other._v # noqa
|
1264
|
-
)
|
1265
|
-
|
1266
|
-
|
1267
|
-
class _EmptyMaybe(_Maybe[T]):
|
1268
|
-
__slots__ = ()
|
1269
|
-
|
1270
|
-
@property
|
1271
|
-
def present(self) -> bool:
|
1272
|
-
return False
|
1273
|
-
|
1274
|
-
def must(self) -> T:
|
1275
|
-
raise Maybe.ValueNotPresentError
|
1276
|
-
|
1277
|
-
#
|
1278
|
-
|
1279
|
-
def __repr__(self) -> str:
|
1280
|
-
return 'empty()'
|
1281
|
-
|
1282
|
-
def __hash__(self) -> int:
|
1283
|
-
return hash(_EmptyMaybe)
|
1284
|
-
|
1285
|
-
def __eq__(self, other):
|
1286
|
-
return self.__class__ is other.__class__
|
1287
|
-
|
1288
|
-
|
1289
|
-
Maybe._empty = _EmptyMaybe() # noqa
|
1290
|
-
|
1291
|
-
|
1292
|
-
########################################
|
1293
|
-
# ../../../omlish/lite/reflect.py
|
1294
|
-
|
1295
|
-
|
1296
|
-
##
|
1297
|
-
|
1298
|
-
|
1299
|
-
_GENERIC_ALIAS_TYPES = (
|
1300
|
-
ta._GenericAlias, # type: ignore # noqa
|
1301
|
-
*([ta._SpecialGenericAlias] if hasattr(ta, '_SpecialGenericAlias') else []), # noqa
|
1302
|
-
)
|
1303
|
-
|
1304
|
-
|
1305
|
-
def is_generic_alias(obj: ta.Any, *, origin: ta.Any = None) -> bool:
|
1306
|
-
return (
|
1307
|
-
isinstance(obj, _GENERIC_ALIAS_TYPES) and
|
1308
|
-
(origin is None or ta.get_origin(obj) is origin)
|
1309
|
-
)
|
1310
|
-
|
1311
|
-
|
1312
|
-
is_callable_alias = functools.partial(is_generic_alias, origin=ta.Callable)
|
1313
|
-
|
1314
|
-
|
1315
|
-
##
|
1316
|
-
|
1317
|
-
|
1318
|
-
_UNION_ALIAS_ORIGINS = frozenset([
|
1319
|
-
ta.get_origin(ta.Optional[int]),
|
1320
|
-
*(
|
1321
|
-
[
|
1322
|
-
ta.get_origin(int | None),
|
1323
|
-
ta.get_origin(getattr(ta, 'TypeVar')('_T') | None),
|
1324
|
-
] if sys.version_info >= (3, 10) else ()
|
1325
|
-
),
|
1326
|
-
])
|
1327
|
-
|
1328
|
-
|
1329
|
-
def is_union_alias(obj: ta.Any) -> bool:
|
1330
|
-
return ta.get_origin(obj) in _UNION_ALIAS_ORIGINS
|
1331
|
-
|
1332
|
-
|
1333
|
-
#
|
1334
|
-
|
1335
|
-
|
1336
|
-
def is_optional_alias(spec: ta.Any) -> bool:
|
1337
|
-
return (
|
1338
|
-
is_union_alias(spec) and
|
1339
|
-
len(ta.get_args(spec)) == 2 and
|
1340
|
-
any(a in (None, type(None)) for a in ta.get_args(spec))
|
1341
|
-
)
|
1342
|
-
|
1343
|
-
|
1344
|
-
def get_optional_alias_arg(spec: ta.Any) -> ta.Any:
|
1345
|
-
[it] = [it for it in ta.get_args(spec) if it not in (None, type(None))]
|
1346
|
-
return it
|
1347
|
-
|
1348
|
-
|
1349
|
-
##
|
1350
|
-
|
1351
|
-
|
1352
|
-
def is_new_type(spec: ta.Any) -> bool:
|
1353
|
-
if isinstance(ta.NewType, type):
|
1354
|
-
return isinstance(spec, ta.NewType)
|
1355
|
-
else:
|
1356
|
-
# Before https://github.com/python/cpython/commit/c2f33dfc83ab270412bf243fb21f724037effa1a
|
1357
|
-
return isinstance(spec, types.FunctionType) and spec.__code__ is ta.NewType.__code__.co_consts[1] # type: ignore # noqa
|
1358
|
-
|
1359
|
-
|
1360
|
-
def get_new_type_supertype(spec: ta.Any) -> ta.Any:
|
1361
|
-
return spec.__supertype__
|
1362
|
-
|
1363
|
-
|
1364
|
-
##
|
1365
|
-
|
1366
|
-
|
1367
|
-
def is_literal_type(spec: ta.Any) -> bool:
|
1368
|
-
if hasattr(ta, '_LiteralGenericAlias'):
|
1369
|
-
return isinstance(spec, ta._LiteralGenericAlias) # noqa
|
1370
|
-
else:
|
1371
|
-
return (
|
1372
|
-
isinstance(spec, ta._GenericAlias) and # type: ignore # noqa
|
1373
|
-
spec.__origin__ is ta.Literal
|
1374
|
-
)
|
1375
|
-
|
1376
|
-
|
1377
|
-
def get_literal_type_args(spec: ta.Any) -> ta.Iterable[ta.Any]:
|
1378
|
-
return spec.__args__
|
1379
|
-
|
1380
|
-
|
1381
|
-
##
|
1382
|
-
|
1383
|
-
|
1384
|
-
def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
|
1385
|
-
seen = set()
|
1386
|
-
todo = list(reversed(cls.__subclasses__()))
|
1387
|
-
while todo:
|
1388
|
-
cur = todo.pop()
|
1389
|
-
if cur in seen:
|
1390
|
-
continue
|
1391
|
-
seen.add(cur)
|
1392
|
-
yield cur
|
1393
|
-
todo.extend(reversed(cur.__subclasses__()))
|
1394
|
-
|
1395
|
-
|
1396
|
-
########################################
|
1397
|
-
# ../../../omlish/lite/strings.py
|
1398
|
-
|
1399
|
-
|
1400
|
-
##
|
1401
|
-
|
1402
|
-
|
1403
|
-
def camel_case(name: str, *, lower: bool = False) -> str:
|
1404
|
-
if not name:
|
1405
|
-
return ''
|
1406
|
-
s = ''.join(map(str.capitalize, name.split('_'))) # noqa
|
1407
|
-
if lower:
|
1408
|
-
s = s[0].lower() + s[1:]
|
1409
|
-
return s
|
1410
|
-
|
1411
|
-
|
1412
|
-
def snake_case(name: str) -> str:
|
1413
|
-
uppers: list[int | None] = [i for i, c in enumerate(name) if c.isupper()]
|
1414
|
-
return '_'.join([name[l:r].lower() for l, r in zip([None, *uppers], [*uppers, None])]).strip('_')
|
1415
|
-
|
1416
|
-
|
1417
|
-
##
|
1318
|
+
##
|
1418
1319
|
|
1419
1320
|
|
1420
1321
|
def is_dunder(name: str) -> bool:
|
@@ -1492,277 +1393,78 @@ def format_num_bytes(num_bytes: int) -> str:
|
|
1492
1393
|
|
1493
1394
|
|
1494
1395
|
########################################
|
1495
|
-
# ../../../omlish/
|
1496
|
-
"""
|
1497
|
-
TODO:
|
1498
|
-
- Event (/ Predicate)
|
1499
|
-
"""
|
1396
|
+
# ../../../omlish/logs/filters.py
|
1500
1397
|
|
1501
1398
|
|
1502
1399
|
##
|
1503
1400
|
|
1504
1401
|
|
1505
|
-
class
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1402
|
+
class TidLogFilter(logging.Filter):
|
1403
|
+
def filter(self, record):
|
1404
|
+
# FIXME: handle better - missing from wasm and cosmos
|
1405
|
+
if hasattr(threading, 'get_native_id'):
|
1406
|
+
record.tid = threading.get_native_id()
|
1407
|
+
else:
|
1408
|
+
record.tid = '?'
|
1409
|
+
return True
|
1512
1410
|
|
1513
|
-
@abc.abstractmethod
|
1514
|
-
def expired(self) -> bool:
|
1515
|
-
"""Return whether or not this timeout has expired."""
|
1516
1411
|
|
1517
|
-
|
1412
|
+
########################################
|
1413
|
+
# ../../../omlish/logs/proxy.py
|
1518
1414
|
|
1519
|
-
@abc.abstractmethod
|
1520
|
-
def remaining(self) -> float:
|
1521
|
-
"""Returns the time (in seconds) remaining until the timeout expires. May be negative and/or infinite."""
|
1522
1415
|
|
1523
|
-
|
1416
|
+
##
|
1524
1417
|
|
1525
|
-
@abc.abstractmethod
|
1526
|
-
def __call__(self) -> float:
|
1527
|
-
"""Returns the time (in seconds) remaining until the timeout expires, or raises if the timeout has expired."""
|
1528
1418
|
|
1529
|
-
|
1419
|
+
class ProxyLogFilterer(logging.Filterer):
|
1420
|
+
def __init__(self, underlying: logging.Filterer) -> None: # noqa
|
1421
|
+
self._underlying = underlying
|
1530
1422
|
|
1531
|
-
@
|
1532
|
-
def
|
1533
|
-
|
1423
|
+
@property
|
1424
|
+
def underlying(self) -> logging.Filterer:
|
1425
|
+
return self._underlying
|
1534
1426
|
|
1535
|
-
|
1427
|
+
@property
|
1428
|
+
def filters(self):
|
1429
|
+
return self._underlying.filters
|
1536
1430
|
|
1537
|
-
|
1431
|
+
@filters.setter
|
1432
|
+
def filters(self, filters):
|
1433
|
+
self._underlying.filters = filters
|
1538
1434
|
|
1539
|
-
|
1540
|
-
|
1541
|
-
return time.monotonic()
|
1435
|
+
def addFilter(self, filter): # noqa
|
1436
|
+
self._underlying.addFilter(filter)
|
1542
1437
|
|
1543
|
-
#
|
1438
|
+
def removeFilter(self, filter): # noqa
|
1439
|
+
self._underlying.removeFilter(filter)
|
1544
1440
|
|
1545
|
-
|
1546
|
-
|
1547
|
-
raise TypeError
|
1441
|
+
def filter(self, record):
|
1442
|
+
return self._underlying.filter(record)
|
1548
1443
|
|
1549
|
-
class _NOT_SPECIFIED: # noqa
|
1550
|
-
def __new__(cls, *args, **kwargs): # noqa
|
1551
|
-
raise TypeError
|
1552
1444
|
|
1553
|
-
|
1554
|
-
def
|
1555
|
-
|
1556
|
-
obj: TimeoutLike,
|
1557
|
-
default: ta.Union[TimeoutLike, ta.Type[_NOT_SPECIFIED]] = _NOT_SPECIFIED,
|
1558
|
-
) -> 'Timeout':
|
1559
|
-
if obj is None:
|
1560
|
-
return InfiniteTimeout()
|
1445
|
+
class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
|
1446
|
+
def __init__(self, underlying: logging.Handler) -> None: # noqa
|
1447
|
+
ProxyLogFilterer.__init__(self, underlying)
|
1561
1448
|
|
1562
|
-
|
1563
|
-
return obj
|
1449
|
+
_underlying: logging.Handler
|
1564
1450
|
|
1565
|
-
|
1566
|
-
|
1451
|
+
@property
|
1452
|
+
def underlying(self) -> logging.Handler:
|
1453
|
+
return self._underlying
|
1567
1454
|
|
1568
|
-
|
1569
|
-
|
1455
|
+
def get_name(self):
|
1456
|
+
return self._underlying.get_name()
|
1570
1457
|
|
1571
|
-
|
1572
|
-
|
1573
|
-
raise RuntimeError('Must specify a default timeout')
|
1458
|
+
def set_name(self, name):
|
1459
|
+
self._underlying.set_name(name)
|
1574
1460
|
|
1575
|
-
|
1576
|
-
|
1461
|
+
@property
|
1462
|
+
def name(self): # type: ignore[override]
|
1463
|
+
return self._underlying.name
|
1577
1464
|
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
@classmethod
|
1582
|
-
def of_deadline(cls, deadline: float) -> 'DeadlineTimeout':
|
1583
|
-
return DeadlineTimeout(deadline)
|
1584
|
-
|
1585
|
-
@classmethod
|
1586
|
-
def of_predicate(cls, expired_fn: ta.Callable[[], bool]) -> 'PredicateTimeout':
|
1587
|
-
return PredicateTimeout(expired_fn)
|
1588
|
-
|
1589
|
-
|
1590
|
-
class DeadlineTimeout(Timeout):
|
1591
|
-
def __init__(
|
1592
|
-
self,
|
1593
|
-
deadline: float,
|
1594
|
-
exc: ta.Union[ta.Type[BaseException], BaseException] = TimeoutError,
|
1595
|
-
) -> None:
|
1596
|
-
super().__init__()
|
1597
|
-
|
1598
|
-
self.deadline = deadline
|
1599
|
-
self.exc = exc
|
1600
|
-
|
1601
|
-
@property
|
1602
|
-
def can_expire(self) -> bool:
|
1603
|
-
return True
|
1604
|
-
|
1605
|
-
def expired(self) -> bool:
|
1606
|
-
return not (self.remaining() > 0)
|
1607
|
-
|
1608
|
-
def remaining(self) -> float:
|
1609
|
-
return self.deadline - self._now()
|
1610
|
-
|
1611
|
-
def __call__(self) -> float:
|
1612
|
-
if (rem := self.remaining()) > 0:
|
1613
|
-
return rem
|
1614
|
-
raise self.exc
|
1615
|
-
|
1616
|
-
def or_(self, o: ta.Any) -> ta.Any:
|
1617
|
-
return self()
|
1618
|
-
|
1619
|
-
|
1620
|
-
class InfiniteTimeout(Timeout):
|
1621
|
-
@property
|
1622
|
-
def can_expire(self) -> bool:
|
1623
|
-
return False
|
1624
|
-
|
1625
|
-
def expired(self) -> bool:
|
1626
|
-
return False
|
1627
|
-
|
1628
|
-
def remaining(self) -> float:
|
1629
|
-
return float('inf')
|
1630
|
-
|
1631
|
-
def __call__(self) -> float:
|
1632
|
-
return float('inf')
|
1633
|
-
|
1634
|
-
def or_(self, o: ta.Any) -> ta.Any:
|
1635
|
-
return o
|
1636
|
-
|
1637
|
-
|
1638
|
-
class CompositeTimeout(Timeout):
|
1639
|
-
def __init__(self, *children: Timeout) -> None:
|
1640
|
-
super().__init__()
|
1641
|
-
|
1642
|
-
self.children = children
|
1643
|
-
|
1644
|
-
@property
|
1645
|
-
def can_expire(self) -> bool:
|
1646
|
-
return any(c.can_expire for c in self.children)
|
1647
|
-
|
1648
|
-
def expired(self) -> bool:
|
1649
|
-
return any(c.expired() for c in self.children)
|
1650
|
-
|
1651
|
-
def remaining(self) -> float:
|
1652
|
-
return min(c.remaining() for c in self.children)
|
1653
|
-
|
1654
|
-
def __call__(self) -> float:
|
1655
|
-
return min(c() for c in self.children)
|
1656
|
-
|
1657
|
-
def or_(self, o: ta.Any) -> ta.Any:
|
1658
|
-
if self.can_expire:
|
1659
|
-
return self()
|
1660
|
-
return o
|
1661
|
-
|
1662
|
-
|
1663
|
-
class PredicateTimeout(Timeout):
|
1664
|
-
def __init__(
|
1665
|
-
self,
|
1666
|
-
expired_fn: ta.Callable[[], bool],
|
1667
|
-
exc: ta.Union[ta.Type[BaseException], BaseException] = TimeoutError,
|
1668
|
-
) -> None:
|
1669
|
-
super().__init__()
|
1670
|
-
|
1671
|
-
self.expired_fn = expired_fn
|
1672
|
-
self.exc = exc
|
1673
|
-
|
1674
|
-
@property
|
1675
|
-
def can_expire(self) -> bool:
|
1676
|
-
return True
|
1677
|
-
|
1678
|
-
def expired(self) -> bool:
|
1679
|
-
return self.expired_fn()
|
1680
|
-
|
1681
|
-
def remaining(self) -> float:
|
1682
|
-
return float('inf')
|
1683
|
-
|
1684
|
-
def __call__(self) -> float:
|
1685
|
-
if not self.expired_fn():
|
1686
|
-
return float('inf')
|
1687
|
-
raise self.exc
|
1688
|
-
|
1689
|
-
def or_(self, o: ta.Any) -> ta.Any:
|
1690
|
-
return self()
|
1691
|
-
|
1692
|
-
|
1693
|
-
########################################
|
1694
|
-
# ../../../omlish/logs/filters.py
|
1695
|
-
|
1696
|
-
|
1697
|
-
##
|
1698
|
-
|
1699
|
-
|
1700
|
-
class TidLogFilter(logging.Filter):
|
1701
|
-
def filter(self, record):
|
1702
|
-
# FIXME: handle better - missing from wasm and cosmos
|
1703
|
-
if hasattr(threading, 'get_native_id'):
|
1704
|
-
record.tid = threading.get_native_id()
|
1705
|
-
else:
|
1706
|
-
record.tid = '?'
|
1707
|
-
return True
|
1708
|
-
|
1709
|
-
|
1710
|
-
########################################
|
1711
|
-
# ../../../omlish/logs/proxy.py
|
1712
|
-
|
1713
|
-
|
1714
|
-
##
|
1715
|
-
|
1716
|
-
|
1717
|
-
class ProxyLogFilterer(logging.Filterer):
|
1718
|
-
def __init__(self, underlying: logging.Filterer) -> None: # noqa
|
1719
|
-
self._underlying = underlying
|
1720
|
-
|
1721
|
-
@property
|
1722
|
-
def underlying(self) -> logging.Filterer:
|
1723
|
-
return self._underlying
|
1724
|
-
|
1725
|
-
@property
|
1726
|
-
def filters(self):
|
1727
|
-
return self._underlying.filters
|
1728
|
-
|
1729
|
-
@filters.setter
|
1730
|
-
def filters(self, filters):
|
1731
|
-
self._underlying.filters = filters
|
1732
|
-
|
1733
|
-
def addFilter(self, filter): # noqa
|
1734
|
-
self._underlying.addFilter(filter)
|
1735
|
-
|
1736
|
-
def removeFilter(self, filter): # noqa
|
1737
|
-
self._underlying.removeFilter(filter)
|
1738
|
-
|
1739
|
-
def filter(self, record):
|
1740
|
-
return self._underlying.filter(record)
|
1741
|
-
|
1742
|
-
|
1743
|
-
class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
|
1744
|
-
def __init__(self, underlying: logging.Handler) -> None: # noqa
|
1745
|
-
ProxyLogFilterer.__init__(self, underlying)
|
1746
|
-
|
1747
|
-
_underlying: logging.Handler
|
1748
|
-
|
1749
|
-
@property
|
1750
|
-
def underlying(self) -> logging.Handler:
|
1751
|
-
return self._underlying
|
1752
|
-
|
1753
|
-
def get_name(self):
|
1754
|
-
return self._underlying.get_name()
|
1755
|
-
|
1756
|
-
def set_name(self, name):
|
1757
|
-
self._underlying.set_name(name)
|
1758
|
-
|
1759
|
-
@property
|
1760
|
-
def name(self): # type: ignore[override]
|
1761
|
-
return self._underlying.name
|
1762
|
-
|
1763
|
-
@property
|
1764
|
-
def level(self):
|
1765
|
-
return self._underlying.level
|
1465
|
+
@property
|
1466
|
+
def level(self):
|
1467
|
+
return self._underlying.level
|
1766
1468
|
|
1767
1469
|
@level.setter
|
1768
1470
|
def level(self, level):
|
@@ -1833,7 +1535,7 @@ class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
|
|
1833
1535
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This file is dual licensed under the terms of the
|
1834
1536
|
# Apache License, Version 2.0, and the BSD License. See the LICENSE file in the root of this repository for complete
|
1835
1537
|
# details.
|
1836
|
-
# https://github.com/pypa/packaging/blob/
|
1538
|
+
# https://github.com/pypa/packaging/blob/48125006684bb2d7d28c50af48a03176da45942d/src/packaging/specifiers.py
|
1837
1539
|
|
1838
1540
|
|
1839
1541
|
##
|
@@ -1981,7 +1683,7 @@ class Specifier(BaseSpecifier):
|
|
1981
1683
|
) -> None:
|
1982
1684
|
match = self._regex.search(spec)
|
1983
1685
|
if not match:
|
1984
|
-
raise InvalidSpecifier(f
|
1686
|
+
raise InvalidSpecifier(f'Invalid specifier: {spec!r}')
|
1985
1687
|
|
1986
1688
|
self._spec: ta.Tuple[str, str] = (
|
1987
1689
|
match.group('operator').strip(),
|
@@ -1996,7 +1698,7 @@ class Specifier(BaseSpecifier):
|
|
1996
1698
|
return self._prereleases
|
1997
1699
|
|
1998
1700
|
operator, version = self._spec
|
1999
|
-
if operator in ['==', '>=', '<=', '~=', '===']:
|
1701
|
+
if operator in ['==', '>=', '<=', '~=', '===', '>', '<']:
|
2000
1702
|
if operator == '==' and version.endswith('.*'):
|
2001
1703
|
version = version[:-2]
|
2002
1704
|
|
@@ -2122,40 +1824,38 @@ class Specifier(BaseSpecifier):
|
|
2122
1824
|
return self.contains(item)
|
2123
1825
|
|
2124
1826
|
def contains(self, item: UnparsedVersion, prereleases: ta.Optional[bool] = None) -> bool:
|
2125
|
-
|
2126
|
-
prereleases = self.prereleases
|
2127
|
-
|
2128
|
-
normalized_item = _coerce_version(item)
|
2129
|
-
|
2130
|
-
if normalized_item.is_prerelease and not prereleases:
|
2131
|
-
return False
|
2132
|
-
|
2133
|
-
operator_callable: CallableVersionOperator = self._get_operator(self.operator)
|
2134
|
-
return operator_callable(normalized_item, self.version)
|
1827
|
+
return bool(list(self.filter([item], prereleases=prereleases)))
|
2135
1828
|
|
2136
1829
|
def filter(
|
2137
1830
|
self,
|
2138
1831
|
iterable: ta.Iterable[UnparsedVersionVar],
|
2139
1832
|
prereleases: ta.Optional[bool] = None,
|
2140
1833
|
) -> ta.Iterator[UnparsedVersionVar]:
|
2141
|
-
|
2142
|
-
|
1834
|
+
prereleases_versions = []
|
1835
|
+
found_non_prereleases = False
|
1836
|
+
|
1837
|
+
include_prereleases = (
|
1838
|
+
prereleases if prereleases is not None else self.prereleases
|
1839
|
+
)
|
2143
1840
|
|
2144
|
-
|
1841
|
+
operator_callable = self._get_operator(self.operator)
|
2145
1842
|
|
2146
1843
|
for version in iterable:
|
2147
1844
|
parsed_version = _coerce_version(version)
|
2148
1845
|
|
2149
|
-
if
|
2150
|
-
if parsed_version.is_prerelease
|
2151
|
-
|
2152
|
-
else:
|
2153
|
-
yielded = True
|
1846
|
+
if operator_callable(parsed_version, self.version):
|
1847
|
+
if not parsed_version.is_prerelease or include_prereleases:
|
1848
|
+
found_non_prereleases = True
|
2154
1849
|
yield version
|
1850
|
+
elif prereleases is None and self._prereleases is not False:
|
1851
|
+
prereleases_versions.append(version)
|
2155
1852
|
|
2156
|
-
if
|
2157
|
-
|
2158
|
-
|
1853
|
+
if (
|
1854
|
+
not found_non_prereleases and
|
1855
|
+
prereleases is None and
|
1856
|
+
self._prereleases is not False
|
1857
|
+
):
|
1858
|
+
yield from prereleases_versions
|
2159
1859
|
|
2160
1860
|
|
2161
1861
|
_version_prefix_regex = re.compile(r'^([0-9]+)((?:a|b|c|rc)[0-9]+)$')
|
@@ -2206,12 +1906,15 @@ def _pad_version(left: ta.List[str], right: ta.List[str]) -> ta.Tuple[ta.List[st
|
|
2206
1906
|
class SpecifierSet(BaseSpecifier):
|
2207
1907
|
def __init__(
|
2208
1908
|
self,
|
2209
|
-
specifiers: str = '',
|
1909
|
+
specifiers: ta.Union[str, ta.Iterable['Specifier']] = '',
|
2210
1910
|
prereleases: ta.Optional[bool] = None,
|
2211
1911
|
) -> None:
|
2212
|
-
|
1912
|
+
if isinstance(specifiers, str):
|
1913
|
+
split_specifiers = [s.strip() for s in specifiers.split(',') if s.strip()]
|
1914
|
+
self._specs = frozenset(map(Specifier, split_specifiers))
|
1915
|
+
else:
|
1916
|
+
self._specs = frozenset(specifiers)
|
2213
1917
|
|
2214
|
-
self._specs = frozenset(map(Specifier, split_specifiers))
|
2215
1918
|
self._prereleases = prereleases
|
2216
1919
|
|
2217
1920
|
@property
|
@@ -2222,7 +1925,10 @@ class SpecifierSet(BaseSpecifier):
|
|
2222
1925
|
if not self._specs:
|
2223
1926
|
return None
|
2224
1927
|
|
2225
|
-
|
1928
|
+
if any(s.prereleases for s in self._specs):
|
1929
|
+
return True
|
1930
|
+
|
1931
|
+
return None
|
2226
1932
|
|
2227
1933
|
@prereleases.setter
|
2228
1934
|
def prereleases(self, value: bool) -> None:
|
@@ -2289,28 +1995,22 @@ class SpecifierSet(BaseSpecifier):
|
|
2289
1995
|
if not isinstance(item, Version):
|
2290
1996
|
item = Version(item)
|
2291
1997
|
|
2292
|
-
if prereleases is None:
|
2293
|
-
prereleases = self.prereleases
|
2294
|
-
|
2295
|
-
if not prereleases and item.is_prerelease:
|
2296
|
-
return False
|
2297
|
-
|
2298
1998
|
if installed and item.is_prerelease:
|
2299
|
-
|
1999
|
+
prereleases = True
|
2300
2000
|
|
2301
|
-
return
|
2001
|
+
return bool(list(self.filter([item], prereleases=prereleases)))
|
2302
2002
|
|
2303
2003
|
def filter(
|
2304
2004
|
self,
|
2305
2005
|
iterable: ta.Iterable[UnparsedVersionVar],
|
2306
2006
|
prereleases: ta.Optional[bool] = None,
|
2307
2007
|
) -> ta.Iterator[UnparsedVersionVar]:
|
2308
|
-
if prereleases is None:
|
2008
|
+
if prereleases is None and self.prereleases is not None:
|
2309
2009
|
prereleases = self.prereleases
|
2310
2010
|
|
2311
2011
|
if self._specs:
|
2312
2012
|
for spec in self._specs:
|
2313
|
-
iterable = spec.filter(iterable, prereleases=
|
2013
|
+
iterable = spec.filter(iterable, prereleases=prereleases)
|
2314
2014
|
return iter(iterable)
|
2315
2015
|
|
2316
2016
|
else:
|
@@ -2543,91 +2243,676 @@ class ArgparseCli:
|
|
2543
2243
|
else:
|
2544
2244
|
obj.dest = obj.kwargs['dest'] = att # type: ignore
|
2545
2245
|
|
2546
|
-
parser.add_argument(*obj.args, **obj.kwargs)
|
2246
|
+
parser.add_argument(*obj.args, **obj.kwargs)
|
2247
|
+
|
2248
|
+
else:
|
2249
|
+
raise TypeError(obj)
|
2250
|
+
|
2251
|
+
#
|
2252
|
+
|
2253
|
+
_parser: ta.ClassVar[argparse.ArgumentParser]
|
2254
|
+
|
2255
|
+
@classmethod
|
2256
|
+
def get_parser(cls) -> argparse.ArgumentParser:
|
2257
|
+
return cls._parser
|
2258
|
+
|
2259
|
+
@property
|
2260
|
+
def argv(self) -> ta.Sequence[str]:
|
2261
|
+
return self._argv
|
2262
|
+
|
2263
|
+
@property
|
2264
|
+
def args(self) -> argparse.Namespace:
|
2265
|
+
return self._args
|
2266
|
+
|
2267
|
+
@property
|
2268
|
+
def unknown_args(self) -> ta.Sequence[str]:
|
2269
|
+
return self._unknown_args
|
2270
|
+
|
2271
|
+
#
|
2272
|
+
|
2273
|
+
def _bind_cli_cmd(self, cmd: ArgparseCmd) -> ta.Callable:
|
2274
|
+
return cmd.__get__(self, type(self))
|
2275
|
+
|
2276
|
+
def prepare_cli_run(self) -> ta.Optional[ta.Callable]:
|
2277
|
+
cmd = getattr(self.args, '_cmd', None)
|
2278
|
+
|
2279
|
+
if self._unknown_args and not (cmd is not None and cmd.accepts_unknown):
|
2280
|
+
msg = f'unrecognized arguments: {" ".join(self._unknown_args)}'
|
2281
|
+
if (parser := self.get_parser()).exit_on_error:
|
2282
|
+
parser.error(msg)
|
2283
|
+
else:
|
2284
|
+
raise argparse.ArgumentError(None, msg)
|
2285
|
+
|
2286
|
+
if cmd is None:
|
2287
|
+
self.get_parser().print_help()
|
2288
|
+
return None
|
2289
|
+
|
2290
|
+
return self._bind_cli_cmd(cmd)
|
2291
|
+
|
2292
|
+
#
|
2293
|
+
|
2294
|
+
def cli_run(self) -> ta.Optional[int]:
|
2295
|
+
if (fn := self.prepare_cli_run()) is None:
|
2296
|
+
return 0
|
2297
|
+
|
2298
|
+
return fn()
|
2299
|
+
|
2300
|
+
def cli_run_and_exit(self) -> ta.NoReturn:
|
2301
|
+
sys.exit(rc if isinstance(rc := self.cli_run(), int) else 0)
|
2302
|
+
|
2303
|
+
def __call__(self, *, exit: bool = False) -> ta.Optional[int]: # noqa
|
2304
|
+
if exit:
|
2305
|
+
return self.cli_run_and_exit()
|
2306
|
+
else:
|
2307
|
+
return self.cli_run()
|
2308
|
+
|
2309
|
+
#
|
2310
|
+
|
2311
|
+
async def async_cli_run(
|
2312
|
+
self,
|
2313
|
+
*,
|
2314
|
+
force_async: bool = False,
|
2315
|
+
) -> ta.Optional[int]:
|
2316
|
+
if (fn := self.prepare_cli_run()) is None:
|
2317
|
+
return 0
|
2318
|
+
|
2319
|
+
if force_async:
|
2320
|
+
is_async = True
|
2321
|
+
else:
|
2322
|
+
tfn = fn
|
2323
|
+
if isinstance(tfn, ArgparseCmd):
|
2324
|
+
tfn = tfn.fn
|
2325
|
+
is_async = inspect.iscoroutinefunction(tfn)
|
2326
|
+
|
2327
|
+
if is_async:
|
2328
|
+
return await fn()
|
2329
|
+
else:
|
2330
|
+
return fn()
|
2331
|
+
|
2332
|
+
|
2333
|
+
########################################
|
2334
|
+
# ../../../omlish/lite/maybes.py
|
2335
|
+
|
2336
|
+
|
2337
|
+
##
|
2338
|
+
|
2339
|
+
|
2340
|
+
@functools.total_ordering
|
2341
|
+
class Maybe(ta.Generic[T]):
|
2342
|
+
class ValueNotPresentError(BaseException):
|
2343
|
+
pass
|
2344
|
+
|
2345
|
+
#
|
2346
|
+
|
2347
|
+
@property
|
2348
|
+
@abc.abstractmethod
|
2349
|
+
def present(self) -> bool:
|
2350
|
+
raise NotImplementedError
|
2351
|
+
|
2352
|
+
@abc.abstractmethod
|
2353
|
+
def must(self) -> T:
|
2354
|
+
raise NotImplementedError
|
2355
|
+
|
2356
|
+
#
|
2357
|
+
|
2358
|
+
@abc.abstractmethod
|
2359
|
+
def __repr__(self) -> str:
|
2360
|
+
raise NotImplementedError
|
2361
|
+
|
2362
|
+
@abc.abstractmethod
|
2363
|
+
def __hash__(self) -> int:
|
2364
|
+
raise NotImplementedError
|
2365
|
+
|
2366
|
+
@abc.abstractmethod
|
2367
|
+
def __eq__(self, other) -> bool:
|
2368
|
+
raise NotImplementedError
|
2369
|
+
|
2370
|
+
@abc.abstractmethod
|
2371
|
+
def __lt__(self, other) -> bool:
|
2372
|
+
raise NotImplementedError
|
2373
|
+
|
2374
|
+
#
|
2375
|
+
|
2376
|
+
@ta.final
|
2377
|
+
def __ne__(self, other):
|
2378
|
+
return not (self == other)
|
2379
|
+
|
2380
|
+
@ta.final
|
2381
|
+
def __iter__(self) -> ta.Iterator[T]:
|
2382
|
+
if self.present:
|
2383
|
+
yield self.must()
|
2384
|
+
|
2385
|
+
@ta.final
|
2386
|
+
def __bool__(self) -> ta.NoReturn:
|
2387
|
+
raise TypeError
|
2388
|
+
|
2389
|
+
#
|
2390
|
+
|
2391
|
+
@ta.final
|
2392
|
+
def if_present(self, consumer: ta.Callable[[T], None]) -> None:
|
2393
|
+
if self.present:
|
2394
|
+
consumer(self.must())
|
2395
|
+
|
2396
|
+
@ta.final
|
2397
|
+
def filter(self, predicate: ta.Callable[[T], bool]) -> 'Maybe[T]':
|
2398
|
+
if self.present and predicate(self.must()):
|
2399
|
+
return self
|
2400
|
+
else:
|
2401
|
+
return Maybe.empty()
|
2402
|
+
|
2403
|
+
@ta.final
|
2404
|
+
def map(self, mapper: ta.Callable[[T], U]) -> 'Maybe[U]':
|
2405
|
+
if self.present:
|
2406
|
+
return Maybe.just(mapper(self.must()))
|
2407
|
+
else:
|
2408
|
+
return Maybe.empty()
|
2409
|
+
|
2410
|
+
@ta.final
|
2411
|
+
def flat_map(self, mapper: ta.Callable[[T], 'Maybe[U]']) -> 'Maybe[U]':
|
2412
|
+
if self.present:
|
2413
|
+
if not isinstance(v := mapper(self.must()), Maybe):
|
2414
|
+
raise TypeError(v)
|
2415
|
+
return v
|
2416
|
+
else:
|
2417
|
+
return Maybe.empty()
|
2418
|
+
|
2419
|
+
@ta.final
|
2420
|
+
def or_else(self, other: ta.Union[T, U]) -> ta.Union[T, U]:
|
2421
|
+
if self.present:
|
2422
|
+
return self.must()
|
2423
|
+
else:
|
2424
|
+
return other
|
2425
|
+
|
2426
|
+
@ta.final
|
2427
|
+
def or_else_get(self, supplier: ta.Callable[[], ta.Union[T, U]]) -> ta.Union[T, U]:
|
2428
|
+
if self.present:
|
2429
|
+
return self.must()
|
2430
|
+
else:
|
2431
|
+
return supplier()
|
2432
|
+
|
2433
|
+
@ta.final
|
2434
|
+
def or_else_raise(self, exception_supplier: ta.Callable[[], Exception]) -> T:
|
2435
|
+
if self.present:
|
2436
|
+
return self.must()
|
2437
|
+
else:
|
2438
|
+
raise exception_supplier()
|
2439
|
+
|
2440
|
+
#
|
2441
|
+
|
2442
|
+
@classmethod
|
2443
|
+
def of_optional(cls, v: ta.Optional[T]) -> 'Maybe[T]':
|
2444
|
+
if v is not None:
|
2445
|
+
return cls.just(v)
|
2446
|
+
else:
|
2447
|
+
return cls.empty()
|
2448
|
+
|
2449
|
+
@classmethod
|
2450
|
+
def just(cls, v: T) -> 'Maybe[T]':
|
2451
|
+
return _JustMaybe(v)
|
2452
|
+
|
2453
|
+
_empty: ta.ClassVar['Maybe']
|
2454
|
+
|
2455
|
+
@classmethod
|
2456
|
+
def empty(cls) -> 'Maybe[T]':
|
2457
|
+
return Maybe._empty
|
2458
|
+
|
2459
|
+
|
2460
|
+
##
|
2461
|
+
|
2462
|
+
|
2463
|
+
class _Maybe(Maybe[T], Abstract):
|
2464
|
+
def __lt__(self, other):
|
2465
|
+
if not isinstance(other, _Maybe):
|
2466
|
+
return NotImplemented
|
2467
|
+
sp = self.present
|
2468
|
+
op = other.present
|
2469
|
+
if self.present and other.present:
|
2470
|
+
return self.must() < other.must()
|
2471
|
+
else:
|
2472
|
+
return op and not sp
|
2473
|
+
|
2474
|
+
|
2475
|
+
class _JustMaybe(_Maybe[T]):
|
2476
|
+
__slots__ = ('_v', '_hash')
|
2477
|
+
|
2478
|
+
def __init__(self, v: T) -> None:
|
2479
|
+
super().__init__()
|
2480
|
+
|
2481
|
+
self._v = v
|
2482
|
+
|
2483
|
+
@property
|
2484
|
+
def present(self) -> bool:
|
2485
|
+
return True
|
2486
|
+
|
2487
|
+
def must(self) -> T:
|
2488
|
+
return self._v
|
2489
|
+
|
2490
|
+
#
|
2491
|
+
|
2492
|
+
def __repr__(self) -> str:
|
2493
|
+
return f'just({self._v!r})'
|
2494
|
+
|
2495
|
+
_hash: int
|
2496
|
+
|
2497
|
+
def __hash__(self) -> int:
|
2498
|
+
try:
|
2499
|
+
return self._hash
|
2500
|
+
except AttributeError:
|
2501
|
+
pass
|
2502
|
+
h = self._hash = hash((_JustMaybe, self._v))
|
2503
|
+
return h
|
2504
|
+
|
2505
|
+
def __eq__(self, other):
|
2506
|
+
return (
|
2507
|
+
self.__class__ is other.__class__ and
|
2508
|
+
self._v == other._v # noqa
|
2509
|
+
)
|
2510
|
+
|
2511
|
+
|
2512
|
+
class _EmptyMaybe(_Maybe[T]):
|
2513
|
+
__slots__ = ()
|
2514
|
+
|
2515
|
+
@property
|
2516
|
+
def present(self) -> bool:
|
2517
|
+
return False
|
2518
|
+
|
2519
|
+
def must(self) -> T:
|
2520
|
+
raise Maybe.ValueNotPresentError
|
2521
|
+
|
2522
|
+
#
|
2523
|
+
|
2524
|
+
def __repr__(self) -> str:
|
2525
|
+
return 'empty()'
|
2526
|
+
|
2527
|
+
def __hash__(self) -> int:
|
2528
|
+
return hash(_EmptyMaybe)
|
2529
|
+
|
2530
|
+
def __eq__(self, other):
|
2531
|
+
return self.__class__ is other.__class__
|
2532
|
+
|
2533
|
+
|
2534
|
+
Maybe._empty = _EmptyMaybe() # noqa
|
2535
|
+
|
2536
|
+
|
2537
|
+
########################################
|
2538
|
+
# ../../../omlish/lite/runtime.py
|
2539
|
+
|
2540
|
+
|
2541
|
+
##
|
2542
|
+
|
2543
|
+
|
2544
|
+
@cached_nullary
|
2545
|
+
def is_debugger_attached() -> bool:
|
2546
|
+
return any(frame[1].endswith('pydevd.py') for frame in inspect.stack())
|
2547
|
+
|
2548
|
+
|
2549
|
+
LITE_REQUIRED_PYTHON_VERSION = (3, 8)
|
2550
|
+
|
2551
|
+
|
2552
|
+
def check_lite_runtime_version() -> None:
|
2553
|
+
if sys.version_info < LITE_REQUIRED_PYTHON_VERSION:
|
2554
|
+
raise OSError(f'Requires python {LITE_REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa
|
2555
|
+
|
2556
|
+
|
2557
|
+
########################################
|
2558
|
+
# ../../../omlish/lite/timeouts.py
|
2559
|
+
"""
|
2560
|
+
TODO:
|
2561
|
+
- Event (/ Predicate)
|
2562
|
+
"""
|
2563
|
+
|
2564
|
+
|
2565
|
+
##
|
2566
|
+
|
2567
|
+
|
2568
|
+
class Timeout(Abstract):
|
2569
|
+
@property
|
2570
|
+
@abc.abstractmethod
|
2571
|
+
def can_expire(self) -> bool:
|
2572
|
+
"""Indicates whether or not this timeout will ever expire."""
|
2573
|
+
|
2574
|
+
raise NotImplementedError
|
2575
|
+
|
2576
|
+
@abc.abstractmethod
|
2577
|
+
def expired(self) -> bool:
|
2578
|
+
"""Return whether or not this timeout has expired."""
|
2579
|
+
|
2580
|
+
raise NotImplementedError
|
2581
|
+
|
2582
|
+
@abc.abstractmethod
|
2583
|
+
def remaining(self) -> float:
|
2584
|
+
"""Returns the time (in seconds) remaining until the timeout expires. May be negative and/or infinite."""
|
2585
|
+
|
2586
|
+
raise NotImplementedError
|
2587
|
+
|
2588
|
+
@abc.abstractmethod
|
2589
|
+
def __call__(self) -> float:
|
2590
|
+
"""Returns the time (in seconds) remaining until the timeout expires, or raises if the timeout has expired."""
|
2591
|
+
|
2592
|
+
raise NotImplementedError
|
2593
|
+
|
2594
|
+
@abc.abstractmethod
|
2595
|
+
def or_(self, o: ta.Any) -> ta.Any:
|
2596
|
+
"""Evaluates time remaining via remaining() if this timeout can expire, otherwise returns `o`."""
|
2597
|
+
|
2598
|
+
raise NotImplementedError
|
2599
|
+
|
2600
|
+
#
|
2601
|
+
|
2602
|
+
@classmethod
|
2603
|
+
def _now(cls) -> float:
|
2604
|
+
return time.monotonic()
|
2605
|
+
|
2606
|
+
#
|
2607
|
+
|
2608
|
+
class DEFAULT: # Noqa
|
2609
|
+
def __new__(cls, *args, **kwargs): # noqa
|
2610
|
+
raise TypeError
|
2611
|
+
|
2612
|
+
class _NOT_SPECIFIED: # noqa
|
2613
|
+
def __new__(cls, *args, **kwargs): # noqa
|
2614
|
+
raise TypeError
|
2615
|
+
|
2616
|
+
@classmethod
|
2617
|
+
def of(
|
2618
|
+
cls,
|
2619
|
+
obj: TimeoutLike,
|
2620
|
+
default: ta.Union[TimeoutLike, ta.Type[_NOT_SPECIFIED]] = _NOT_SPECIFIED,
|
2621
|
+
) -> 'Timeout':
|
2622
|
+
if obj is None:
|
2623
|
+
return InfiniteTimeout()
|
2624
|
+
|
2625
|
+
elif isinstance(obj, Timeout):
|
2626
|
+
return obj
|
2627
|
+
|
2628
|
+
elif isinstance(obj, (float, int)):
|
2629
|
+
return DeadlineTimeout(cls._now() + obj)
|
2630
|
+
|
2631
|
+
elif isinstance(obj, ta.Iterable):
|
2632
|
+
return CompositeTimeout(*[Timeout.of(c) for c in obj])
|
2633
|
+
|
2634
|
+
elif obj is Timeout.DEFAULT:
|
2635
|
+
if default is Timeout._NOT_SPECIFIED or default is Timeout.DEFAULT:
|
2636
|
+
raise RuntimeError('Must specify a default timeout')
|
2637
|
+
|
2638
|
+
else:
|
2639
|
+
return Timeout.of(default) # type: ignore[arg-type]
|
2640
|
+
|
2641
|
+
else:
|
2642
|
+
raise TypeError(obj)
|
2643
|
+
|
2644
|
+
@classmethod
|
2645
|
+
def of_deadline(cls, deadline: float) -> 'DeadlineTimeout':
|
2646
|
+
return DeadlineTimeout(deadline)
|
2647
|
+
|
2648
|
+
@classmethod
|
2649
|
+
def of_predicate(cls, expired_fn: ta.Callable[[], bool]) -> 'PredicateTimeout':
|
2650
|
+
return PredicateTimeout(expired_fn)
|
2651
|
+
|
2652
|
+
|
2653
|
+
class DeadlineTimeout(Timeout):
|
2654
|
+
def __init__(
|
2655
|
+
self,
|
2656
|
+
deadline: float,
|
2657
|
+
exc: ta.Union[ta.Type[BaseException], BaseException] = TimeoutError,
|
2658
|
+
) -> None:
|
2659
|
+
super().__init__()
|
2660
|
+
|
2661
|
+
self.deadline = deadline
|
2662
|
+
self.exc = exc
|
2663
|
+
|
2664
|
+
@property
|
2665
|
+
def can_expire(self) -> bool:
|
2666
|
+
return True
|
2667
|
+
|
2668
|
+
def expired(self) -> bool:
|
2669
|
+
return not (self.remaining() > 0)
|
2670
|
+
|
2671
|
+
def remaining(self) -> float:
|
2672
|
+
return self.deadline - self._now()
|
2673
|
+
|
2674
|
+
def __call__(self) -> float:
|
2675
|
+
if (rem := self.remaining()) > 0:
|
2676
|
+
return rem
|
2677
|
+
raise self.exc
|
2678
|
+
|
2679
|
+
def or_(self, o: ta.Any) -> ta.Any:
|
2680
|
+
return self()
|
2681
|
+
|
2682
|
+
|
2683
|
+
class InfiniteTimeout(Timeout):
|
2684
|
+
@property
|
2685
|
+
def can_expire(self) -> bool:
|
2686
|
+
return False
|
2687
|
+
|
2688
|
+
def expired(self) -> bool:
|
2689
|
+
return False
|
2690
|
+
|
2691
|
+
def remaining(self) -> float:
|
2692
|
+
return float('inf')
|
2693
|
+
|
2694
|
+
def __call__(self) -> float:
|
2695
|
+
return float('inf')
|
2696
|
+
|
2697
|
+
def or_(self, o: ta.Any) -> ta.Any:
|
2698
|
+
return o
|
2699
|
+
|
2700
|
+
|
2701
|
+
class CompositeTimeout(Timeout):
|
2702
|
+
def __init__(self, *children: Timeout) -> None:
|
2703
|
+
super().__init__()
|
2704
|
+
|
2705
|
+
self.children = children
|
2706
|
+
|
2707
|
+
@property
|
2708
|
+
def can_expire(self) -> bool:
|
2709
|
+
return any(c.can_expire for c in self.children)
|
2710
|
+
|
2711
|
+
def expired(self) -> bool:
|
2712
|
+
return any(c.expired() for c in self.children)
|
2713
|
+
|
2714
|
+
def remaining(self) -> float:
|
2715
|
+
return min(c.remaining() for c in self.children)
|
2716
|
+
|
2717
|
+
def __call__(self) -> float:
|
2718
|
+
return min(c() for c in self.children)
|
2719
|
+
|
2720
|
+
def or_(self, o: ta.Any) -> ta.Any:
|
2721
|
+
if self.can_expire:
|
2722
|
+
return self()
|
2723
|
+
return o
|
2724
|
+
|
2725
|
+
|
2726
|
+
class PredicateTimeout(Timeout):
|
2727
|
+
def __init__(
|
2728
|
+
self,
|
2729
|
+
expired_fn: ta.Callable[[], bool],
|
2730
|
+
exc: ta.Union[ta.Type[BaseException], BaseException] = TimeoutError,
|
2731
|
+
) -> None:
|
2732
|
+
super().__init__()
|
2733
|
+
|
2734
|
+
self.expired_fn = expired_fn
|
2735
|
+
self.exc = exc
|
2736
|
+
|
2737
|
+
@property
|
2738
|
+
def can_expire(self) -> bool:
|
2739
|
+
return True
|
2740
|
+
|
2741
|
+
def expired(self) -> bool:
|
2742
|
+
return self.expired_fn()
|
2743
|
+
|
2744
|
+
def remaining(self) -> float:
|
2745
|
+
return float('inf')
|
2746
|
+
|
2747
|
+
def __call__(self) -> float:
|
2748
|
+
if not self.expired_fn():
|
2749
|
+
return float('inf')
|
2750
|
+
raise self.exc
|
2751
|
+
|
2752
|
+
def or_(self, o: ta.Any) -> ta.Any:
|
2753
|
+
return self()
|
2754
|
+
|
2755
|
+
|
2756
|
+
########################################
|
2757
|
+
# ../../../omlish/logs/json.py
|
2758
|
+
"""
|
2759
|
+
TODO:
|
2760
|
+
- translate json keys
|
2761
|
+
"""
|
2762
|
+
|
2763
|
+
|
2764
|
+
##
|
2765
|
+
|
2766
|
+
|
2767
|
+
class JsonLogFormatter(logging.Formatter):
|
2768
|
+
KEYS: ta.Mapping[str, bool] = {
|
2769
|
+
'name': False,
|
2770
|
+
'msg': False,
|
2771
|
+
'args': False,
|
2772
|
+
'levelname': False,
|
2773
|
+
'levelno': False,
|
2774
|
+
'pathname': False,
|
2775
|
+
'filename': False,
|
2776
|
+
'module': False,
|
2777
|
+
'exc_info': True,
|
2778
|
+
'exc_text': True,
|
2779
|
+
'stack_info': True,
|
2780
|
+
'lineno': False,
|
2781
|
+
'funcName': False,
|
2782
|
+
'created': False,
|
2783
|
+
'msecs': False,
|
2784
|
+
'relativeCreated': False,
|
2785
|
+
'thread': False,
|
2786
|
+
'threadName': False,
|
2787
|
+
'processName': False,
|
2788
|
+
'process': False,
|
2789
|
+
}
|
2790
|
+
|
2791
|
+
def __init__(
|
2792
|
+
self,
|
2793
|
+
*args: ta.Any,
|
2794
|
+
json_dumps: ta.Optional[ta.Callable[[ta.Any], str]] = None,
|
2795
|
+
**kwargs: ta.Any,
|
2796
|
+
) -> None:
|
2797
|
+
super().__init__(*args, **kwargs)
|
2798
|
+
|
2799
|
+
if json_dumps is None:
|
2800
|
+
json_dumps = json_dumps_compact
|
2801
|
+
self._json_dumps = json_dumps
|
2802
|
+
|
2803
|
+
def format(self, record: logging.LogRecord) -> str:
|
2804
|
+
dct = {
|
2805
|
+
k: v
|
2806
|
+
for k, o in self.KEYS.items()
|
2807
|
+
for v in [getattr(record, k)]
|
2808
|
+
if not (o and v is None)
|
2809
|
+
}
|
2810
|
+
return self._json_dumps(dct)
|
2811
|
+
|
2812
|
+
|
2813
|
+
########################################
|
2814
|
+
# ../types.py
|
2815
|
+
|
2816
|
+
|
2817
|
+
##
|
2818
|
+
|
2819
|
+
|
2820
|
+
# See https://peps.python.org/pep-3149/
|
2821
|
+
INTERP_OPT_GLYPHS_BY_ATTR: ta.Mapping[str, str] = collections.OrderedDict([
|
2822
|
+
('debug', 'd'),
|
2823
|
+
('threaded', 't'),
|
2824
|
+
])
|
2825
|
+
|
2826
|
+
INTERP_OPT_ATTRS_BY_GLYPH: ta.Mapping[str, str] = collections.OrderedDict(
|
2827
|
+
(g, a) for a, g in INTERP_OPT_GLYPHS_BY_ATTR.items()
|
2828
|
+
)
|
2547
2829
|
|
2548
|
-
else:
|
2549
|
-
raise TypeError(obj)
|
2550
2830
|
|
2551
|
-
|
2831
|
+
@dc.dataclass(frozen=True)
|
2832
|
+
class InterpOpts:
|
2833
|
+
threaded: bool = False
|
2834
|
+
debug: bool = False
|
2552
2835
|
|
2553
|
-
|
2836
|
+
def __str__(self) -> str:
|
2837
|
+
return ''.join(g for a, g in INTERP_OPT_GLYPHS_BY_ATTR.items() if getattr(self, a))
|
2554
2838
|
|
2555
2839
|
@classmethod
|
2556
|
-
def
|
2557
|
-
return cls
|
2840
|
+
def parse(cls, s: str) -> 'InterpOpts':
|
2841
|
+
return cls(**{INTERP_OPT_ATTRS_BY_GLYPH[g]: True for g in s})
|
2558
2842
|
|
2559
|
-
@
|
2560
|
-
def
|
2561
|
-
|
2843
|
+
@classmethod
|
2844
|
+
def parse_suffix(cls, s: str) -> ta.Tuple[str, 'InterpOpts']:
|
2845
|
+
kw = {}
|
2846
|
+
while s and (a := INTERP_OPT_ATTRS_BY_GLYPH.get(s[-1])):
|
2847
|
+
s, kw[a] = s[:-1], True
|
2848
|
+
return s, cls(**kw)
|
2562
2849
|
|
2563
|
-
@property
|
2564
|
-
def args(self) -> argparse.Namespace:
|
2565
|
-
return self._args
|
2566
2850
|
|
2567
|
-
|
2568
|
-
def unknown_args(self) -> ta.Sequence[str]:
|
2569
|
-
return self._unknown_args
|
2851
|
+
##
|
2570
2852
|
|
2571
|
-
#
|
2572
2853
|
|
2573
|
-
|
2574
|
-
|
2854
|
+
@dc.dataclass(frozen=True)
|
2855
|
+
class InterpVersion:
|
2856
|
+
version: Version
|
2857
|
+
opts: InterpOpts
|
2575
2858
|
|
2576
|
-
def
|
2577
|
-
|
2859
|
+
def __str__(self) -> str:
|
2860
|
+
return str(self.version) + str(self.opts)
|
2578
2861
|
|
2579
|
-
|
2580
|
-
|
2581
|
-
|
2582
|
-
|
2583
|
-
|
2584
|
-
|
2862
|
+
@classmethod
|
2863
|
+
def parse(cls, s: str) -> 'InterpVersion':
|
2864
|
+
s, o = InterpOpts.parse_suffix(s)
|
2865
|
+
v = Version(s)
|
2866
|
+
return cls(
|
2867
|
+
version=v,
|
2868
|
+
opts=o,
|
2869
|
+
)
|
2585
2870
|
|
2586
|
-
|
2587
|
-
|
2871
|
+
@classmethod
|
2872
|
+
def try_parse(cls, s: str) -> ta.Optional['InterpVersion']:
|
2873
|
+
try:
|
2874
|
+
return cls.parse(s)
|
2875
|
+
except (KeyError, InvalidVersion):
|
2588
2876
|
return None
|
2589
2877
|
|
2590
|
-
return self._bind_cli_cmd(cmd)
|
2591
2878
|
|
2592
|
-
|
2879
|
+
##
|
2593
2880
|
|
2594
|
-
def cli_run(self) -> ta.Optional[int]:
|
2595
|
-
if (fn := self.prepare_cli_run()) is None:
|
2596
|
-
return 0
|
2597
2881
|
|
2598
|
-
|
2882
|
+
@dc.dataclass(frozen=True)
|
2883
|
+
class InterpSpecifier:
|
2884
|
+
specifier: Specifier
|
2885
|
+
opts: InterpOpts
|
2599
2886
|
|
2600
|
-
def
|
2601
|
-
|
2887
|
+
def __str__(self) -> str:
|
2888
|
+
return str(self.specifier) + str(self.opts)
|
2602
2889
|
|
2603
|
-
|
2604
|
-
|
2605
|
-
|
2606
|
-
|
2607
|
-
|
2890
|
+
@classmethod
|
2891
|
+
def parse(cls, s: str) -> 'InterpSpecifier':
|
2892
|
+
s, o = InterpOpts.parse_suffix(s)
|
2893
|
+
if not any(s.startswith(o) for o in Specifier.OPERATORS):
|
2894
|
+
s = '~=' + s
|
2895
|
+
if s.count('.') < 2:
|
2896
|
+
s += '.0'
|
2897
|
+
return cls(
|
2898
|
+
specifier=Specifier(s),
|
2899
|
+
opts=o,
|
2900
|
+
)
|
2608
2901
|
|
2609
|
-
|
2902
|
+
def contains(self, iv: InterpVersion) -> bool:
|
2903
|
+
return self.specifier.contains(iv.version) and self.opts == iv.opts
|
2610
2904
|
|
2611
|
-
|
2612
|
-
|
2613
|
-
*,
|
2614
|
-
force_async: bool = False,
|
2615
|
-
) -> ta.Optional[int]:
|
2616
|
-
if (fn := self.prepare_cli_run()) is None:
|
2617
|
-
return 0
|
2905
|
+
def __contains__(self, iv: InterpVersion) -> bool:
|
2906
|
+
return self.contains(iv)
|
2618
2907
|
|
2619
|
-
if force_async:
|
2620
|
-
is_async = True
|
2621
|
-
else:
|
2622
|
-
tfn = fn
|
2623
|
-
if isinstance(tfn, ArgparseCmd):
|
2624
|
-
tfn = tfn.fn
|
2625
|
-
is_async = inspect.iscoroutinefunction(tfn)
|
2626
2908
|
|
2627
|
-
|
2628
|
-
|
2629
|
-
|
2630
|
-
|
2909
|
+
##
|
2910
|
+
|
2911
|
+
|
2912
|
+
@dc.dataclass(frozen=True)
|
2913
|
+
class Interp:
|
2914
|
+
exe: str
|
2915
|
+
version: InterpVersion
|
2631
2916
|
|
2632
2917
|
|
2633
2918
|
########################################
|
@@ -2680,7 +2965,7 @@ def check_valid_injector_key_cls(cls: T) -> T:
|
|
2680
2965
|
##
|
2681
2966
|
|
2682
2967
|
|
2683
|
-
class InjectorProvider(
|
2968
|
+
class InjectorProvider(Abstract):
|
2684
2969
|
@abc.abstractmethod
|
2685
2970
|
def provider_fn(self) -> InjectorProviderFn:
|
2686
2971
|
raise NotImplementedError
|
@@ -2699,7 +2984,7 @@ class InjectorBinding:
|
|
2699
2984
|
check.isinstance(self.provider, InjectorProvider)
|
2700
2985
|
|
2701
2986
|
|
2702
|
-
class InjectorBindings(
|
2987
|
+
class InjectorBindings(Abstract):
|
2703
2988
|
@abc.abstractmethod
|
2704
2989
|
def bindings(self) -> ta.Iterator[InjectorBinding]:
|
2705
2990
|
raise NotImplementedError
|
@@ -2707,7 +2992,7 @@ class InjectorBindings(abc.ABC):
|
|
2707
2992
|
##
|
2708
2993
|
|
2709
2994
|
|
2710
|
-
class Injector(
|
2995
|
+
class Injector(Abstract):
|
2711
2996
|
@abc.abstractmethod
|
2712
2997
|
def try_provide(self, key: ta.Any) -> Maybe[ta.Any]:
|
2713
2998
|
raise NotImplementedError
|
@@ -2966,14 +3251,12 @@ def injector_override(p: InjectorBindings, *args: InjectorBindingOrBindings) ->
|
|
2966
3251
|
# scopes
|
2967
3252
|
|
2968
3253
|
|
2969
|
-
class InjectorScope(
|
3254
|
+
class InjectorScope(Abstract):
|
2970
3255
|
def __init__(
|
2971
3256
|
self,
|
2972
3257
|
*,
|
2973
3258
|
_i: Injector,
|
2974
3259
|
) -> None:
|
2975
|
-
check.not_in(abc.ABC, type(self).__bases__)
|
2976
|
-
|
2977
3260
|
super().__init__()
|
2978
3261
|
|
2979
3262
|
self._i = _i
|
@@ -3004,7 +3287,7 @@ class InjectorScope(abc.ABC): # noqa
|
|
3004
3287
|
raise NotImplementedError
|
3005
3288
|
|
3006
3289
|
|
3007
|
-
class ExclusiveInjectorScope(InjectorScope,
|
3290
|
+
class ExclusiveInjectorScope(InjectorScope, Abstract):
|
3008
3291
|
_st: ta.Optional[InjectorScope.State] = None
|
3009
3292
|
|
3010
3293
|
def state(self) -> InjectorScope.State:
|
@@ -3020,12 +3303,13 @@ class ExclusiveInjectorScope(InjectorScope, abc.ABC):
|
|
3020
3303
|
self._st = None
|
3021
3304
|
|
3022
3305
|
|
3023
|
-
class ContextvarInjectorScope(InjectorScope,
|
3306
|
+
class ContextvarInjectorScope(InjectorScope, Abstract):
|
3024
3307
|
_cv: contextvars.ContextVar
|
3025
3308
|
|
3026
3309
|
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
3027
3310
|
super().__init_subclass__(**kwargs)
|
3028
3311
|
|
3312
|
+
check.not_in(Abstract, cls.__bases__)
|
3029
3313
|
check.not_in(abc.ABC, cls.__bases__)
|
3030
3314
|
check.state(not hasattr(cls, '_cv'))
|
3031
3315
|
cls._cv = contextvars.ContextVar(f'{cls.__name__}_cv')
|
@@ -3531,7 +3815,7 @@ class InjectorBinder:
|
|
3531
3815
|
pws: ta.List[ta.Any] = []
|
3532
3816
|
if in_ is not None:
|
3533
3817
|
check.issubclass(in_, InjectorScope)
|
3534
|
-
check.not_in(
|
3818
|
+
check.not_in(Abstract, in_.__bases__)
|
3535
3819
|
pws.append(functools.partial(ScopedInjectorProvider, k=key, sc=in_))
|
3536
3820
|
if singleton:
|
3537
3821
|
pws.append(SingletonInjectorProvider)
|
@@ -3732,80 +4016,125 @@ inj = InjectionApi()
|
|
3732
4016
|
|
3733
4017
|
|
3734
4018
|
########################################
|
3735
|
-
# ../../../omlish/
|
4019
|
+
# ../../../omlish/logs/standard.py
|
4020
|
+
"""
|
4021
|
+
TODO:
|
4022
|
+
- structured
|
4023
|
+
- prefixed
|
4024
|
+
- debug
|
4025
|
+
- optional noisy? noisy will never be lite - some kinda configure_standard callback mechanism?
|
4026
|
+
"""
|
3736
4027
|
|
3737
4028
|
|
3738
4029
|
##
|
3739
4030
|
|
3740
4031
|
|
3741
|
-
|
3742
|
-
|
3743
|
-
|
4032
|
+
STANDARD_LOG_FORMAT_PARTS = [
|
4033
|
+
('asctime', '%(asctime)-15s'),
|
4034
|
+
('process', 'pid=%(process)s'),
|
4035
|
+
('thread', 'tid=%(thread)x'),
|
4036
|
+
('levelname', '%(levelname)s'),
|
4037
|
+
('name', '%(name)s'),
|
4038
|
+
('separator', '::'),
|
4039
|
+
('message', '%(message)s'),
|
4040
|
+
]
|
3744
4041
|
|
3745
4042
|
|
3746
|
-
|
4043
|
+
class StandardLogFormatter(logging.Formatter):
|
4044
|
+
@staticmethod
|
4045
|
+
def build_log_format(parts: ta.Iterable[ta.Tuple[str, str]]) -> str:
|
4046
|
+
return ' '.join(v for k, v in parts)
|
3747
4047
|
|
4048
|
+
converter = datetime.datetime.fromtimestamp # type: ignore
|
3748
4049
|
|
3749
|
-
def
|
3750
|
-
|
3751
|
-
|
4050
|
+
def formatTime(self, record, datefmt=None):
|
4051
|
+
ct = self.converter(record.created)
|
4052
|
+
if datefmt:
|
4053
|
+
return ct.strftime(datefmt) # noqa
|
4054
|
+
else:
|
4055
|
+
t = ct.strftime('%Y-%m-%d %H:%M:%S')
|
4056
|
+
return '%s.%03d' % (t, record.msecs) # noqa
|
3752
4057
|
|
3753
4058
|
|
3754
|
-
|
3755
|
-
|
3756
|
-
|
3757
|
-
|
3758
|
-
|
3759
|
-
|
4059
|
+
##
|
4060
|
+
|
4061
|
+
|
4062
|
+
class StandardConfiguredLogHandler(ProxyLogHandler):
|
4063
|
+
def __init_subclass__(cls, **kwargs):
|
4064
|
+
raise TypeError('This class serves only as a marker and should not be subclassed.')
|
3760
4065
|
|
3761
4066
|
|
3762
4067
|
##
|
3763
4068
|
|
3764
4069
|
|
3765
|
-
|
3766
|
-
|
3767
|
-
|
3768
|
-
|
3769
|
-
|
3770
|
-
|
3771
|
-
|
3772
|
-
|
3773
|
-
|
3774
|
-
|
3775
|
-
|
3776
|
-
|
3777
|
-
|
3778
|
-
|
3779
|
-
|
3780
|
-
'
|
3781
|
-
|
3782
|
-
|
3783
|
-
|
3784
|
-
|
3785
|
-
|
3786
|
-
|
3787
|
-
|
4070
|
+
@contextlib.contextmanager
|
4071
|
+
def _locking_logging_module_lock() -> ta.Iterator[None]:
|
4072
|
+
if hasattr(logging, '_acquireLock'):
|
4073
|
+
logging._acquireLock() # noqa
|
4074
|
+
try:
|
4075
|
+
yield
|
4076
|
+
finally:
|
4077
|
+
logging._releaseLock() # type: ignore # noqa
|
4078
|
+
|
4079
|
+
elif hasattr(logging, '_lock'):
|
4080
|
+
# https://github.com/python/cpython/commit/74723e11109a320e628898817ab449b3dad9ee96
|
4081
|
+
with logging._lock: # noqa
|
4082
|
+
yield
|
4083
|
+
|
4084
|
+
else:
|
4085
|
+
raise Exception("Can't find lock in logging module")
|
4086
|
+
|
4087
|
+
|
4088
|
+
def configure_standard_logging(
|
4089
|
+
level: ta.Union[int, str] = logging.INFO,
|
4090
|
+
*,
|
4091
|
+
json: bool = False,
|
4092
|
+
target: ta.Optional[logging.Logger] = None,
|
4093
|
+
force: bool = False,
|
4094
|
+
handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
|
4095
|
+
) -> ta.Optional[StandardConfiguredLogHandler]:
|
4096
|
+
with _locking_logging_module_lock():
|
4097
|
+
if target is None:
|
4098
|
+
target = logging.root
|
4099
|
+
|
4100
|
+
#
|
4101
|
+
|
4102
|
+
if not force:
|
4103
|
+
if any(isinstance(h, StandardConfiguredLogHandler) for h in list(target.handlers)):
|
4104
|
+
return None
|
4105
|
+
|
4106
|
+
#
|
4107
|
+
|
4108
|
+
if handler_factory is not None:
|
4109
|
+
handler = handler_factory()
|
4110
|
+
else:
|
4111
|
+
handler = logging.StreamHandler()
|
4112
|
+
|
4113
|
+
#
|
4114
|
+
|
4115
|
+
formatter: logging.Formatter
|
4116
|
+
if json:
|
4117
|
+
formatter = JsonLogFormatter()
|
4118
|
+
else:
|
4119
|
+
formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
|
4120
|
+
handler.setFormatter(formatter)
|
4121
|
+
|
4122
|
+
#
|
4123
|
+
|
4124
|
+
handler.addFilter(TidLogFilter())
|
3788
4125
|
|
3789
|
-
|
3790
|
-
self,
|
3791
|
-
*args: ta.Any,
|
3792
|
-
json_dumps: ta.Optional[ta.Callable[[ta.Any], str]] = None,
|
3793
|
-
**kwargs: ta.Any,
|
3794
|
-
) -> None:
|
3795
|
-
super().__init__(*args, **kwargs)
|
4126
|
+
#
|
3796
4127
|
|
3797
|
-
|
3798
|
-
json_dumps = json_dumps_compact
|
3799
|
-
self._json_dumps = json_dumps
|
4128
|
+
target.addHandler(handler)
|
3800
4129
|
|
3801
|
-
|
3802
|
-
|
3803
|
-
|
3804
|
-
|
3805
|
-
|
3806
|
-
|
3807
|
-
|
3808
|
-
return
|
4130
|
+
#
|
4131
|
+
|
4132
|
+
if level is not None:
|
4133
|
+
target.setLevel(level)
|
4134
|
+
|
4135
|
+
#
|
4136
|
+
|
4137
|
+
return StandardConfiguredLogHandler(handler)
|
3809
4138
|
|
3810
4139
|
|
3811
4140
|
########################################
|
@@ -3924,7 +4253,7 @@ SubprocessRun._FIELD_NAMES = frozenset(fld.name for fld in dc.fields(SubprocessR
|
|
3924
4253
|
##
|
3925
4254
|
|
3926
4255
|
|
3927
|
-
class SubprocessRunnable(
|
4256
|
+
class SubprocessRunnable(Abstract, ta.Generic[T]):
|
3928
4257
|
@abc.abstractmethod
|
3929
4258
|
def make_run(self) -> SubprocessRun:
|
3930
4259
|
raise NotImplementedError
|
@@ -3957,233 +4286,6 @@ class SubprocessRunnable(abc.ABC, ta.Generic[T]):
|
|
3957
4286
|
return self.handle_run_output(await self.make_run().maysync_run(maysync_subprocesses, **kwargs))
|
3958
4287
|
|
3959
4288
|
|
3960
|
-
########################################
|
3961
|
-
# ../types.py
|
3962
|
-
|
3963
|
-
|
3964
|
-
##
|
3965
|
-
|
3966
|
-
|
3967
|
-
# See https://peps.python.org/pep-3149/
|
3968
|
-
INTERP_OPT_GLYPHS_BY_ATTR: ta.Mapping[str, str] = collections.OrderedDict([
|
3969
|
-
('debug', 'd'),
|
3970
|
-
('threaded', 't'),
|
3971
|
-
])
|
3972
|
-
|
3973
|
-
INTERP_OPT_ATTRS_BY_GLYPH: ta.Mapping[str, str] = collections.OrderedDict(
|
3974
|
-
(g, a) for a, g in INTERP_OPT_GLYPHS_BY_ATTR.items()
|
3975
|
-
)
|
3976
|
-
|
3977
|
-
|
3978
|
-
@dc.dataclass(frozen=True)
|
3979
|
-
class InterpOpts:
|
3980
|
-
threaded: bool = False
|
3981
|
-
debug: bool = False
|
3982
|
-
|
3983
|
-
def __str__(self) -> str:
|
3984
|
-
return ''.join(g for a, g in INTERP_OPT_GLYPHS_BY_ATTR.items() if getattr(self, a))
|
3985
|
-
|
3986
|
-
@classmethod
|
3987
|
-
def parse(cls, s: str) -> 'InterpOpts':
|
3988
|
-
return cls(**{INTERP_OPT_ATTRS_BY_GLYPH[g]: True for g in s})
|
3989
|
-
|
3990
|
-
@classmethod
|
3991
|
-
def parse_suffix(cls, s: str) -> ta.Tuple[str, 'InterpOpts']:
|
3992
|
-
kw = {}
|
3993
|
-
while s and (a := INTERP_OPT_ATTRS_BY_GLYPH.get(s[-1])):
|
3994
|
-
s, kw[a] = s[:-1], True
|
3995
|
-
return s, cls(**kw)
|
3996
|
-
|
3997
|
-
|
3998
|
-
##
|
3999
|
-
|
4000
|
-
|
4001
|
-
@dc.dataclass(frozen=True)
|
4002
|
-
class InterpVersion:
|
4003
|
-
version: Version
|
4004
|
-
opts: InterpOpts
|
4005
|
-
|
4006
|
-
def __str__(self) -> str:
|
4007
|
-
return str(self.version) + str(self.opts)
|
4008
|
-
|
4009
|
-
@classmethod
|
4010
|
-
def parse(cls, s: str) -> 'InterpVersion':
|
4011
|
-
s, o = InterpOpts.parse_suffix(s)
|
4012
|
-
v = Version(s)
|
4013
|
-
return cls(
|
4014
|
-
version=v,
|
4015
|
-
opts=o,
|
4016
|
-
)
|
4017
|
-
|
4018
|
-
@classmethod
|
4019
|
-
def try_parse(cls, s: str) -> ta.Optional['InterpVersion']:
|
4020
|
-
try:
|
4021
|
-
return cls.parse(s)
|
4022
|
-
except (KeyError, InvalidVersion):
|
4023
|
-
return None
|
4024
|
-
|
4025
|
-
|
4026
|
-
##
|
4027
|
-
|
4028
|
-
|
4029
|
-
@dc.dataclass(frozen=True)
|
4030
|
-
class InterpSpecifier:
|
4031
|
-
specifier: Specifier
|
4032
|
-
opts: InterpOpts
|
4033
|
-
|
4034
|
-
def __str__(self) -> str:
|
4035
|
-
return str(self.specifier) + str(self.opts)
|
4036
|
-
|
4037
|
-
@classmethod
|
4038
|
-
def parse(cls, s: str) -> 'InterpSpecifier':
|
4039
|
-
s, o = InterpOpts.parse_suffix(s)
|
4040
|
-
if not any(s.startswith(o) for o in Specifier.OPERATORS):
|
4041
|
-
s = '~=' + s
|
4042
|
-
if s.count('.') < 2:
|
4043
|
-
s += '.0'
|
4044
|
-
return cls(
|
4045
|
-
specifier=Specifier(s),
|
4046
|
-
opts=o,
|
4047
|
-
)
|
4048
|
-
|
4049
|
-
def contains(self, iv: InterpVersion) -> bool:
|
4050
|
-
return self.specifier.contains(iv.version) and self.opts == iv.opts
|
4051
|
-
|
4052
|
-
def __contains__(self, iv: InterpVersion) -> bool:
|
4053
|
-
return self.contains(iv)
|
4054
|
-
|
4055
|
-
|
4056
|
-
##
|
4057
|
-
|
4058
|
-
|
4059
|
-
@dc.dataclass(frozen=True)
|
4060
|
-
class Interp:
|
4061
|
-
exe: str
|
4062
|
-
version: InterpVersion
|
4063
|
-
|
4064
|
-
|
4065
|
-
########################################
|
4066
|
-
# ../../../omlish/logs/standard.py
|
4067
|
-
"""
|
4068
|
-
TODO:
|
4069
|
-
- structured
|
4070
|
-
- prefixed
|
4071
|
-
- debug
|
4072
|
-
- optional noisy? noisy will never be lite - some kinda configure_standard callback mechanism?
|
4073
|
-
"""
|
4074
|
-
|
4075
|
-
|
4076
|
-
##
|
4077
|
-
|
4078
|
-
|
4079
|
-
STANDARD_LOG_FORMAT_PARTS = [
|
4080
|
-
('asctime', '%(asctime)-15s'),
|
4081
|
-
('process', 'pid=%(process)s'),
|
4082
|
-
('thread', 'tid=%(thread)x'),
|
4083
|
-
('levelname', '%(levelname)s'),
|
4084
|
-
('name', '%(name)s'),
|
4085
|
-
('separator', '::'),
|
4086
|
-
('message', '%(message)s'),
|
4087
|
-
]
|
4088
|
-
|
4089
|
-
|
4090
|
-
class StandardLogFormatter(logging.Formatter):
|
4091
|
-
@staticmethod
|
4092
|
-
def build_log_format(parts: ta.Iterable[ta.Tuple[str, str]]) -> str:
|
4093
|
-
return ' '.join(v for k, v in parts)
|
4094
|
-
|
4095
|
-
converter = datetime.datetime.fromtimestamp # type: ignore
|
4096
|
-
|
4097
|
-
def formatTime(self, record, datefmt=None):
|
4098
|
-
ct = self.converter(record.created)
|
4099
|
-
if datefmt:
|
4100
|
-
return ct.strftime(datefmt) # noqa
|
4101
|
-
else:
|
4102
|
-
t = ct.strftime('%Y-%m-%d %H:%M:%S')
|
4103
|
-
return '%s.%03d' % (t, record.msecs) # noqa
|
4104
|
-
|
4105
|
-
|
4106
|
-
##
|
4107
|
-
|
4108
|
-
|
4109
|
-
class StandardConfiguredLogHandler(ProxyLogHandler):
|
4110
|
-
def __init_subclass__(cls, **kwargs):
|
4111
|
-
raise TypeError('This class serves only as a marker and should not be subclassed.')
|
4112
|
-
|
4113
|
-
|
4114
|
-
##
|
4115
|
-
|
4116
|
-
|
4117
|
-
@contextlib.contextmanager
|
4118
|
-
def _locking_logging_module_lock() -> ta.Iterator[None]:
|
4119
|
-
if hasattr(logging, '_acquireLock'):
|
4120
|
-
logging._acquireLock() # noqa
|
4121
|
-
try:
|
4122
|
-
yield
|
4123
|
-
finally:
|
4124
|
-
logging._releaseLock() # type: ignore # noqa
|
4125
|
-
|
4126
|
-
elif hasattr(logging, '_lock'):
|
4127
|
-
# https://github.com/python/cpython/commit/74723e11109a320e628898817ab449b3dad9ee96
|
4128
|
-
with logging._lock: # noqa
|
4129
|
-
yield
|
4130
|
-
|
4131
|
-
else:
|
4132
|
-
raise Exception("Can't find lock in logging module")
|
4133
|
-
|
4134
|
-
|
4135
|
-
def configure_standard_logging(
|
4136
|
-
level: ta.Union[int, str] = logging.INFO,
|
4137
|
-
*,
|
4138
|
-
json: bool = False,
|
4139
|
-
target: ta.Optional[logging.Logger] = None,
|
4140
|
-
force: bool = False,
|
4141
|
-
handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
|
4142
|
-
) -> ta.Optional[StandardConfiguredLogHandler]:
|
4143
|
-
with _locking_logging_module_lock():
|
4144
|
-
if target is None:
|
4145
|
-
target = logging.root
|
4146
|
-
|
4147
|
-
#
|
4148
|
-
|
4149
|
-
if not force:
|
4150
|
-
if any(isinstance(h, StandardConfiguredLogHandler) for h in list(target.handlers)):
|
4151
|
-
return None
|
4152
|
-
|
4153
|
-
#
|
4154
|
-
|
4155
|
-
if handler_factory is not None:
|
4156
|
-
handler = handler_factory()
|
4157
|
-
else:
|
4158
|
-
handler = logging.StreamHandler()
|
4159
|
-
|
4160
|
-
#
|
4161
|
-
|
4162
|
-
formatter: logging.Formatter
|
4163
|
-
if json:
|
4164
|
-
formatter = JsonLogFormatter()
|
4165
|
-
else:
|
4166
|
-
formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
|
4167
|
-
handler.setFormatter(formatter)
|
4168
|
-
|
4169
|
-
#
|
4170
|
-
|
4171
|
-
handler.addFilter(TidLogFilter())
|
4172
|
-
|
4173
|
-
#
|
4174
|
-
|
4175
|
-
target.addHandler(handler)
|
4176
|
-
|
4177
|
-
#
|
4178
|
-
|
4179
|
-
if level is not None:
|
4180
|
-
target.setLevel(level)
|
4181
|
-
|
4182
|
-
#
|
4183
|
-
|
4184
|
-
return StandardConfiguredLogHandler(handler)
|
4185
|
-
|
4186
|
-
|
4187
4289
|
########################################
|
4188
4290
|
# ../../../omlish/subprocesses/wrap.py
|
4189
4291
|
"""
|
@@ -4225,13 +4327,13 @@ TODO:
|
|
4225
4327
|
##
|
4226
4328
|
|
4227
4329
|
|
4228
|
-
class InterpProvider(
|
4330
|
+
class InterpProvider(Abstract):
|
4229
4331
|
name: ta.ClassVar[str]
|
4230
4332
|
|
4231
4333
|
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
4232
4334
|
super().__init_subclass__(**kwargs)
|
4233
4335
|
|
4234
|
-
if
|
4336
|
+
if Abstract not in cls.__bases__ and 'name' not in cls.__dict__:
|
4235
4337
|
sfx = 'InterpProvider'
|
4236
4338
|
if not cls.__name__.endswith(sfx):
|
4237
4339
|
raise NameError(cls)
|
@@ -4297,7 +4399,7 @@ class VerboseCalledProcessError(subprocess.CalledProcessError):
|
|
4297
4399
|
return msg
|
4298
4400
|
|
4299
4401
|
|
4300
|
-
class BaseSubprocesses(
|
4402
|
+
class BaseSubprocesses(Abstract):
|
4301
4403
|
DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = None
|
4302
4404
|
|
4303
4405
|
def __init__(
|
@@ -4562,7 +4664,7 @@ class InterpResolver:
|
|
4562
4664
|
##
|
4563
4665
|
|
4564
4666
|
|
4565
|
-
class AbstractAsyncSubprocesses(BaseSubprocesses):
|
4667
|
+
class AbstractAsyncSubprocesses(BaseSubprocesses, Abstract):
|
4566
4668
|
@abc.abstractmethod
|
4567
4669
|
def run_(self, run: SubprocessRun) -> ta.Awaitable[SubprocessRunOutput]:
|
4568
4670
|
raise NotImplementedError
|
@@ -5328,7 +5430,7 @@ THREADED_PYENV_INSTALL_OPTS = PyenvInstallOpts(conf_opts=['--disable-gil'])
|
|
5328
5430
|
#
|
5329
5431
|
|
5330
5432
|
|
5331
|
-
class PyenvInstallOptsProvider(
|
5433
|
+
class PyenvInstallOptsProvider(Abstract):
|
5332
5434
|
@abc.abstractmethod
|
5333
5435
|
def opts(self) -> ta.Awaitable[PyenvInstallOpts]:
|
5334
5436
|
raise NotImplementedError
|