omdev 0.0.0.dev224__py3-none-any.whl → 0.0.0.dev226__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/amalg/typing.py +7 -1
- omdev/ci/requirements.py +1 -1
- omdev/git/revisions.py +2 -2
- omdev/git/shallow.py +1 -1
- omdev/git/status.py +1 -1
- omdev/precheck/lite.py +1 -1
- omdev/pyproject/pkg.py +1 -1
- omdev/scripts/ci.py +600 -296
- omdev/scripts/interp.py +501 -343
- omdev/scripts/pyproject.py +603 -299
- {omdev-0.0.0.dev224.dist-info → omdev-0.0.0.dev226.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev224.dist-info → omdev-0.0.0.dev226.dist-info}/RECORD +16 -16
- {omdev-0.0.0.dev224.dist-info → omdev-0.0.0.dev226.dist-info}/LICENSE +0 -0
- {omdev-0.0.0.dev224.dist-info → omdev-0.0.0.dev226.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev224.dist-info → omdev-0.0.0.dev226.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev224.dist-info → omdev-0.0.0.dev226.dist-info}/top_level.txt +0 -0
omdev/scripts/interp.py
CHANGED
@@ -57,9 +57,6 @@ VersionCmpLocalType = ta.Union['NegativeInfinityVersionType', _VersionCmpLocalTy
|
|
57
57
|
VersionCmpKey = ta.Tuple[int, ta.Tuple[int, ...], VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpLocalType] # noqa
|
58
58
|
VersionComparisonMethod = ta.Callable[[VersionCmpKey, VersionCmpKey], bool]
|
59
59
|
|
60
|
-
# ../../omlish/asyncs/asyncio/timeouts.py
|
61
|
-
AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
|
62
|
-
|
63
60
|
# ../../omlish/lite/cached.py
|
64
61
|
T = ta.TypeVar('T')
|
65
62
|
CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
|
@@ -72,6 +69,9 @@ CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
|
|
72
69
|
CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
|
73
70
|
CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
|
74
71
|
|
72
|
+
# ../../omlish/lite/timeouts.py
|
73
|
+
TimeoutLike = ta.Union['Timeout', 'Timeout.Default', ta.Iterable['TimeoutLike'], float] # ta.TypeAlias
|
74
|
+
|
75
75
|
# ../packaging/specifiers.py
|
76
76
|
UnparsedVersion = ta.Union['Version', str]
|
77
77
|
UnparsedVersionVar = ta.TypeVar('UnparsedVersionVar', bound=UnparsedVersion)
|
@@ -80,6 +80,9 @@ CallableVersionOperator = ta.Callable[['Version', str], bool]
|
|
80
80
|
# ../../omlish/argparse/cli.py
|
81
81
|
ArgparseCmdFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
|
82
82
|
|
83
|
+
# ../../omlish/asyncs/asyncio/timeouts.py
|
84
|
+
AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
|
85
|
+
|
83
86
|
# ../../omlish/lite/inject.py
|
84
87
|
U = ta.TypeVar('U')
|
85
88
|
InjectorKeyCls = ta.Union[type, ta.NewType]
|
@@ -87,7 +90,7 @@ InjectorProviderFn = ta.Callable[['Injector'], ta.Any]
|
|
87
90
|
InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
|
88
91
|
InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
89
92
|
|
90
|
-
# ../../omlish/subprocesses.py
|
93
|
+
# ../../omlish/subprocesses/base.py
|
91
94
|
SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
|
92
95
|
|
93
96
|
|
@@ -498,19 +501,6 @@ def canonicalize_version(
|
|
498
501
|
return ''.join(parts)
|
499
502
|
|
500
503
|
|
501
|
-
########################################
|
502
|
-
# ../../../omlish/asyncs/asyncio/timeouts.py
|
503
|
-
|
504
|
-
|
505
|
-
def asyncio_maybe_timeout(
|
506
|
-
fut: AwaitableT,
|
507
|
-
timeout: ta.Optional[float] = None,
|
508
|
-
) -> AwaitableT:
|
509
|
-
if timeout is not None:
|
510
|
-
fut = asyncio.wait_for(fut, timeout) # type: ignore
|
511
|
-
return fut
|
512
|
-
|
513
|
-
|
514
504
|
########################################
|
515
505
|
# ../../../omlish/lite/cached.py
|
516
506
|
|
@@ -1303,6 +1293,205 @@ def format_num_bytes(num_bytes: int) -> str:
|
|
1303
1293
|
return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
|
1304
1294
|
|
1305
1295
|
|
1296
|
+
########################################
|
1297
|
+
# ../../../omlish/lite/timeouts.py
|
1298
|
+
"""
|
1299
|
+
TODO:
|
1300
|
+
- Event (/ Predicate)
|
1301
|
+
"""
|
1302
|
+
|
1303
|
+
|
1304
|
+
##
|
1305
|
+
|
1306
|
+
|
1307
|
+
class Timeout(abc.ABC):
|
1308
|
+
@property
|
1309
|
+
@abc.abstractmethod
|
1310
|
+
def can_expire(self) -> bool:
|
1311
|
+
"""Indicates whether or not this timeout will ever expire."""
|
1312
|
+
|
1313
|
+
raise NotImplementedError
|
1314
|
+
|
1315
|
+
@abc.abstractmethod
|
1316
|
+
def expired(self) -> bool:
|
1317
|
+
"""Return whether or not this timeout has expired."""
|
1318
|
+
|
1319
|
+
raise NotImplementedError
|
1320
|
+
|
1321
|
+
@abc.abstractmethod
|
1322
|
+
def remaining(self) -> float:
|
1323
|
+
"""Returns the time (in seconds) remaining until the timeout expires. May be negative and/or infinite."""
|
1324
|
+
|
1325
|
+
raise NotImplementedError
|
1326
|
+
|
1327
|
+
@abc.abstractmethod
|
1328
|
+
def __call__(self) -> float:
|
1329
|
+
"""Returns the time (in seconds) remaining until the timeout expires, or raises if the timeout has expired."""
|
1330
|
+
|
1331
|
+
raise NotImplementedError
|
1332
|
+
|
1333
|
+
@abc.abstractmethod
|
1334
|
+
def or_(self, o: ta.Any) -> ta.Any:
|
1335
|
+
"""Evaluates time remaining via remaining() if this timeout can expire, otherwise returns `o`."""
|
1336
|
+
|
1337
|
+
raise NotImplementedError
|
1338
|
+
|
1339
|
+
#
|
1340
|
+
|
1341
|
+
@classmethod
|
1342
|
+
def _now(cls) -> float:
|
1343
|
+
return time.time()
|
1344
|
+
|
1345
|
+
#
|
1346
|
+
|
1347
|
+
class Default:
|
1348
|
+
def __new__(cls, *args, **kwargs): # noqa
|
1349
|
+
raise TypeError
|
1350
|
+
|
1351
|
+
class _NOT_SPECIFIED: # noqa
|
1352
|
+
def __new__(cls, *args, **kwargs): # noqa
|
1353
|
+
raise TypeError
|
1354
|
+
|
1355
|
+
@classmethod
|
1356
|
+
def of(
|
1357
|
+
cls,
|
1358
|
+
obj: ta.Optional[TimeoutLike],
|
1359
|
+
default: ta.Union[TimeoutLike, ta.Type[_NOT_SPECIFIED]] = _NOT_SPECIFIED,
|
1360
|
+
) -> 'Timeout':
|
1361
|
+
if obj is None:
|
1362
|
+
return InfiniteTimeout()
|
1363
|
+
|
1364
|
+
elif isinstance(obj, Timeout):
|
1365
|
+
return obj
|
1366
|
+
|
1367
|
+
elif isinstance(obj, (float, int)):
|
1368
|
+
return DeadlineTimeout(cls._now() + obj)
|
1369
|
+
|
1370
|
+
elif isinstance(obj, ta.Iterable):
|
1371
|
+
return CompositeTimeout(*[Timeout.of(c) for c in obj])
|
1372
|
+
|
1373
|
+
elif obj is Timeout.Default:
|
1374
|
+
if default is Timeout._NOT_SPECIFIED or default is Timeout.Default:
|
1375
|
+
raise RuntimeError('Must specify a default timeout')
|
1376
|
+
|
1377
|
+
else:
|
1378
|
+
return Timeout.of(default) # type: ignore[arg-type]
|
1379
|
+
|
1380
|
+
else:
|
1381
|
+
raise TypeError(obj)
|
1382
|
+
|
1383
|
+
@classmethod
|
1384
|
+
def of_deadline(cls, deadline: float) -> 'DeadlineTimeout':
|
1385
|
+
return DeadlineTimeout(deadline)
|
1386
|
+
|
1387
|
+
@classmethod
|
1388
|
+
def of_predicate(cls, expired_fn: ta.Callable[[], bool]) -> 'PredicateTimeout':
|
1389
|
+
return PredicateTimeout(expired_fn)
|
1390
|
+
|
1391
|
+
|
1392
|
+
class DeadlineTimeout(Timeout):
|
1393
|
+
def __init__(
|
1394
|
+
self,
|
1395
|
+
deadline: float,
|
1396
|
+
exc: ta.Union[ta.Type[BaseException], BaseException] = TimeoutError,
|
1397
|
+
) -> None:
|
1398
|
+
super().__init__()
|
1399
|
+
|
1400
|
+
self.deadline = deadline
|
1401
|
+
self.exc = exc
|
1402
|
+
|
1403
|
+
@property
|
1404
|
+
def can_expire(self) -> bool:
|
1405
|
+
return True
|
1406
|
+
|
1407
|
+
def expired(self) -> bool:
|
1408
|
+
return not (self.remaining() > 0)
|
1409
|
+
|
1410
|
+
def remaining(self) -> float:
|
1411
|
+
return self.deadline - self._now()
|
1412
|
+
|
1413
|
+
def __call__(self) -> float:
|
1414
|
+
if (rem := self.remaining()) > 0:
|
1415
|
+
return rem
|
1416
|
+
raise self.exc
|
1417
|
+
|
1418
|
+
def or_(self, o: ta.Any) -> ta.Any:
|
1419
|
+
return self()
|
1420
|
+
|
1421
|
+
|
1422
|
+
class InfiniteTimeout(Timeout):
|
1423
|
+
@property
|
1424
|
+
def can_expire(self) -> bool:
|
1425
|
+
return False
|
1426
|
+
|
1427
|
+
def expired(self) -> bool:
|
1428
|
+
return False
|
1429
|
+
|
1430
|
+
def remaining(self) -> float:
|
1431
|
+
return float('inf')
|
1432
|
+
|
1433
|
+
def __call__(self) -> float:
|
1434
|
+
return float('inf')
|
1435
|
+
|
1436
|
+
def or_(self, o: ta.Any) -> ta.Any:
|
1437
|
+
return o
|
1438
|
+
|
1439
|
+
|
1440
|
+
class CompositeTimeout(Timeout):
|
1441
|
+
def __init__(self, *children: Timeout) -> None:
|
1442
|
+
super().__init__()
|
1443
|
+
|
1444
|
+
self.children = children
|
1445
|
+
|
1446
|
+
@property
|
1447
|
+
def can_expire(self) -> bool:
|
1448
|
+
return any(c.can_expire for c in self.children)
|
1449
|
+
|
1450
|
+
def expired(self) -> bool:
|
1451
|
+
return any(c.expired() for c in self.children)
|
1452
|
+
|
1453
|
+
def remaining(self) -> float:
|
1454
|
+
return min(c.remaining() for c in self.children)
|
1455
|
+
|
1456
|
+
def __call__(self) -> float:
|
1457
|
+
return min(c() for c in self.children)
|
1458
|
+
|
1459
|
+
def or_(self, o: ta.Any) -> ta.Any:
|
1460
|
+
if self.can_expire:
|
1461
|
+
return self()
|
1462
|
+
return o
|
1463
|
+
|
1464
|
+
|
1465
|
+
class PredicateTimeout(Timeout):
|
1466
|
+
def __init__(
|
1467
|
+
self,
|
1468
|
+
expired_fn: ta.Callable[[], bool],
|
1469
|
+
exc: ta.Union[ta.Type[BaseException], BaseException] = TimeoutError,
|
1470
|
+
) -> None:
|
1471
|
+
super().__init__()
|
1472
|
+
|
1473
|
+
self.expired_fn = expired_fn
|
1474
|
+
self.exc = exc
|
1475
|
+
|
1476
|
+
@property
|
1477
|
+
def can_expire(self) -> bool:
|
1478
|
+
return True
|
1479
|
+
|
1480
|
+
def expired(self) -> bool:
|
1481
|
+
return self.expired_fn()
|
1482
|
+
|
1483
|
+
def remaining(self) -> float:
|
1484
|
+
return float('inf')
|
1485
|
+
|
1486
|
+
def __call__(self) -> float:
|
1487
|
+
if not self.expired_fn():
|
1488
|
+
return float('inf')
|
1489
|
+
raise self.exc
|
1490
|
+
|
1491
|
+
def or_(self, o: ta.Any) -> ta.Any:
|
1492
|
+
return self()
|
1493
|
+
|
1494
|
+
|
1306
1495
|
########################################
|
1307
1496
|
# ../../../omlish/logs/filters.py
|
1308
1497
|
|
@@ -2224,6 +2413,19 @@ class ArgparseCli:
|
|
2224
2413
|
return fn()
|
2225
2414
|
|
2226
2415
|
|
2416
|
+
########################################
|
2417
|
+
# ../../../omlish/asyncs/asyncio/timeouts.py
|
2418
|
+
|
2419
|
+
|
2420
|
+
def asyncio_maybe_timeout(
|
2421
|
+
fut: AwaitableT,
|
2422
|
+
timeout: ta.Optional[TimeoutLike] = None,
|
2423
|
+
) -> AwaitableT:
|
2424
|
+
if timeout is not None:
|
2425
|
+
fut = asyncio.wait_for(fut, Timeout.of(timeout)()) # type: ignore
|
2426
|
+
return fut
|
2427
|
+
|
2428
|
+
|
2227
2429
|
########################################
|
2228
2430
|
# ../../../omlish/lite/inject.py
|
2229
2431
|
|
@@ -3369,6 +3571,137 @@ class JsonLogFormatter(logging.Formatter):
|
|
3369
3571
|
return self._json_dumps(dct)
|
3370
3572
|
|
3371
3573
|
|
3574
|
+
########################################
|
3575
|
+
# ../../../omlish/subprocesses/run.py
|
3576
|
+
|
3577
|
+
|
3578
|
+
##
|
3579
|
+
|
3580
|
+
|
3581
|
+
@dc.dataclass(frozen=True)
|
3582
|
+
class SubprocessRunOutput(ta.Generic[T]):
|
3583
|
+
proc: T
|
3584
|
+
|
3585
|
+
returncode: int # noqa
|
3586
|
+
|
3587
|
+
stdout: ta.Optional[bytes] = None
|
3588
|
+
stderr: ta.Optional[bytes] = None
|
3589
|
+
|
3590
|
+
|
3591
|
+
##
|
3592
|
+
|
3593
|
+
|
3594
|
+
@dc.dataclass(frozen=True)
|
3595
|
+
class SubprocessRun:
|
3596
|
+
cmd: ta.Sequence[str]
|
3597
|
+
input: ta.Any = None
|
3598
|
+
timeout: ta.Optional[TimeoutLike] = None
|
3599
|
+
check: bool = False
|
3600
|
+
capture_output: ta.Optional[bool] = None
|
3601
|
+
kwargs: ta.Optional[ta.Mapping[str, ta.Any]] = None
|
3602
|
+
|
3603
|
+
#
|
3604
|
+
|
3605
|
+
_FIELD_NAMES: ta.ClassVar[ta.FrozenSet[str]]
|
3606
|
+
|
3607
|
+
def replace(self, **kwargs: ta.Any) -> 'SubprocessRun':
|
3608
|
+
if not kwargs:
|
3609
|
+
return self
|
3610
|
+
|
3611
|
+
field_kws = {}
|
3612
|
+
extra_kws = {}
|
3613
|
+
for k, v in kwargs.items():
|
3614
|
+
if k in self._FIELD_NAMES:
|
3615
|
+
field_kws[k] = v
|
3616
|
+
else:
|
3617
|
+
extra_kws[k] = v
|
3618
|
+
|
3619
|
+
return dc.replace(self, **{
|
3620
|
+
**dict(kwargs={
|
3621
|
+
**(self.kwargs or {}),
|
3622
|
+
**extra_kws,
|
3623
|
+
}),
|
3624
|
+
**field_kws, # passing a kwarg named 'kwargs' intentionally clobbers
|
3625
|
+
})
|
3626
|
+
|
3627
|
+
#
|
3628
|
+
|
3629
|
+
@classmethod
|
3630
|
+
def of(
|
3631
|
+
cls,
|
3632
|
+
*cmd: str,
|
3633
|
+
input: ta.Any = None, # noqa
|
3634
|
+
timeout: ta.Optional[TimeoutLike] = None,
|
3635
|
+
check: bool = False, # noqa
|
3636
|
+
capture_output: ta.Optional[bool] = None,
|
3637
|
+
**kwargs: ta.Any,
|
3638
|
+
) -> 'SubprocessRun':
|
3639
|
+
return cls(
|
3640
|
+
cmd=cmd,
|
3641
|
+
input=input,
|
3642
|
+
timeout=timeout,
|
3643
|
+
check=check,
|
3644
|
+
capture_output=capture_output,
|
3645
|
+
kwargs=kwargs,
|
3646
|
+
)
|
3647
|
+
|
3648
|
+
#
|
3649
|
+
|
3650
|
+
_DEFAULT_SUBPROCESSES: ta.ClassVar[ta.Optional[ta.Any]] = None # AbstractSubprocesses
|
3651
|
+
|
3652
|
+
def run(
|
3653
|
+
self,
|
3654
|
+
subprocesses: ta.Optional[ta.Any] = None, # AbstractSubprocesses
|
3655
|
+
**kwargs: ta.Any,
|
3656
|
+
) -> SubprocessRunOutput:
|
3657
|
+
if subprocesses is None:
|
3658
|
+
subprocesses = self._DEFAULT_SUBPROCESSES
|
3659
|
+
return check.not_none(subprocesses).run_(self.replace(**kwargs)) # type: ignore[attr-defined]
|
3660
|
+
|
3661
|
+
_DEFAULT_ASYNC_SUBPROCESSES: ta.ClassVar[ta.Optional[ta.Any]] = None # AbstractAsyncSubprocesses
|
3662
|
+
|
3663
|
+
async def async_run(
|
3664
|
+
self,
|
3665
|
+
async_subprocesses: ta.Optional[ta.Any] = None, # AbstractAsyncSubprocesses
|
3666
|
+
**kwargs: ta.Any,
|
3667
|
+
) -> SubprocessRunOutput:
|
3668
|
+
if async_subprocesses is None:
|
3669
|
+
async_subprocesses = self._DEFAULT_ASYNC_SUBPROCESSES
|
3670
|
+
return await check.not_none(async_subprocesses).run_(self.replace(**kwargs)) # type: ignore[attr-defined]
|
3671
|
+
|
3672
|
+
|
3673
|
+
SubprocessRun._FIELD_NAMES = frozenset(fld.name for fld in dc.fields(SubprocessRun)) # noqa
|
3674
|
+
|
3675
|
+
|
3676
|
+
##
|
3677
|
+
|
3678
|
+
|
3679
|
+
class SubprocessRunnable(abc.ABC, ta.Generic[T]):
|
3680
|
+
@abc.abstractmethod
|
3681
|
+
def make_run(self) -> SubprocessRun:
|
3682
|
+
raise NotImplementedError
|
3683
|
+
|
3684
|
+
@abc.abstractmethod
|
3685
|
+
def handle_run_output(self, output: SubprocessRunOutput) -> T:
|
3686
|
+
raise NotImplementedError
|
3687
|
+
|
3688
|
+
#
|
3689
|
+
|
3690
|
+
def run(
|
3691
|
+
self,
|
3692
|
+
subprocesses: ta.Optional[ta.Any] = None, # AbstractSubprocesses
|
3693
|
+
**kwargs: ta.Any,
|
3694
|
+
) -> T:
|
3695
|
+
return self.handle_run_output(self.make_run().run(subprocesses, **kwargs))
|
3696
|
+
|
3697
|
+
async def async_run(
|
3698
|
+
self,
|
3699
|
+
async_subprocesses: ta.Optional[ta.Any] = None, # AbstractAsyncSubprocesses
|
3700
|
+
**kwargs: ta.Any,
|
3701
|
+
) -> T:
|
3702
|
+
return self.handle_run_output(await self.make_run().async_run(async_subprocesses, **kwargs))
|
3703
|
+
|
3704
|
+
|
3372
3705
|
########################################
|
3373
3706
|
# ../types.py
|
3374
3707
|
|
@@ -3607,23 +3940,7 @@ def configure_standard_logging(
|
|
3607
3940
|
|
3608
3941
|
|
3609
3942
|
########################################
|
3610
|
-
# ../../../omlish/subprocesses.py
|
3611
|
-
|
3612
|
-
|
3613
|
-
##
|
3614
|
-
|
3615
|
-
|
3616
|
-
# Valid channel type kwarg values:
|
3617
|
-
# - A special flag negative int
|
3618
|
-
# - A positive fd int
|
3619
|
-
# - A file-like object
|
3620
|
-
# - None
|
3621
|
-
|
3622
|
-
SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
|
3623
|
-
'pipe': subprocess.PIPE,
|
3624
|
-
'stdout': subprocess.STDOUT,
|
3625
|
-
'devnull': subprocess.DEVNULL,
|
3626
|
-
}
|
3943
|
+
# ../../../omlish/subprocesses/wrap.py
|
3627
3944
|
|
3628
3945
|
|
3629
3946
|
##
|
@@ -3643,28 +3960,74 @@ def subprocess_maybe_shell_wrap_exec(*cmd: str) -> ta.Tuple[str, ...]:
|
|
3643
3960
|
return cmd
|
3644
3961
|
|
3645
3962
|
|
3646
|
-
|
3647
|
-
|
3648
|
-
|
3649
|
-
|
3650
|
-
|
3651
|
-
|
3652
|
-
|
3653
|
-
|
3654
|
-
|
3655
|
-
|
3656
|
-
if proc.stderr:
|
3657
|
-
proc.stderr.close()
|
3658
|
-
if proc.stdin:
|
3659
|
-
proc.stdin.close()
|
3660
|
-
|
3661
|
-
proc.wait(timeout)
|
3963
|
+
########################################
|
3964
|
+
# ../providers/base.py
|
3965
|
+
"""
|
3966
|
+
TODO:
|
3967
|
+
- backends
|
3968
|
+
- local builds
|
3969
|
+
- deadsnakes?
|
3970
|
+
- uv
|
3971
|
+
- loose versions
|
3972
|
+
"""
|
3662
3973
|
|
3663
3974
|
|
3664
3975
|
##
|
3665
3976
|
|
3666
3977
|
|
3667
|
-
class
|
3978
|
+
class InterpProvider(abc.ABC):
|
3979
|
+
name: ta.ClassVar[str]
|
3980
|
+
|
3981
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
3982
|
+
super().__init_subclass__(**kwargs)
|
3983
|
+
if abc.ABC not in cls.__bases__ and 'name' not in cls.__dict__:
|
3984
|
+
sfx = 'InterpProvider'
|
3985
|
+
if not cls.__name__.endswith(sfx):
|
3986
|
+
raise NameError(cls)
|
3987
|
+
setattr(cls, 'name', snake_case(cls.__name__[:-len(sfx)]))
|
3988
|
+
|
3989
|
+
@abc.abstractmethod
|
3990
|
+
def get_installed_versions(self, spec: InterpSpecifier) -> ta.Awaitable[ta.Sequence[InterpVersion]]:
|
3991
|
+
raise NotImplementedError
|
3992
|
+
|
3993
|
+
@abc.abstractmethod
|
3994
|
+
def get_installed_version(self, version: InterpVersion) -> ta.Awaitable[Interp]:
|
3995
|
+
raise NotImplementedError
|
3996
|
+
|
3997
|
+
async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
3998
|
+
return []
|
3999
|
+
|
4000
|
+
async def install_version(self, version: InterpVersion) -> Interp:
|
4001
|
+
raise TypeError
|
4002
|
+
|
4003
|
+
|
4004
|
+
InterpProviders = ta.NewType('InterpProviders', ta.Sequence[InterpProvider])
|
4005
|
+
|
4006
|
+
|
4007
|
+
########################################
|
4008
|
+
# ../../../omlish/subprocesses/base.py
|
4009
|
+
|
4010
|
+
|
4011
|
+
##
|
4012
|
+
|
4013
|
+
|
4014
|
+
# Valid channel type kwarg values:
|
4015
|
+
# - A special flag negative int
|
4016
|
+
# - A positive fd int
|
4017
|
+
# - A file-like object
|
4018
|
+
# - None
|
4019
|
+
|
4020
|
+
SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
|
4021
|
+
'pipe': subprocess.PIPE,
|
4022
|
+
'stdout': subprocess.STDOUT,
|
4023
|
+
'devnull': subprocess.DEVNULL,
|
4024
|
+
}
|
4025
|
+
|
4026
|
+
|
4027
|
+
##
|
4028
|
+
|
4029
|
+
|
4030
|
+
class VerboseCalledProcessError(subprocess.CalledProcessError):
|
3668
4031
|
@classmethod
|
3669
4032
|
def from_std(cls, e: subprocess.CalledProcessError) -> 'VerboseCalledProcessError':
|
3670
4033
|
return cls(
|
@@ -3741,6 +4104,11 @@ class BaseSubprocesses(abc.ABC): # noqa
|
|
3741
4104
|
|
3742
4105
|
#
|
3743
4106
|
|
4107
|
+
if 'timeout' in kwargs:
|
4108
|
+
kwargs['timeout'] = Timeout.of(kwargs['timeout']).or_(None)
|
4109
|
+
|
4110
|
+
#
|
4111
|
+
|
3744
4112
|
return cmd, dict(
|
3745
4113
|
env=env,
|
3746
4114
|
shell=shell,
|
@@ -3845,174 +4213,96 @@ class BaseSubprocesses(abc.ABC): # noqa
|
|
3845
4213
|
return e
|
3846
4214
|
|
3847
4215
|
|
3848
|
-
|
3849
|
-
|
3850
|
-
|
3851
|
-
@dc.dataclass(frozen=True)
|
3852
|
-
class SubprocessRun:
|
3853
|
-
cmd: ta.Sequence[str]
|
3854
|
-
input: ta.Any = None
|
3855
|
-
timeout: ta.Optional[float] = None
|
3856
|
-
check: bool = False
|
3857
|
-
capture_output: ta.Optional[bool] = None
|
3858
|
-
kwargs: ta.Optional[ta.Mapping[str, ta.Any]] = None
|
3859
|
-
|
3860
|
-
@classmethod
|
3861
|
-
def of(
|
3862
|
-
cls,
|
3863
|
-
*cmd: str,
|
3864
|
-
input: ta.Any = None, # noqa
|
3865
|
-
timeout: ta.Optional[float] = None,
|
3866
|
-
check: bool = False,
|
3867
|
-
capture_output: ta.Optional[bool] = None,
|
3868
|
-
**kwargs: ta.Any,
|
3869
|
-
) -> 'SubprocessRun':
|
3870
|
-
return cls(
|
3871
|
-
cmd=cmd,
|
3872
|
-
input=input,
|
3873
|
-
timeout=timeout,
|
3874
|
-
check=check,
|
3875
|
-
capture_output=capture_output,
|
3876
|
-
kwargs=kwargs,
|
3877
|
-
)
|
4216
|
+
########################################
|
4217
|
+
# ../resolvers.py
|
3878
4218
|
|
3879
4219
|
|
3880
4220
|
@dc.dataclass(frozen=True)
|
3881
|
-
class
|
3882
|
-
|
3883
|
-
|
3884
|
-
returncode: int # noqa
|
3885
|
-
|
3886
|
-
stdout: ta.Optional[bytes] = None
|
3887
|
-
stderr: ta.Optional[bytes] = None
|
3888
|
-
|
3889
|
-
|
3890
|
-
class AbstractSubprocesses(BaseSubprocesses, abc.ABC):
|
3891
|
-
@abc.abstractmethod
|
3892
|
-
def run_(self, run: SubprocessRun) -> SubprocessRunOutput:
|
3893
|
-
raise NotImplementedError
|
3894
|
-
|
3895
|
-
def run(
|
3896
|
-
self,
|
3897
|
-
*cmd: str,
|
3898
|
-
input: ta.Any = None, # noqa
|
3899
|
-
timeout: ta.Optional[float] = None,
|
3900
|
-
check: bool = False,
|
3901
|
-
capture_output: ta.Optional[bool] = None,
|
3902
|
-
**kwargs: ta.Any,
|
3903
|
-
) -> SubprocessRunOutput:
|
3904
|
-
return self.run_(SubprocessRun(
|
3905
|
-
cmd=cmd,
|
3906
|
-
input=input,
|
3907
|
-
timeout=timeout,
|
3908
|
-
check=check,
|
3909
|
-
capture_output=capture_output,
|
3910
|
-
kwargs=kwargs,
|
3911
|
-
))
|
4221
|
+
class InterpResolverProviders:
|
4222
|
+
providers: ta.Sequence[ta.Tuple[str, InterpProvider]]
|
3912
4223
|
|
3913
|
-
#
|
3914
4224
|
|
3915
|
-
|
3916
|
-
def
|
4225
|
+
class InterpResolver:
|
4226
|
+
def __init__(
|
3917
4227
|
self,
|
3918
|
-
|
3919
|
-
stdout: ta.Any = sys.stderr,
|
3920
|
-
**kwargs: ta.Any,
|
4228
|
+
providers: InterpResolverProviders,
|
3921
4229
|
) -> None:
|
3922
|
-
|
3923
|
-
|
3924
|
-
@abc.abstractmethod
|
3925
|
-
def check_output(
|
3926
|
-
self,
|
3927
|
-
*cmd: str,
|
3928
|
-
**kwargs: ta.Any,
|
3929
|
-
) -> bytes:
|
3930
|
-
raise NotImplementedError
|
4230
|
+
super().__init__()
|
3931
4231
|
|
3932
|
-
|
4232
|
+
self._providers: ta.Mapping[str, InterpProvider] = collections.OrderedDict(providers.providers)
|
3933
4233
|
|
3934
|
-
def
|
3935
|
-
|
3936
|
-
|
3937
|
-
|
3938
|
-
|
3939
|
-
|
4234
|
+
async def _resolve_installed(self, spec: InterpSpecifier) -> ta.Optional[ta.Tuple[InterpProvider, InterpVersion]]:
|
4235
|
+
lst = [
|
4236
|
+
(i, si)
|
4237
|
+
for i, p in enumerate(self._providers.values())
|
4238
|
+
for si in await p.get_installed_versions(spec)
|
4239
|
+
if spec.contains(si)
|
4240
|
+
]
|
3940
4241
|
|
3941
|
-
|
4242
|
+
slst = sorted(lst, key=lambda t: (-t[0], t[1].version))
|
4243
|
+
if not slst:
|
4244
|
+
return None
|
3942
4245
|
|
3943
|
-
|
3944
|
-
|
3945
|
-
|
3946
|
-
**kwargs: ta.Any,
|
3947
|
-
) -> bool:
|
3948
|
-
if isinstance(self.try_fn(self.check_call, *cmd, **kwargs), Exception):
|
3949
|
-
return False
|
3950
|
-
else:
|
3951
|
-
return True
|
4246
|
+
bi, bv = slst[-1]
|
4247
|
+
bp = list(self._providers.values())[bi]
|
4248
|
+
return (bp, bv)
|
3952
4249
|
|
3953
|
-
def
|
4250
|
+
async def resolve(
|
3954
4251
|
self,
|
3955
|
-
|
3956
|
-
|
3957
|
-
|
3958
|
-
|
3959
|
-
|
3960
|
-
|
3961
|
-
|
4252
|
+
spec: InterpSpecifier,
|
4253
|
+
*,
|
4254
|
+
install: bool = False,
|
4255
|
+
) -> ta.Optional[Interp]:
|
4256
|
+
tup = await self._resolve_installed(spec)
|
4257
|
+
if tup is not None:
|
4258
|
+
bp, bv = tup
|
4259
|
+
return await bp.get_installed_version(bv)
|
3962
4260
|
|
3963
|
-
|
3964
|
-
self,
|
3965
|
-
*cmd: str,
|
3966
|
-
**kwargs: ta.Any,
|
3967
|
-
) -> ta.Optional[str]:
|
3968
|
-
if (ret := self.try_output(*cmd, **kwargs)) is None:
|
4261
|
+
if not install:
|
3969
4262
|
return None
|
3970
|
-
else:
|
3971
|
-
return ret.decode().strip()
|
3972
|
-
|
3973
|
-
|
3974
|
-
##
|
3975
4263
|
|
4264
|
+
tp = list(self._providers.values())[0] # noqa
|
3976
4265
|
|
3977
|
-
|
3978
|
-
|
3979
|
-
|
3980
|
-
run.cmd,
|
3981
|
-
input=run.input,
|
3982
|
-
timeout=run.timeout,
|
3983
|
-
check=run.check,
|
3984
|
-
capture_output=run.capture_output or False,
|
3985
|
-
**(run.kwargs or {}),
|
4266
|
+
sv = sorted(
|
4267
|
+
[s for s in await tp.get_installable_versions(spec) if s in spec],
|
4268
|
+
key=lambda s: s.version,
|
3986
4269
|
)
|
4270
|
+
if not sv:
|
4271
|
+
return None
|
3987
4272
|
|
3988
|
-
|
3989
|
-
|
3990
|
-
|
3991
|
-
returncode=proc.returncode,
|
4273
|
+
bv = sv[-1]
|
4274
|
+
return await tp.install_version(bv)
|
3992
4275
|
|
3993
|
-
|
3994
|
-
|
3995
|
-
)
|
4276
|
+
async def list(self, spec: InterpSpecifier) -> None:
|
4277
|
+
print('installed:')
|
4278
|
+
for n, p in self._providers.items():
|
4279
|
+
lst = [
|
4280
|
+
si
|
4281
|
+
for si in await p.get_installed_versions(spec)
|
4282
|
+
if spec.contains(si)
|
4283
|
+
]
|
4284
|
+
if lst:
|
4285
|
+
print(f' {n}')
|
4286
|
+
for si in lst:
|
4287
|
+
print(f' {si}')
|
3996
4288
|
|
3997
|
-
|
3998
|
-
self,
|
3999
|
-
*cmd: str,
|
4000
|
-
stdout: ta.Any = sys.stderr,
|
4001
|
-
**kwargs: ta.Any,
|
4002
|
-
) -> None:
|
4003
|
-
with self.prepare_and_wrap(*cmd, stdout=stdout, **kwargs) as (cmd, kwargs): # noqa
|
4004
|
-
subprocess.check_call(cmd, **kwargs)
|
4289
|
+
print()
|
4005
4290
|
|
4006
|
-
|
4007
|
-
|
4008
|
-
|
4009
|
-
|
4010
|
-
|
4011
|
-
|
4012
|
-
|
4291
|
+
print('installable:')
|
4292
|
+
for n, p in self._providers.items():
|
4293
|
+
lst = [
|
4294
|
+
si
|
4295
|
+
for si in await p.get_installable_versions(spec)
|
4296
|
+
if spec.contains(si)
|
4297
|
+
]
|
4298
|
+
if lst:
|
4299
|
+
print(f' {n}')
|
4300
|
+
for si in lst:
|
4301
|
+
print(f' {si}')
|
4013
4302
|
|
4014
4303
|
|
4015
|
-
|
4304
|
+
########################################
|
4305
|
+
# ../../../omlish/subprocesses/async_.py
|
4016
4306
|
|
4017
4307
|
|
4018
4308
|
##
|
@@ -4027,7 +4317,7 @@ class AbstractAsyncSubprocesses(BaseSubprocesses):
|
|
4027
4317
|
self,
|
4028
4318
|
*cmd: str,
|
4029
4319
|
input: ta.Any = None, # noqa
|
4030
|
-
timeout: ta.Optional[
|
4320
|
+
timeout: ta.Optional[TimeoutLike] = None,
|
4031
4321
|
check: bool = False,
|
4032
4322
|
capture_output: ta.Optional[bool] = None,
|
4033
4323
|
**kwargs: ta.Any,
|
@@ -4102,50 +4392,6 @@ class AbstractAsyncSubprocesses(BaseSubprocesses):
|
|
4102
4392
|
return ret.decode().strip()
|
4103
4393
|
|
4104
4394
|
|
4105
|
-
########################################
|
4106
|
-
# ../providers/base.py
|
4107
|
-
"""
|
4108
|
-
TODO:
|
4109
|
-
- backends
|
4110
|
-
- local builds
|
4111
|
-
- deadsnakes?
|
4112
|
-
- uv
|
4113
|
-
- loose versions
|
4114
|
-
"""
|
4115
|
-
|
4116
|
-
|
4117
|
-
##
|
4118
|
-
|
4119
|
-
|
4120
|
-
class InterpProvider(abc.ABC):
|
4121
|
-
name: ta.ClassVar[str]
|
4122
|
-
|
4123
|
-
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
4124
|
-
super().__init_subclass__(**kwargs)
|
4125
|
-
if abc.ABC not in cls.__bases__ and 'name' not in cls.__dict__:
|
4126
|
-
sfx = 'InterpProvider'
|
4127
|
-
if not cls.__name__.endswith(sfx):
|
4128
|
-
raise NameError(cls)
|
4129
|
-
setattr(cls, 'name', snake_case(cls.__name__[:-len(sfx)]))
|
4130
|
-
|
4131
|
-
@abc.abstractmethod
|
4132
|
-
def get_installed_versions(self, spec: InterpSpecifier) -> ta.Awaitable[ta.Sequence[InterpVersion]]:
|
4133
|
-
raise NotImplementedError
|
4134
|
-
|
4135
|
-
@abc.abstractmethod
|
4136
|
-
def get_installed_version(self, version: InterpVersion) -> ta.Awaitable[Interp]:
|
4137
|
-
raise NotImplementedError
|
4138
|
-
|
4139
|
-
async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
4140
|
-
return []
|
4141
|
-
|
4142
|
-
async def install_version(self, version: InterpVersion) -> Interp:
|
4143
|
-
raise TypeError
|
4144
|
-
|
4145
|
-
|
4146
|
-
InterpProviders = ta.NewType('InterpProviders', ta.Sequence[InterpProvider])
|
4147
|
-
|
4148
|
-
|
4149
4395
|
########################################
|
4150
4396
|
# ../../../omlish/asyncs/asyncio/subprocesses.py
|
4151
4397
|
|
@@ -4261,7 +4507,7 @@ class AsyncioProcessCommunicator:
|
|
4261
4507
|
async def communicate(
|
4262
4508
|
self,
|
4263
4509
|
input: ta.Any = None, # noqa
|
4264
|
-
timeout: ta.Optional[
|
4510
|
+
timeout: ta.Optional[TimeoutLike] = None,
|
4265
4511
|
) -> Communication:
|
4266
4512
|
return await asyncio_maybe_timeout(self._communicate(input), timeout)
|
4267
4513
|
|
@@ -4274,7 +4520,7 @@ class AsyncioSubprocesses(AbstractAsyncSubprocesses):
|
|
4274
4520
|
self,
|
4275
4521
|
proc: asyncio.subprocess.Process,
|
4276
4522
|
input: ta.Any = None, # noqa
|
4277
|
-
timeout: ta.Optional[
|
4523
|
+
timeout: ta.Optional[TimeoutLike] = None,
|
4278
4524
|
) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
|
4279
4525
|
return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
|
4280
4526
|
|
@@ -4285,22 +4531,22 @@ class AsyncioSubprocesses(AbstractAsyncSubprocesses):
|
|
4285
4531
|
self,
|
4286
4532
|
*cmd: str,
|
4287
4533
|
shell: bool = False,
|
4288
|
-
timeout: ta.Optional[
|
4534
|
+
timeout: ta.Optional[TimeoutLike] = None,
|
4289
4535
|
**kwargs: ta.Any,
|
4290
4536
|
) -> ta.AsyncGenerator[asyncio.subprocess.Process, None]:
|
4291
|
-
fac: ta.Any
|
4292
|
-
if shell:
|
4293
|
-
fac = functools.partial(
|
4294
|
-
asyncio.create_subprocess_shell,
|
4295
|
-
check.single(cmd),
|
4296
|
-
)
|
4297
|
-
else:
|
4298
|
-
fac = functools.partial(
|
4299
|
-
asyncio.create_subprocess_exec,
|
4300
|
-
*cmd,
|
4301
|
-
)
|
4302
|
-
|
4303
4537
|
with self.prepare_and_wrap( *cmd, shell=shell, **kwargs) as (cmd, kwargs): # noqa
|
4538
|
+
fac: ta.Any
|
4539
|
+
if shell:
|
4540
|
+
fac = functools.partial(
|
4541
|
+
asyncio.create_subprocess_shell,
|
4542
|
+
check.single(cmd),
|
4543
|
+
)
|
4544
|
+
else:
|
4545
|
+
fac = functools.partial(
|
4546
|
+
asyncio.create_subprocess_exec,
|
4547
|
+
*cmd,
|
4548
|
+
)
|
4549
|
+
|
4304
4550
|
proc: asyncio.subprocess.Process = await fac(**kwargs)
|
4305
4551
|
try:
|
4306
4552
|
yield proc
|
@@ -4541,94 +4787,6 @@ class Pyenv:
|
|
4541
4787
|
return True
|
4542
4788
|
|
4543
4789
|
|
4544
|
-
########################################
|
4545
|
-
# ../resolvers.py
|
4546
|
-
|
4547
|
-
|
4548
|
-
@dc.dataclass(frozen=True)
|
4549
|
-
class InterpResolverProviders:
|
4550
|
-
providers: ta.Sequence[ta.Tuple[str, InterpProvider]]
|
4551
|
-
|
4552
|
-
|
4553
|
-
class InterpResolver:
|
4554
|
-
def __init__(
|
4555
|
-
self,
|
4556
|
-
providers: InterpResolverProviders,
|
4557
|
-
) -> None:
|
4558
|
-
super().__init__()
|
4559
|
-
|
4560
|
-
self._providers: ta.Mapping[str, InterpProvider] = collections.OrderedDict(providers.providers)
|
4561
|
-
|
4562
|
-
async def _resolve_installed(self, spec: InterpSpecifier) -> ta.Optional[ta.Tuple[InterpProvider, InterpVersion]]:
|
4563
|
-
lst = [
|
4564
|
-
(i, si)
|
4565
|
-
for i, p in enumerate(self._providers.values())
|
4566
|
-
for si in await p.get_installed_versions(spec)
|
4567
|
-
if spec.contains(si)
|
4568
|
-
]
|
4569
|
-
|
4570
|
-
slst = sorted(lst, key=lambda t: (-t[0], t[1].version))
|
4571
|
-
if not slst:
|
4572
|
-
return None
|
4573
|
-
|
4574
|
-
bi, bv = slst[-1]
|
4575
|
-
bp = list(self._providers.values())[bi]
|
4576
|
-
return (bp, bv)
|
4577
|
-
|
4578
|
-
async def resolve(
|
4579
|
-
self,
|
4580
|
-
spec: InterpSpecifier,
|
4581
|
-
*,
|
4582
|
-
install: bool = False,
|
4583
|
-
) -> ta.Optional[Interp]:
|
4584
|
-
tup = await self._resolve_installed(spec)
|
4585
|
-
if tup is not None:
|
4586
|
-
bp, bv = tup
|
4587
|
-
return await bp.get_installed_version(bv)
|
4588
|
-
|
4589
|
-
if not install:
|
4590
|
-
return None
|
4591
|
-
|
4592
|
-
tp = list(self._providers.values())[0] # noqa
|
4593
|
-
|
4594
|
-
sv = sorted(
|
4595
|
-
[s for s in await tp.get_installable_versions(spec) if s in spec],
|
4596
|
-
key=lambda s: s.version,
|
4597
|
-
)
|
4598
|
-
if not sv:
|
4599
|
-
return None
|
4600
|
-
|
4601
|
-
bv = sv[-1]
|
4602
|
-
return await tp.install_version(bv)
|
4603
|
-
|
4604
|
-
async def list(self, spec: InterpSpecifier) -> None:
|
4605
|
-
print('installed:')
|
4606
|
-
for n, p in self._providers.items():
|
4607
|
-
lst = [
|
4608
|
-
si
|
4609
|
-
for si in await p.get_installed_versions(spec)
|
4610
|
-
if spec.contains(si)
|
4611
|
-
]
|
4612
|
-
if lst:
|
4613
|
-
print(f' {n}')
|
4614
|
-
for si in lst:
|
4615
|
-
print(f' {si}')
|
4616
|
-
|
4617
|
-
print()
|
4618
|
-
|
4619
|
-
print('installable:')
|
4620
|
-
for n, p in self._providers.items():
|
4621
|
-
lst = [
|
4622
|
-
si
|
4623
|
-
for si in await p.get_installable_versions(spec)
|
4624
|
-
if spec.contains(si)
|
4625
|
-
]
|
4626
|
-
if lst:
|
4627
|
-
print(f' {n}')
|
4628
|
-
for si in lst:
|
4629
|
-
print(f' {si}')
|
4630
|
-
|
4631
|
-
|
4632
4790
|
########################################
|
4633
4791
|
# ../providers/running.py
|
4634
4792
|
|