omdev 0.0.0.dev150__py3-none-any.whl → 0.0.0.dev152__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.
Potentially problematic release.
This version of omdev might be problematic. Click here for more details.
- omdev/cache/data/cache.py +2 -2
- omdev/imgur.py +1 -1
- omdev/pycharm/cli.py +1 -1
- omdev/pyproject/cli.py +133 -258
- omdev/pyproject/venvs.py +114 -0
- omdev/scripts/interp.py +2 -2
- omdev/scripts/pyproject.py +456 -200
- omdev/tools/doc.py +1 -1
- omdev/tools/docker.py +2 -2
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/RECORD +15 -14
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/LICENSE +0 -0
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/top_level.txt +0 -0
omdev/scripts/pyproject.py
CHANGED
|
@@ -102,7 +102,7 @@ CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
|
|
|
102
102
|
# ../../omlish/lite/check.py
|
|
103
103
|
SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
|
|
104
104
|
CheckMessage = ta.Union[str, ta.Callable[..., ta.Optional[str]], None] # ta.TypeAlias
|
|
105
|
-
CheckLateConfigureFn = ta.Callable[['Checks'], None]
|
|
105
|
+
CheckLateConfigureFn = ta.Callable[['Checks'], None] # ta.TypeAlias
|
|
106
106
|
CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
|
|
107
107
|
CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
|
|
108
108
|
CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
|
|
@@ -112,8 +112,11 @@ UnparsedVersion = ta.Union['Version', str]
|
|
|
112
112
|
UnparsedVersionVar = ta.TypeVar('UnparsedVersionVar', bound=UnparsedVersion)
|
|
113
113
|
CallableVersionOperator = ta.Callable[['Version', str], bool]
|
|
114
114
|
|
|
115
|
+
# ../../omlish/argparse/cli.py
|
|
116
|
+
ArgparseCommandFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
|
|
117
|
+
|
|
115
118
|
# ../../omlish/lite/subprocesses.py
|
|
116
|
-
SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull']
|
|
119
|
+
SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
|
|
117
120
|
|
|
118
121
|
|
|
119
122
|
########################################
|
|
@@ -3253,6 +3256,278 @@ class SpecifierSet(BaseSpecifier):
|
|
|
3253
3256
|
return iter(filtered)
|
|
3254
3257
|
|
|
3255
3258
|
|
|
3259
|
+
########################################
|
|
3260
|
+
# ../../../omlish/argparse/cli.py
|
|
3261
|
+
"""
|
|
3262
|
+
TODO:
|
|
3263
|
+
- default command
|
|
3264
|
+
- auto match all underscores to hyphens
|
|
3265
|
+
- pre-run, post-run hooks
|
|
3266
|
+
- exitstack?
|
|
3267
|
+
"""
|
|
3268
|
+
|
|
3269
|
+
|
|
3270
|
+
##
|
|
3271
|
+
|
|
3272
|
+
|
|
3273
|
+
@dc.dataclass(eq=False)
|
|
3274
|
+
class ArgparseArg:
|
|
3275
|
+
args: ta.Sequence[ta.Any]
|
|
3276
|
+
kwargs: ta.Mapping[str, ta.Any]
|
|
3277
|
+
dest: ta.Optional[str] = None
|
|
3278
|
+
|
|
3279
|
+
def __get__(self, instance, owner=None):
|
|
3280
|
+
if instance is None:
|
|
3281
|
+
return self
|
|
3282
|
+
return getattr(instance.args, self.dest) # type: ignore
|
|
3283
|
+
|
|
3284
|
+
|
|
3285
|
+
def argparse_arg(*args, **kwargs) -> ArgparseArg:
|
|
3286
|
+
return ArgparseArg(args, kwargs)
|
|
3287
|
+
|
|
3288
|
+
|
|
3289
|
+
#
|
|
3290
|
+
|
|
3291
|
+
|
|
3292
|
+
@dc.dataclass(eq=False)
|
|
3293
|
+
class ArgparseCommand:
|
|
3294
|
+
name: str
|
|
3295
|
+
fn: ArgparseCommandFn
|
|
3296
|
+
args: ta.Sequence[ArgparseArg] = () # noqa
|
|
3297
|
+
|
|
3298
|
+
# _: dc.KW_ONLY
|
|
3299
|
+
|
|
3300
|
+
aliases: ta.Optional[ta.Sequence[str]] = None
|
|
3301
|
+
parent: ta.Optional['ArgparseCommand'] = None
|
|
3302
|
+
accepts_unknown: bool = False
|
|
3303
|
+
|
|
3304
|
+
def __post_init__(self) -> None:
|
|
3305
|
+
def check_name(s: str) -> None:
|
|
3306
|
+
check.isinstance(s, str)
|
|
3307
|
+
check.not_in('_', s)
|
|
3308
|
+
check.not_empty(s)
|
|
3309
|
+
check_name(self.name)
|
|
3310
|
+
check.not_isinstance(self.aliases, str)
|
|
3311
|
+
for a in self.aliases or []:
|
|
3312
|
+
check_name(a)
|
|
3313
|
+
|
|
3314
|
+
check.arg(callable(self.fn))
|
|
3315
|
+
check.arg(all(isinstance(a, ArgparseArg) for a in self.args))
|
|
3316
|
+
check.isinstance(self.parent, (ArgparseCommand, type(None)))
|
|
3317
|
+
check.isinstance(self.accepts_unknown, bool)
|
|
3318
|
+
|
|
3319
|
+
functools.update_wrapper(self, self.fn)
|
|
3320
|
+
|
|
3321
|
+
def __get__(self, instance, owner=None):
|
|
3322
|
+
if instance is None:
|
|
3323
|
+
return self
|
|
3324
|
+
return dc.replace(self, fn=self.fn.__get__(instance, owner)) # noqa
|
|
3325
|
+
|
|
3326
|
+
def __call__(self, *args, **kwargs) -> ta.Optional[int]:
|
|
3327
|
+
return self.fn(*args, **kwargs)
|
|
3328
|
+
|
|
3329
|
+
|
|
3330
|
+
def argparse_command(
|
|
3331
|
+
*args: ArgparseArg,
|
|
3332
|
+
name: ta.Optional[str] = None,
|
|
3333
|
+
aliases: ta.Optional[ta.Iterable[str]] = None,
|
|
3334
|
+
parent: ta.Optional[ArgparseCommand] = None,
|
|
3335
|
+
accepts_unknown: bool = False,
|
|
3336
|
+
) -> ta.Any: # ta.Callable[[ArgparseCommandFn], ArgparseCommand]: # FIXME
|
|
3337
|
+
for arg in args:
|
|
3338
|
+
check.isinstance(arg, ArgparseArg)
|
|
3339
|
+
check.isinstance(name, (str, type(None)))
|
|
3340
|
+
check.isinstance(parent, (ArgparseCommand, type(None)))
|
|
3341
|
+
check.not_isinstance(aliases, str)
|
|
3342
|
+
|
|
3343
|
+
def inner(fn):
|
|
3344
|
+
return ArgparseCommand(
|
|
3345
|
+
(name if name is not None else fn.__name__).replace('_', '-'),
|
|
3346
|
+
fn,
|
|
3347
|
+
args,
|
|
3348
|
+
aliases=tuple(aliases) if aliases is not None else None,
|
|
3349
|
+
parent=parent,
|
|
3350
|
+
accepts_unknown=accepts_unknown,
|
|
3351
|
+
)
|
|
3352
|
+
|
|
3353
|
+
return inner
|
|
3354
|
+
|
|
3355
|
+
|
|
3356
|
+
##
|
|
3357
|
+
|
|
3358
|
+
|
|
3359
|
+
def _get_argparse_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
|
|
3360
|
+
if ann is str:
|
|
3361
|
+
return {}
|
|
3362
|
+
elif ann is int:
|
|
3363
|
+
return {'type': int}
|
|
3364
|
+
elif ann is bool:
|
|
3365
|
+
return {'action': 'store_true'}
|
|
3366
|
+
elif ann is list:
|
|
3367
|
+
return {'action': 'append'}
|
|
3368
|
+
else:
|
|
3369
|
+
raise TypeError(ann)
|
|
3370
|
+
|
|
3371
|
+
|
|
3372
|
+
class _ArgparseCliAnnotationBox:
|
|
3373
|
+
def __init__(self, annotations: ta.Mapping[str, ta.Any]) -> None:
|
|
3374
|
+
super().__init__()
|
|
3375
|
+
self.__annotations__ = annotations # type: ignore
|
|
3376
|
+
|
|
3377
|
+
|
|
3378
|
+
class ArgparseCli:
|
|
3379
|
+
def __init__(self, argv: ta.Optional[ta.Sequence[str]] = None) -> None:
|
|
3380
|
+
super().__init__()
|
|
3381
|
+
|
|
3382
|
+
self._argv = argv if argv is not None else sys.argv[1:]
|
|
3383
|
+
|
|
3384
|
+
self._args, self._unknown_args = self.get_parser().parse_known_args(self._argv)
|
|
3385
|
+
|
|
3386
|
+
#
|
|
3387
|
+
|
|
3388
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
|
3389
|
+
super().__init_subclass__(**kwargs)
|
|
3390
|
+
|
|
3391
|
+
ns = cls.__dict__
|
|
3392
|
+
objs = {}
|
|
3393
|
+
mro = cls.__mro__[::-1]
|
|
3394
|
+
for bns in [bcls.__dict__ for bcls in reversed(mro)] + [ns]:
|
|
3395
|
+
bseen = set() # type: ignore
|
|
3396
|
+
for k, v in bns.items():
|
|
3397
|
+
if isinstance(v, (ArgparseCommand, ArgparseArg)):
|
|
3398
|
+
check.not_in(v, bseen)
|
|
3399
|
+
bseen.add(v)
|
|
3400
|
+
objs[k] = v
|
|
3401
|
+
elif k in objs:
|
|
3402
|
+
del [k]
|
|
3403
|
+
|
|
3404
|
+
#
|
|
3405
|
+
|
|
3406
|
+
anns = ta.get_type_hints(_ArgparseCliAnnotationBox({
|
|
3407
|
+
**{k: v for bcls in reversed(mro) for k, v in getattr(bcls, '__annotations__', {}).items()},
|
|
3408
|
+
**ns.get('__annotations__', {}),
|
|
3409
|
+
}), globalns=ns.get('__globals__', {}))
|
|
3410
|
+
|
|
3411
|
+
#
|
|
3412
|
+
|
|
3413
|
+
if '_parser' in ns:
|
|
3414
|
+
parser = check.isinstance(ns['_parser'], argparse.ArgumentParser)
|
|
3415
|
+
else:
|
|
3416
|
+
parser = argparse.ArgumentParser()
|
|
3417
|
+
setattr(cls, '_parser', parser)
|
|
3418
|
+
|
|
3419
|
+
#
|
|
3420
|
+
|
|
3421
|
+
subparsers = parser.add_subparsers()
|
|
3422
|
+
|
|
3423
|
+
for att, obj in objs.items():
|
|
3424
|
+
if isinstance(obj, ArgparseCommand):
|
|
3425
|
+
if obj.parent is not None:
|
|
3426
|
+
raise NotImplementedError
|
|
3427
|
+
|
|
3428
|
+
for cn in [obj.name, *(obj.aliases or [])]:
|
|
3429
|
+
subparser = subparsers.add_parser(cn)
|
|
3430
|
+
|
|
3431
|
+
for arg in (obj.args or []):
|
|
3432
|
+
if (
|
|
3433
|
+
len(arg.args) == 1 and
|
|
3434
|
+
isinstance(arg.args[0], str) and
|
|
3435
|
+
not (n := check.isinstance(arg.args[0], str)).startswith('-') and
|
|
3436
|
+
'metavar' not in arg.kwargs
|
|
3437
|
+
):
|
|
3438
|
+
subparser.add_argument(
|
|
3439
|
+
n.replace('-', '_'),
|
|
3440
|
+
**arg.kwargs,
|
|
3441
|
+
metavar=n,
|
|
3442
|
+
)
|
|
3443
|
+
else:
|
|
3444
|
+
subparser.add_argument(*arg.args, **arg.kwargs)
|
|
3445
|
+
|
|
3446
|
+
subparser.set_defaults(_cmd=obj)
|
|
3447
|
+
|
|
3448
|
+
elif isinstance(obj, ArgparseArg):
|
|
3449
|
+
if att in anns:
|
|
3450
|
+
ann_kwargs = _get_argparse_arg_ann_kwargs(anns[att])
|
|
3451
|
+
obj.kwargs = {**ann_kwargs, **obj.kwargs}
|
|
3452
|
+
|
|
3453
|
+
if not obj.dest:
|
|
3454
|
+
if 'dest' in obj.kwargs:
|
|
3455
|
+
obj.dest = obj.kwargs['dest']
|
|
3456
|
+
else:
|
|
3457
|
+
obj.dest = obj.kwargs['dest'] = att # type: ignore
|
|
3458
|
+
|
|
3459
|
+
parser.add_argument(*obj.args, **obj.kwargs)
|
|
3460
|
+
|
|
3461
|
+
else:
|
|
3462
|
+
raise TypeError(obj)
|
|
3463
|
+
|
|
3464
|
+
#
|
|
3465
|
+
|
|
3466
|
+
_parser: ta.ClassVar[argparse.ArgumentParser]
|
|
3467
|
+
|
|
3468
|
+
@classmethod
|
|
3469
|
+
def get_parser(cls) -> argparse.ArgumentParser:
|
|
3470
|
+
return cls._parser
|
|
3471
|
+
|
|
3472
|
+
@property
|
|
3473
|
+
def argv(self) -> ta.Sequence[str]:
|
|
3474
|
+
return self._argv
|
|
3475
|
+
|
|
3476
|
+
@property
|
|
3477
|
+
def args(self) -> argparse.Namespace:
|
|
3478
|
+
return self._args
|
|
3479
|
+
|
|
3480
|
+
@property
|
|
3481
|
+
def unknown_args(self) -> ta.Sequence[str]:
|
|
3482
|
+
return self._unknown_args
|
|
3483
|
+
|
|
3484
|
+
#
|
|
3485
|
+
|
|
3486
|
+
def _bind_cli_cmd(self, cmd: ArgparseCommand) -> ta.Callable:
|
|
3487
|
+
return cmd.__get__(self, type(self))
|
|
3488
|
+
|
|
3489
|
+
def prepare_cli_run(self) -> ta.Optional[ta.Callable]:
|
|
3490
|
+
cmd = getattr(self.args, '_cmd', None)
|
|
3491
|
+
|
|
3492
|
+
if self._unknown_args and not (cmd is not None and cmd.accepts_unknown):
|
|
3493
|
+
msg = f'unrecognized arguments: {" ".join(self._unknown_args)}'
|
|
3494
|
+
if (parser := self.get_parser()).exit_on_error: # type: ignore
|
|
3495
|
+
parser.error(msg)
|
|
3496
|
+
else:
|
|
3497
|
+
raise argparse.ArgumentError(None, msg)
|
|
3498
|
+
|
|
3499
|
+
if cmd is None:
|
|
3500
|
+
self.get_parser().print_help()
|
|
3501
|
+
return None
|
|
3502
|
+
|
|
3503
|
+
return self._bind_cli_cmd(cmd)
|
|
3504
|
+
|
|
3505
|
+
#
|
|
3506
|
+
|
|
3507
|
+
def cli_run(self) -> ta.Optional[int]:
|
|
3508
|
+
if (fn := self.prepare_cli_run()) is None:
|
|
3509
|
+
return 0
|
|
3510
|
+
|
|
3511
|
+
return fn()
|
|
3512
|
+
|
|
3513
|
+
def cli_run_and_exit(self) -> ta.NoReturn:
|
|
3514
|
+
sys.exit(rc if isinstance(rc := self.cli_run(), int) else 0)
|
|
3515
|
+
|
|
3516
|
+
def __call__(self, *, exit: bool = False) -> ta.Optional[int]: # noqa
|
|
3517
|
+
if exit:
|
|
3518
|
+
return self.cli_run_and_exit()
|
|
3519
|
+
else:
|
|
3520
|
+
return self.cli_run()
|
|
3521
|
+
|
|
3522
|
+
#
|
|
3523
|
+
|
|
3524
|
+
async def async_cli_run(self) -> ta.Optional[int]:
|
|
3525
|
+
if (fn := self.prepare_cli_run()) is None:
|
|
3526
|
+
return 0
|
|
3527
|
+
|
|
3528
|
+
return await fn()
|
|
3529
|
+
|
|
3530
|
+
|
|
3256
3531
|
########################################
|
|
3257
3532
|
# ../../../omlish/lite/logs.py
|
|
3258
3533
|
"""
|
|
@@ -6601,52 +6876,7 @@ DEFAULT_INTERP_RESOLVER = InterpResolver([(p.name, p) for p in [
|
|
|
6601
6876
|
|
|
6602
6877
|
|
|
6603
6878
|
########################################
|
|
6604
|
-
#
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
##
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
@dc.dataclass(frozen=True)
|
|
6611
|
-
class VersionsFile:
|
|
6612
|
-
name: ta.Optional[str] = '.versions'
|
|
6613
|
-
|
|
6614
|
-
@staticmethod
|
|
6615
|
-
def parse(s: str) -> ta.Mapping[str, str]:
|
|
6616
|
-
return {
|
|
6617
|
-
k: v
|
|
6618
|
-
for l in s.splitlines()
|
|
6619
|
-
if (sl := l.split('#')[0].strip())
|
|
6620
|
-
for k, _, v in (sl.partition('='),)
|
|
6621
|
-
}
|
|
6622
|
-
|
|
6623
|
-
@cached_nullary
|
|
6624
|
-
def contents(self) -> ta.Mapping[str, str]:
|
|
6625
|
-
if not self.name or not os.path.exists(self.name):
|
|
6626
|
-
return {}
|
|
6627
|
-
with open(self.name) as f:
|
|
6628
|
-
s = f.read()
|
|
6629
|
-
return self.parse(s)
|
|
6630
|
-
|
|
6631
|
-
@staticmethod
|
|
6632
|
-
def get_pythons(d: ta.Mapping[str, str]) -> ta.Mapping[str, str]:
|
|
6633
|
-
pfx = 'PYTHON_'
|
|
6634
|
-
return {k[len(pfx):].lower(): v for k, v in d.items() if k.startswith(pfx)}
|
|
6635
|
-
|
|
6636
|
-
@cached_nullary
|
|
6637
|
-
def pythons(self) -> ta.Mapping[str, str]:
|
|
6638
|
-
return self.get_pythons(self.contents())
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
##
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
@cached_nullary
|
|
6645
|
-
def _script_rel_path() -> str:
|
|
6646
|
-
cwd = os.getcwd()
|
|
6647
|
-
if not (f := __file__).startswith(cwd):
|
|
6648
|
-
raise OSError(f'file {f} not in {cwd}')
|
|
6649
|
-
return f[len(cwd):].lstrip(os.sep)
|
|
6879
|
+
# ../venvs.py
|
|
6650
6880
|
|
|
6651
6881
|
|
|
6652
6882
|
##
|
|
@@ -6692,12 +6922,12 @@ class Venv:
|
|
|
6692
6922
|
return False
|
|
6693
6923
|
|
|
6694
6924
|
log.info('Using interpreter %s', (ie := await self.interp_exe()))
|
|
6695
|
-
|
|
6925
|
+
await asyncio_subprocess_check_call(ie, '-m', 'venv', dn)
|
|
6696
6926
|
|
|
6697
6927
|
ve = self.exe()
|
|
6698
6928
|
uv = self._cfg.use_uv
|
|
6699
6929
|
|
|
6700
|
-
|
|
6930
|
+
await asyncio_subprocess_check_call(
|
|
6701
6931
|
ve,
|
|
6702
6932
|
'-m', 'pip',
|
|
6703
6933
|
'install', '-v', '--upgrade',
|
|
@@ -6715,7 +6945,7 @@ class Venv:
|
|
|
6715
6945
|
# Caused by: Failed to download distribution due to network timeout. Try increasing UV_HTTP_TIMEOUT (current value: 30s). # noqa
|
|
6716
6946
|
# UV_CONCURRENT_DOWNLOADS=4 UV_HTTP_TIMEOUT=3600
|
|
6717
6947
|
|
|
6718
|
-
|
|
6948
|
+
await asyncio_subprocess_check_call(
|
|
6719
6949
|
ve,
|
|
6720
6950
|
'-m',
|
|
6721
6951
|
*(['uv'] if uv else []),
|
|
@@ -6748,6 +6978,55 @@ class Venv:
|
|
|
6748
6978
|
return self._resolve_srcs(self._cfg.srcs or [])
|
|
6749
6979
|
|
|
6750
6980
|
|
|
6981
|
+
########################################
|
|
6982
|
+
# cli.py
|
|
6983
|
+
|
|
6984
|
+
|
|
6985
|
+
##
|
|
6986
|
+
|
|
6987
|
+
|
|
6988
|
+
@dc.dataclass(frozen=True)
|
|
6989
|
+
class VersionsFile:
|
|
6990
|
+
name: ta.Optional[str] = '.versions'
|
|
6991
|
+
|
|
6992
|
+
@staticmethod
|
|
6993
|
+
def parse(s: str) -> ta.Mapping[str, str]:
|
|
6994
|
+
return {
|
|
6995
|
+
k: v
|
|
6996
|
+
for l in s.splitlines()
|
|
6997
|
+
if (sl := l.split('#')[0].strip())
|
|
6998
|
+
for k, _, v in (sl.partition('='),)
|
|
6999
|
+
}
|
|
7000
|
+
|
|
7001
|
+
@cached_nullary
|
|
7002
|
+
def contents(self) -> ta.Mapping[str, str]:
|
|
7003
|
+
if not self.name or not os.path.exists(self.name):
|
|
7004
|
+
return {}
|
|
7005
|
+
with open(self.name) as f:
|
|
7006
|
+
s = f.read()
|
|
7007
|
+
return self.parse(s)
|
|
7008
|
+
|
|
7009
|
+
@staticmethod
|
|
7010
|
+
def get_pythons(d: ta.Mapping[str, str]) -> ta.Mapping[str, str]:
|
|
7011
|
+
pfx = 'PYTHON_'
|
|
7012
|
+
return {k[len(pfx):].lower(): v for k, v in d.items() if k.startswith(pfx)}
|
|
7013
|
+
|
|
7014
|
+
@cached_nullary
|
|
7015
|
+
def pythons(self) -> ta.Mapping[str, str]:
|
|
7016
|
+
return self.get_pythons(self.contents())
|
|
7017
|
+
|
|
7018
|
+
|
|
7019
|
+
##
|
|
7020
|
+
|
|
7021
|
+
|
|
7022
|
+
@cached_nullary
|
|
7023
|
+
def _script_rel_path() -> str:
|
|
7024
|
+
cwd = os.getcwd()
|
|
7025
|
+
if not (f := __file__).startswith(cwd):
|
|
7026
|
+
raise OSError(f'file {f} not in {cwd}')
|
|
7027
|
+
return f[len(cwd):].lstrip(os.sep)
|
|
7028
|
+
|
|
7029
|
+
|
|
6751
7030
|
##
|
|
6752
7031
|
|
|
6753
7032
|
|
|
@@ -6791,183 +7070,160 @@ class Run:
|
|
|
6791
7070
|
##
|
|
6792
7071
|
|
|
6793
7072
|
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
if (sd := venv.cfg.docker) is not None and sd != (cd := args._docker_container): # noqa
|
|
6797
|
-
script = ' '.join([
|
|
6798
|
-
'python3',
|
|
6799
|
-
shlex.quote(_script_rel_path()),
|
|
6800
|
-
f'--_docker_container={shlex.quote(sd)}',
|
|
6801
|
-
*map(shlex.quote, sys.argv[1:]),
|
|
6802
|
-
])
|
|
7073
|
+
class PyprojectCli(ArgparseCli):
|
|
7074
|
+
_docker_container = argparse_arg('--_docker_container', help=argparse.SUPPRESS)
|
|
6803
7075
|
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
*itertools.chain.from_iterable(
|
|
6820
|
-
('-e', f'{k}={v}')
|
|
6821
|
-
for k, v in docker_env.items()
|
|
6822
|
-
),
|
|
6823
|
-
'-it', sd,
|
|
6824
|
-
'bash', '--login', '-c', script,
|
|
6825
|
-
)
|
|
6826
|
-
|
|
6827
|
-
return
|
|
6828
|
-
|
|
6829
|
-
cmd = args.cmd
|
|
6830
|
-
if not cmd:
|
|
6831
|
-
await venv.create()
|
|
6832
|
-
|
|
6833
|
-
elif cmd == 'python':
|
|
6834
|
-
await venv.create()
|
|
6835
|
-
os.execl(
|
|
6836
|
-
(exe := venv.exe()),
|
|
6837
|
-
exe,
|
|
6838
|
-
*args.args,
|
|
6839
|
-
)
|
|
6840
|
-
|
|
6841
|
-
elif cmd == 'exe':
|
|
6842
|
-
await venv.create()
|
|
6843
|
-
check.arg(not args.args)
|
|
6844
|
-
print(venv.exe())
|
|
6845
|
-
|
|
6846
|
-
elif cmd == 'run':
|
|
6847
|
-
await venv.create()
|
|
6848
|
-
sh = check.not_none(shutil.which('bash'))
|
|
6849
|
-
script = ' '.join(args.args)
|
|
6850
|
-
if not script:
|
|
6851
|
-
script = sh
|
|
6852
|
-
os.execl(
|
|
6853
|
-
(bash := check.not_none(sh)),
|
|
6854
|
-
bash,
|
|
6855
|
-
'-c',
|
|
6856
|
-
f'. {venv.dir_name}/bin/activate && ' + script,
|
|
6857
|
-
)
|
|
7076
|
+
@argparse_command(
|
|
7077
|
+
argparse_arg('name'),
|
|
7078
|
+
argparse_arg('-e', '--docker-env', action='append'),
|
|
7079
|
+
argparse_arg('cmd', nargs='?'),
|
|
7080
|
+
argparse_arg('args', nargs=argparse.REMAINDER),
|
|
7081
|
+
)
|
|
7082
|
+
async def venv(self) -> None:
|
|
7083
|
+
venv = Run().venvs()[self.args.name]
|
|
7084
|
+
if (sd := venv.cfg.docker) is not None and sd != (cd := self.args._docker_container): # noqa
|
|
7085
|
+
script = ' '.join([
|
|
7086
|
+
'python3',
|
|
7087
|
+
shlex.quote(_script_rel_path()),
|
|
7088
|
+
f'--_docker_container={shlex.quote(sd)}',
|
|
7089
|
+
*map(shlex.quote, sys.argv[1:]),
|
|
7090
|
+
])
|
|
6858
7091
|
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
7092
|
+
docker_env = {
|
|
7093
|
+
'DOCKER_HOST_PLATFORM': os.environ.get('DOCKER_HOST_PLATFORM', sys.platform),
|
|
7094
|
+
}
|
|
7095
|
+
for e in self.args.docker_env or []:
|
|
7096
|
+
if '=' in e:
|
|
7097
|
+
k, _, v = e.split('=')
|
|
7098
|
+
docker_env[k] = v
|
|
7099
|
+
else:
|
|
7100
|
+
docker_env[e] = os.environ.get(e, '')
|
|
7101
|
+
|
|
7102
|
+
await asyncio_subprocess_check_call(
|
|
7103
|
+
'docker',
|
|
7104
|
+
'compose',
|
|
7105
|
+
'-f', 'docker/compose.yml',
|
|
7106
|
+
'exec',
|
|
7107
|
+
*itertools.chain.from_iterable(
|
|
7108
|
+
('-e', f'{k}={v}')
|
|
7109
|
+
for k, v in docker_env.items()
|
|
7110
|
+
),
|
|
7111
|
+
'-it', sd,
|
|
7112
|
+
'bash', '--login', '-c', script,
|
|
7113
|
+
)
|
|
6862
7114
|
|
|
6863
|
-
|
|
6864
|
-
await venv.create()
|
|
6865
|
-
subprocess_check_call(venv.exe(), '-m', 'pytest', *(args.args or []), *venv.srcs())
|
|
7115
|
+
return
|
|
6866
7116
|
|
|
6867
|
-
|
|
6868
|
-
|
|
7117
|
+
cmd = self.args.cmd
|
|
7118
|
+
if not cmd:
|
|
7119
|
+
await venv.create()
|
|
6869
7120
|
|
|
7121
|
+
elif cmd == 'python':
|
|
7122
|
+
await venv.create()
|
|
7123
|
+
os.execl(
|
|
7124
|
+
(exe := venv.exe()),
|
|
7125
|
+
exe,
|
|
7126
|
+
*self.args.args,
|
|
7127
|
+
)
|
|
6870
7128
|
|
|
6871
|
-
|
|
7129
|
+
elif cmd == 'exe':
|
|
7130
|
+
await venv.create()
|
|
7131
|
+
check.arg(not self.args.args)
|
|
7132
|
+
print(venv.exe())
|
|
7133
|
+
|
|
7134
|
+
elif cmd == 'run':
|
|
7135
|
+
await venv.create()
|
|
7136
|
+
sh = check.not_none(shutil.which('bash'))
|
|
7137
|
+
script = ' '.join(self.args.args)
|
|
7138
|
+
if not script:
|
|
7139
|
+
script = sh
|
|
7140
|
+
os.execl(
|
|
7141
|
+
(bash := check.not_none(sh)),
|
|
7142
|
+
bash,
|
|
7143
|
+
'-c',
|
|
7144
|
+
f'. {venv.dir_name}/bin/activate && ' + script,
|
|
7145
|
+
)
|
|
6872
7146
|
|
|
7147
|
+
elif cmd == 'srcs':
|
|
7148
|
+
check.arg(not self.args.args)
|
|
7149
|
+
print('\n'.join(venv.srcs()))
|
|
6873
7150
|
|
|
6874
|
-
|
|
6875
|
-
|
|
7151
|
+
elif cmd == 'test':
|
|
7152
|
+
await venv.create()
|
|
7153
|
+
await asyncio_subprocess_check_call(venv.exe(), '-m', 'pytest', *(self.args.args or []), *venv.srcs())
|
|
6876
7154
|
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
7155
|
+
else:
|
|
7156
|
+
raise Exception(f'unknown subcommand: {cmd}')
|
|
7157
|
+
|
|
7158
|
+
@argparse_command(
|
|
7159
|
+
argparse_arg('-b', '--build', action='store_true'),
|
|
7160
|
+
argparse_arg('-r', '--revision', action='store_true'),
|
|
7161
|
+
argparse_arg('-j', '--jobs', type=int),
|
|
7162
|
+
argparse_arg('cmd', nargs='?'),
|
|
7163
|
+
argparse_arg('args', nargs=argparse.REMAINDER),
|
|
7164
|
+
)
|
|
7165
|
+
async def pkg(self) -> None:
|
|
7166
|
+
run = Run()
|
|
6880
7167
|
|
|
6881
|
-
|
|
6882
|
-
|
|
7168
|
+
cmd = self.args.cmd
|
|
7169
|
+
if not cmd:
|
|
7170
|
+
raise Exception('must specify command')
|
|
6883
7171
|
|
|
6884
|
-
|
|
6885
|
-
|
|
7172
|
+
elif cmd == 'gen':
|
|
7173
|
+
pkgs_root = os.path.join('.pkg')
|
|
6886
7174
|
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
add_revision = bool(args.revision)
|
|
7175
|
+
if os.path.exists(pkgs_root):
|
|
7176
|
+
shutil.rmtree(pkgs_root)
|
|
6890
7177
|
|
|
6891
|
-
|
|
6892
|
-
|
|
7178
|
+
build_output_dir = 'dist'
|
|
7179
|
+
run_build = bool(self.args.build)
|
|
7180
|
+
add_revision = bool(self.args.revision)
|
|
6893
7181
|
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
dir_name,
|
|
6897
|
-
pkgs_root,
|
|
6898
|
-
)
|
|
6899
|
-
for dir_name in run.cfg().pkgs
|
|
6900
|
-
]
|
|
6901
|
-
pgs = list(itertools.chain.from_iterable([pg, *pg.children()] for pg in pgs))
|
|
7182
|
+
if run_build:
|
|
7183
|
+
os.makedirs(build_output_dir, exist_ok=True)
|
|
6902
7184
|
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
7185
|
+
pgs: ta.List[BasePyprojectPackageGenerator] = [
|
|
7186
|
+
PyprojectPackageGenerator(
|
|
7187
|
+
dir_name,
|
|
7188
|
+
pkgs_root,
|
|
7189
|
+
)
|
|
7190
|
+
for dir_name in run.cfg().pkgs
|
|
7191
|
+
]
|
|
7192
|
+
pgs = list(itertools.chain.from_iterable([pg, *pg.children()] for pg in pgs))
|
|
6909
7193
|
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
build_output_dir,
|
|
6915
|
-
BasePyprojectPackageGenerator.BuildOpts(
|
|
6916
|
-
add_revision=add_revision,
|
|
6917
|
-
),
|
|
6918
|
-
))
|
|
6919
|
-
for pg in pgs
|
|
6920
|
-
]
|
|
7194
|
+
num_threads = self.args.jobs or int(max(mp.cpu_count() // 1.5, 1))
|
|
7195
|
+
futs: ta.List[cf.Future]
|
|
7196
|
+
with cf.ThreadPoolExecutor(num_threads) as ex:
|
|
7197
|
+
futs = [ex.submit(pg.gen) for pg in pgs]
|
|
6921
7198
|
for fut in futs:
|
|
6922
7199
|
fut.result()
|
|
6923
7200
|
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
7201
|
+
if run_build:
|
|
7202
|
+
futs = [
|
|
7203
|
+
ex.submit(functools.partial(
|
|
7204
|
+
pg.build,
|
|
7205
|
+
build_output_dir,
|
|
7206
|
+
BasePyprojectPackageGenerator.BuildOpts(
|
|
7207
|
+
add_revision=add_revision,
|
|
7208
|
+
),
|
|
7209
|
+
))
|
|
7210
|
+
for pg in pgs
|
|
7211
|
+
]
|
|
7212
|
+
for fut in futs:
|
|
7213
|
+
fut.result()
|
|
6927
7214
|
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
def _build_parser() -> argparse.ArgumentParser:
|
|
6932
|
-
parser = argparse.ArgumentParser()
|
|
6933
|
-
parser.add_argument('--_docker_container', help=argparse.SUPPRESS)
|
|
6934
|
-
|
|
6935
|
-
subparsers = parser.add_subparsers()
|
|
6936
|
-
|
|
6937
|
-
#
|
|
6938
|
-
|
|
6939
|
-
parser_venv = subparsers.add_parser('venv')
|
|
6940
|
-
parser_venv.add_argument('name')
|
|
6941
|
-
parser_venv.add_argument('-e', '--docker-env', action='append')
|
|
6942
|
-
parser_venv.add_argument('cmd', nargs='?')
|
|
6943
|
-
parser_venv.add_argument('args', nargs=argparse.REMAINDER)
|
|
6944
|
-
parser_venv.set_defaults(func=_venv_cmd)
|
|
6945
|
-
|
|
6946
|
-
#
|
|
7215
|
+
else:
|
|
7216
|
+
raise Exception(f'unknown subcommand: {cmd}')
|
|
6947
7217
|
|
|
6948
|
-
parser_pkg = subparsers.add_parser('pkg')
|
|
6949
|
-
parser_pkg.add_argument('-b', '--build', action='store_true')
|
|
6950
|
-
parser_pkg.add_argument('-r', '--revision', action='store_true')
|
|
6951
|
-
parser_pkg.add_argument('-j', '--jobs', type=int)
|
|
6952
|
-
parser_pkg.add_argument('cmd', nargs='?')
|
|
6953
|
-
parser_pkg.add_argument('args', nargs=argparse.REMAINDER)
|
|
6954
|
-
parser_pkg.set_defaults(func=_pkg_cmd)
|
|
6955
7218
|
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
return parser
|
|
7219
|
+
##
|
|
6959
7220
|
|
|
6960
7221
|
|
|
6961
7222
|
async def _async_main(argv: ta.Optional[ta.Sequence[str]] = None) -> None:
|
|
6962
7223
|
check_runtime_version()
|
|
6963
7224
|
configure_standard_logging()
|
|
6964
7225
|
|
|
6965
|
-
|
|
6966
|
-
args = parser.parse_args(argv)
|
|
6967
|
-
if not getattr(args, 'func', None):
|
|
6968
|
-
parser.print_help()
|
|
6969
|
-
else:
|
|
6970
|
-
await args.func(args)
|
|
7226
|
+
await PyprojectCli(argv).async_cli_run()
|
|
6971
7227
|
|
|
6972
7228
|
|
|
6973
7229
|
def _main(argv: ta.Optional[ta.Sequence[str]] = None) -> None:
|