ominfra 0.0.0.dev149__py3-none-any.whl → 0.0.0.dev150__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
ominfra/scripts/manage.py CHANGED
@@ -9,6 +9,7 @@ manage.py -s 'docker run -i python:3.12'
9
9
  manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
10
10
  """
11
11
  import abc
12
+ import argparse
12
13
  import asyncio
13
14
  import asyncio.base_subprocess
14
15
  import asyncio.subprocess
@@ -76,6 +77,11 @@ CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
76
77
 
77
78
  # ../../omlish/lite/check.py
78
79
  SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
80
+ CheckMessage = ta.Union[str, ta.Callable[..., ta.Optional[str]], None] # ta.TypeAlias
81
+ CheckLateConfigureFn = ta.Callable[['Checks'], None]
82
+ CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
83
+ CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
84
+ CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
79
85
 
80
86
  # ../../omdev/packaging/specifiers.py
81
87
  UnparsedVersion = ta.Union['Version', str]
@@ -86,6 +92,9 @@ CallableVersionOperator = ta.Callable[['Version', str], bool]
86
92
  CommandT = ta.TypeVar('CommandT', bound='Command')
87
93
  CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
88
94
 
95
+ # ../../omlish/argparse/cli.py
96
+ ArgparseCommandFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
97
+
89
98
  # ../../omlish/lite/inject.py
90
99
  U = ta.TypeVar('U')
91
100
  InjectorKeyCls = ta.Union[type, ta.NewType]
@@ -1152,104 +1161,454 @@ def async_cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
1152
1161
 
1153
1162
  ########################################
1154
1163
  # ../../../omlish/lite/check.py
1164
+ """
1165
+ TODO:
1166
+ - def maybe(v: lang.Maybe[T])
1167
+ - patch / override lite.check ?
1168
+ - checker interface?
1169
+ """
1170
+
1171
+
1172
+ ##
1173
+
1174
+
1175
+ class Checks:
1176
+ def __init__(self) -> None:
1177
+ super().__init__()
1178
+
1179
+ self._config_lock = threading.RLock()
1180
+ self._on_raise_fns: ta.Sequence[CheckOnRaiseFn] = []
1181
+ self._exception_factory: CheckExceptionFactory = Checks.default_exception_factory
1182
+ self._args_renderer: ta.Optional[CheckArgsRenderer] = None
1183
+ self._late_configure_fns: ta.Sequence[CheckLateConfigureFn] = []
1184
+
1185
+ @staticmethod
1186
+ def default_exception_factory(exc_cls: ta.Type[Exception], *args, **kwargs) -> Exception:
1187
+ return exc_cls(*args, **kwargs) # noqa
1188
+
1189
+ #
1190
+
1191
+ def register_on_raise(self, fn: CheckOnRaiseFn) -> None:
1192
+ with self._config_lock:
1193
+ self._on_raise_fns = [*self._on_raise_fns, fn]
1194
+
1195
+ def unregister_on_raise(self, fn: CheckOnRaiseFn) -> None:
1196
+ with self._config_lock:
1197
+ self._on_raise_fns = [e for e in self._on_raise_fns if e != fn]
1198
+
1199
+ #
1200
+
1201
+ def set_exception_factory(self, factory: CheckExceptionFactory) -> None:
1202
+ self._exception_factory = factory
1203
+
1204
+ def set_args_renderer(self, renderer: ta.Optional[CheckArgsRenderer]) -> None:
1205
+ self._args_renderer = renderer
1206
+
1207
+ #
1208
+
1209
+ def register_late_configure(self, fn: CheckLateConfigureFn) -> None:
1210
+ with self._config_lock:
1211
+ self._late_configure_fns = [*self._late_configure_fns, fn]
1212
+
1213
+ def _late_configure(self) -> None:
1214
+ if not self._late_configure_fns:
1215
+ return
1216
+
1217
+ with self._config_lock:
1218
+ if not (lc := self._late_configure_fns):
1219
+ return
1220
+
1221
+ for fn in lc:
1222
+ fn(self)
1223
+
1224
+ self._late_configure_fns = []
1225
+
1226
+ #
1227
+
1228
+ class _ArgsKwargs:
1229
+ def __init__(self, *args, **kwargs):
1230
+ self.args = args
1231
+ self.kwargs = kwargs
1232
+
1233
+ def _raise(
1234
+ self,
1235
+ exception_type: ta.Type[Exception],
1236
+ default_message: str,
1237
+ message: CheckMessage,
1238
+ ak: _ArgsKwargs = _ArgsKwargs(),
1239
+ *,
1240
+ render_fmt: ta.Optional[str] = None,
1241
+ ) -> ta.NoReturn:
1242
+ exc_args = ()
1243
+ if callable(message):
1244
+ message = ta.cast(ta.Callable, message)(*ak.args, **ak.kwargs)
1245
+ if isinstance(message, tuple):
1246
+ message, *exc_args = message # type: ignore
1247
+
1248
+ if message is None:
1249
+ message = default_message
1250
+
1251
+ self._late_configure()
1252
+
1253
+ if render_fmt is not None and (af := self._args_renderer) is not None:
1254
+ rendered_args = af(render_fmt, *ak.args)
1255
+ if rendered_args is not None:
1256
+ message = f'{message} : {rendered_args}'
1257
+
1258
+ exc = self._exception_factory(
1259
+ exception_type,
1260
+ message,
1261
+ *exc_args,
1262
+ *ak.args,
1263
+ **ak.kwargs,
1264
+ )
1265
+
1266
+ for fn in self._on_raise_fns:
1267
+ fn(exc)
1268
+
1269
+ raise exc
1270
+
1271
+ #
1272
+
1273
+ def _unpack_isinstance_spec(self, spec: ta.Any) -> tuple:
1274
+ if isinstance(spec, type):
1275
+ return (spec,)
1276
+ if not isinstance(spec, tuple):
1277
+ spec = (spec,)
1278
+ if None in spec:
1279
+ spec = tuple(filter(None, spec)) + (None.__class__,) # noqa
1280
+ if ta.Any in spec:
1281
+ spec = (object,)
1282
+ return spec
1283
+
1284
+ def isinstance(self, v: ta.Any, spec: ta.Union[ta.Type[T], tuple], msg: CheckMessage = None) -> T: # noqa
1285
+ if not isinstance(v, self._unpack_isinstance_spec(spec)):
1286
+ self._raise(
1287
+ TypeError,
1288
+ 'Must be instance',
1289
+ msg,
1290
+ Checks._ArgsKwargs(v, spec),
1291
+ render_fmt='not isinstance(%s, %s)',
1292
+ )
1293
+
1294
+ return v
1295
+
1296
+ def of_isinstance(self, spec: ta.Union[ta.Type[T], tuple], msg: CheckMessage = None) -> ta.Callable[[ta.Any], T]:
1297
+ def inner(v):
1298
+ return self.isinstance(v, self._unpack_isinstance_spec(spec), msg)
1299
+
1300
+ return inner
1301
+
1302
+ def cast(self, v: ta.Any, cls: ta.Type[T], msg: CheckMessage = None) -> T: # noqa
1303
+ if not isinstance(v, cls):
1304
+ self._raise(
1305
+ TypeError,
1306
+ 'Must be instance',
1307
+ msg,
1308
+ Checks._ArgsKwargs(v, cls),
1309
+ )
1310
+
1311
+ return v
1155
1312
 
1313
+ def of_cast(self, cls: ta.Type[T], msg: CheckMessage = None) -> ta.Callable[[T], T]:
1314
+ def inner(v):
1315
+ return self.cast(v, cls, msg)
1156
1316
 
1157
- def check_isinstance(v: ta.Any, spec: ta.Union[ta.Type[T], tuple]) -> T:
1158
- if not isinstance(v, spec):
1159
- raise TypeError(v)
1160
- return v
1317
+ return inner
1161
1318
 
1319
+ def not_isinstance(self, v: T, spec: ta.Any, msg: CheckMessage = None) -> T: # noqa
1320
+ if isinstance(v, self._unpack_isinstance_spec(spec)):
1321
+ self._raise(
1322
+ TypeError,
1323
+ 'Must not be instance',
1324
+ msg,
1325
+ Checks._ArgsKwargs(v, spec),
1326
+ render_fmt='isinstance(%s, %s)',
1327
+ )
1162
1328
 
1163
- def check_not_isinstance(v: T, spec: ta.Union[type, tuple]) -> T:
1164
- if isinstance(v, spec):
1165
- raise TypeError(v)
1166
- return v
1329
+ return v
1167
1330
 
1331
+ def of_not_isinstance(self, spec: ta.Any, msg: CheckMessage = None) -> ta.Callable[[T], T]:
1332
+ def inner(v):
1333
+ return self.not_isinstance(v, self._unpack_isinstance_spec(spec), msg)
1168
1334
 
1169
- def check_none(v: T) -> None:
1170
- if v is not None:
1171
- raise ValueError(v)
1335
+ return inner
1172
1336
 
1337
+ ##
1173
1338
 
1174
- def check_not_none(v: ta.Optional[T]) -> T:
1175
- if v is None:
1176
- raise ValueError
1177
- return v
1339
+ def issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]: # noqa
1340
+ if not issubclass(v, spec):
1341
+ self._raise(
1342
+ TypeError,
1343
+ 'Must be subclass',
1344
+ msg,
1345
+ Checks._ArgsKwargs(v, spec),
1346
+ render_fmt='not issubclass(%s, %s)',
1347
+ )
1178
1348
 
1349
+ return v
1179
1350
 
1180
- def check_not(v: ta.Any) -> None:
1181
- if v:
1182
- raise ValueError(v)
1183
- return v
1351
+ def not_issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]: # noqa
1352
+ if issubclass(v, spec):
1353
+ self._raise(
1354
+ TypeError,
1355
+ 'Must not be subclass',
1356
+ msg,
1357
+ Checks._ArgsKwargs(v, spec),
1358
+ render_fmt='issubclass(%s, %s)',
1359
+ )
1184
1360
 
1361
+ return v
1185
1362
 
1186
- def check_non_empty_str(v: ta.Optional[str]) -> str:
1187
- if not v:
1188
- raise ValueError
1189
- return v
1363
+ #
1190
1364
 
1365
+ def in_(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
1366
+ if v not in c:
1367
+ self._raise(
1368
+ ValueError,
1369
+ 'Must be in',
1370
+ msg,
1371
+ Checks._ArgsKwargs(v, c),
1372
+ render_fmt='%s not in %s',
1373
+ )
1191
1374
 
1192
- def check_arg(v: bool, msg: str = 'Illegal argument') -> None:
1193
- if not v:
1194
- raise ValueError(msg)
1375
+ return v
1195
1376
 
1377
+ def not_in(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
1378
+ if v in c:
1379
+ self._raise(
1380
+ ValueError,
1381
+ 'Must not be in',
1382
+ msg,
1383
+ Checks._ArgsKwargs(v, c),
1384
+ render_fmt='%s in %s',
1385
+ )
1196
1386
 
1197
- def check_state(v: bool, msg: str = 'Illegal state') -> None:
1198
- if not v:
1199
- raise ValueError(msg)
1387
+ return v
1200
1388
 
1389
+ def empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
1390
+ if len(v) != 0:
1391
+ self._raise(
1392
+ ValueError,
1393
+ 'Must be empty',
1394
+ msg,
1395
+ Checks._ArgsKwargs(v),
1396
+ render_fmt='%s',
1397
+ )
1201
1398
 
1202
- def check_equal(l: T, r: T) -> T:
1203
- if l != r:
1204
- raise ValueError(l, r)
1205
- return l
1399
+ return v
1206
1400
 
1401
+ def iterempty(self, v: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
1402
+ it = iter(v)
1403
+ try:
1404
+ next(it)
1405
+ except StopIteration:
1406
+ pass
1407
+ else:
1408
+ self._raise(
1409
+ ValueError,
1410
+ 'Must be empty',
1411
+ msg,
1412
+ Checks._ArgsKwargs(v),
1413
+ render_fmt='%s',
1414
+ )
1207
1415
 
1208
- def check_not_equal(l: T, r: T) -> T:
1209
- if l == r:
1210
- raise ValueError(l, r)
1211
- return l
1416
+ return v
1212
1417
 
1418
+ def not_empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
1419
+ if len(v) == 0:
1420
+ self._raise(
1421
+ ValueError,
1422
+ 'Must not be empty',
1423
+ msg,
1424
+ Checks._ArgsKwargs(v),
1425
+ render_fmt='%s',
1426
+ )
1213
1427
 
1214
- def check_is(l: T, r: T) -> T:
1215
- if l is not r:
1216
- raise ValueError(l, r)
1217
- return l
1428
+ return v
1218
1429
 
1430
+ def unique(self, it: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
1431
+ dupes = [e for e, c in collections.Counter(it).items() if c > 1]
1432
+ if dupes:
1433
+ self._raise(
1434
+ ValueError,
1435
+ 'Must be unique',
1436
+ msg,
1437
+ Checks._ArgsKwargs(it, dupes),
1438
+ )
1219
1439
 
1220
- def check_is_not(l: T, r: ta.Any) -> T:
1221
- if l is r:
1222
- raise ValueError(l, r)
1223
- return l
1440
+ return it
1224
1441
 
1442
+ def single(self, obj: ta.Iterable[T], message: CheckMessage = None) -> T:
1443
+ try:
1444
+ [value] = obj
1445
+ except ValueError:
1446
+ self._raise(
1447
+ ValueError,
1448
+ 'Must be single',
1449
+ message,
1450
+ Checks._ArgsKwargs(obj),
1451
+ render_fmt='%s',
1452
+ )
1453
+
1454
+ return value
1455
+
1456
+ def opt_single(self, obj: ta.Iterable[T], message: CheckMessage = None) -> ta.Optional[T]:
1457
+ it = iter(obj)
1458
+ try:
1459
+ value = next(it)
1460
+ except StopIteration:
1461
+ return None
1462
+
1463
+ try:
1464
+ next(it)
1465
+ except StopIteration:
1466
+ return value # noqa
1467
+
1468
+ self._raise(
1469
+ ValueError,
1470
+ 'Must be empty or single',
1471
+ message,
1472
+ Checks._ArgsKwargs(obj),
1473
+ render_fmt='%s',
1474
+ )
1475
+
1476
+ raise RuntimeError # noqa
1225
1477
 
1226
- def check_in(v: T, c: ta.Container[T]) -> T:
1227
- if v not in c:
1228
- raise ValueError(v, c)
1229
- return v
1478
+ #
1230
1479
 
1480
+ def none(self, v: ta.Any, msg: CheckMessage = None) -> None:
1481
+ if v is not None:
1482
+ self._raise(
1483
+ ValueError,
1484
+ 'Must be None',
1485
+ msg,
1486
+ Checks._ArgsKwargs(v),
1487
+ render_fmt='%s',
1488
+ )
1231
1489
 
1232
- def check_not_in(v: T, c: ta.Container[T]) -> T:
1233
- if v in c:
1234
- raise ValueError(v, c)
1235
- return v
1490
+ def not_none(self, v: ta.Optional[T], msg: CheckMessage = None) -> T:
1491
+ if v is None:
1492
+ self._raise(
1493
+ ValueError,
1494
+ 'Must not be None',
1495
+ msg,
1496
+ Checks._ArgsKwargs(v),
1497
+ render_fmt='%s',
1498
+ )
1236
1499
 
1500
+ return v
1237
1501
 
1238
- def check_single(vs: ta.Iterable[T]) -> T:
1239
- [v] = vs
1240
- return v
1502
+ #
1503
+
1504
+ def equal(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
1505
+ if o != v:
1506
+ self._raise(
1507
+ ValueError,
1508
+ 'Must be equal',
1509
+ msg,
1510
+ Checks._ArgsKwargs(v, o),
1511
+ render_fmt='%s != %s',
1512
+ )
1513
+
1514
+ return v
1515
+
1516
+ def is_(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
1517
+ if o is not v:
1518
+ self._raise(
1519
+ ValueError,
1520
+ 'Must be the same',
1521
+ msg,
1522
+ Checks._ArgsKwargs(v, o),
1523
+ render_fmt='%s is not %s',
1524
+ )
1525
+
1526
+ return v
1527
+
1528
+ def is_not(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
1529
+ if o is v:
1530
+ self._raise(
1531
+ ValueError,
1532
+ 'Must not be the same',
1533
+ msg,
1534
+ Checks._ArgsKwargs(v, o),
1535
+ render_fmt='%s is %s',
1536
+ )
1537
+
1538
+ return v
1539
+
1540
+ def callable(self, v: T, msg: CheckMessage = None) -> T: # noqa
1541
+ if not callable(v):
1542
+ self._raise(
1543
+ TypeError,
1544
+ 'Must be callable',
1545
+ msg,
1546
+ Checks._ArgsKwargs(v),
1547
+ render_fmt='%s',
1548
+ )
1549
+
1550
+ return v # type: ignore
1551
+
1552
+ def non_empty_str(self, v: ta.Optional[str], msg: CheckMessage = None) -> str:
1553
+ if not isinstance(v, str) or not v:
1554
+ self._raise(
1555
+ ValueError,
1556
+ 'Must be non-empty str',
1557
+ msg,
1558
+ Checks._ArgsKwargs(v),
1559
+ render_fmt='%s',
1560
+ )
1561
+
1562
+ return v
1563
+
1564
+ def replacing(self, expected: ta.Any, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
1565
+ if old != expected:
1566
+ self._raise(
1567
+ ValueError,
1568
+ 'Must be replacing',
1569
+ msg,
1570
+ Checks._ArgsKwargs(expected, old, new),
1571
+ render_fmt='%s -> %s -> %s',
1572
+ )
1573
+
1574
+ return new
1575
+
1576
+ def replacing_none(self, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
1577
+ if old is not None:
1578
+ self._raise(
1579
+ ValueError,
1580
+ 'Must be replacing None',
1581
+ msg,
1582
+ Checks._ArgsKwargs(old, new),
1583
+ render_fmt='%s -> %s',
1584
+ )
1585
+
1586
+ return new
1587
+
1588
+ #
1241
1589
 
1590
+ def arg(self, v: bool, msg: CheckMessage = None) -> None:
1591
+ if not v:
1592
+ self._raise(
1593
+ RuntimeError,
1594
+ 'Argument condition not met',
1595
+ msg,
1596
+ Checks._ArgsKwargs(v),
1597
+ render_fmt='%s',
1598
+ )
1242
1599
 
1243
- def check_empty(v: SizedT) -> SizedT:
1244
- if len(v):
1245
- raise ValueError(v)
1246
- return v
1600
+ def state(self, v: bool, msg: CheckMessage = None) -> None:
1601
+ if not v:
1602
+ self._raise(
1603
+ RuntimeError,
1604
+ 'State condition not met',
1605
+ msg,
1606
+ Checks._ArgsKwargs(v),
1607
+ render_fmt='%s',
1608
+ )
1247
1609
 
1248
1610
 
1249
- def check_not_empty(v: SizedT) -> SizedT:
1250
- if not len(v):
1251
- raise ValueError(v)
1252
- return v
1611
+ check = Checks()
1253
1612
 
1254
1613
 
1255
1614
  ########################################
@@ -2058,7 +2417,7 @@ class Command(abc.ABC, ta.Generic[CommandOutputT]):
2058
2417
 
2059
2418
  @ta.final
2060
2419
  async def execute(self, executor: 'CommandExecutor') -> CommandOutputT:
2061
- return check_isinstance(await executor.execute(self), self.Output) # type: ignore[return-value]
2420
+ return check.isinstance(await executor.execute(self), self.Output) # type: ignore[return-value]
2062
2421
 
2063
2422
 
2064
2423
  ##
@@ -2253,6 +2612,237 @@ def get_remote_payload_src(
2253
2612
  return importlib.resources.files(__package__.split('.')[0] + '.scripts').joinpath('manage.py').read_text()
2254
2613
 
2255
2614
 
2615
+ ########################################
2616
+ # ../../../omlish/argparse/cli.py
2617
+ """
2618
+ TODO:
2619
+ - default command
2620
+ - auto match all underscores to hyphens
2621
+ """
2622
+
2623
+
2624
+ ##
2625
+
2626
+
2627
+ @dc.dataclass(eq=False)
2628
+ class ArgparseArg:
2629
+ args: ta.Sequence[ta.Any]
2630
+ kwargs: ta.Mapping[str, ta.Any]
2631
+ dest: ta.Optional[str] = None
2632
+
2633
+ def __get__(self, instance, owner=None):
2634
+ if instance is None:
2635
+ return self
2636
+ return getattr(instance.args, self.dest) # type: ignore
2637
+
2638
+
2639
+ def argparse_arg(*args, **kwargs) -> ArgparseArg:
2640
+ return ArgparseArg(args, kwargs)
2641
+
2642
+
2643
+ #
2644
+
2645
+
2646
+ @dc.dataclass(eq=False)
2647
+ class ArgparseCommand:
2648
+ name: str
2649
+ fn: ArgparseCommandFn
2650
+ args: ta.Sequence[ArgparseArg] = () # noqa
2651
+
2652
+ # _: dc.KW_ONLY
2653
+
2654
+ aliases: ta.Optional[ta.Sequence[str]] = None
2655
+ parent: ta.Optional['ArgparseCommand'] = None
2656
+ accepts_unknown: bool = False
2657
+
2658
+ def __post_init__(self) -> None:
2659
+ def check_name(s: str) -> None:
2660
+ check.isinstance(s, str)
2661
+ check.not_in('_', s)
2662
+ check.not_empty(s)
2663
+ check_name(self.name)
2664
+ check.not_isinstance(self.aliases, str)
2665
+ for a in self.aliases or []:
2666
+ check_name(a)
2667
+
2668
+ check.arg(callable(self.fn))
2669
+ check.arg(all(isinstance(a, ArgparseArg) for a in self.args))
2670
+ check.isinstance(self.parent, (ArgparseCommand, type(None)))
2671
+ check.isinstance(self.accepts_unknown, bool)
2672
+
2673
+ functools.update_wrapper(self, self.fn)
2674
+
2675
+ def __get__(self, instance, owner=None):
2676
+ if instance is None:
2677
+ return self
2678
+ return dc.replace(self, fn=self.fn.__get__(instance, owner)) # noqa
2679
+
2680
+ def __call__(self, *args, **kwargs) -> ta.Optional[int]:
2681
+ return self.fn(*args, **kwargs)
2682
+
2683
+
2684
+ def argparse_command(
2685
+ *args: ArgparseArg,
2686
+ name: ta.Optional[str] = None,
2687
+ aliases: ta.Optional[ta.Iterable[str]] = None,
2688
+ parent: ta.Optional[ArgparseCommand] = None,
2689
+ accepts_unknown: bool = False,
2690
+ ) -> ta.Any: # ta.Callable[[ArgparseCommandFn], ArgparseCommand]: # FIXME
2691
+ for arg in args:
2692
+ check.isinstance(arg, ArgparseArg)
2693
+ check.isinstance(name, (str, type(None)))
2694
+ check.isinstance(parent, (ArgparseCommand, type(None)))
2695
+ check.not_isinstance(aliases, str)
2696
+
2697
+ def inner(fn):
2698
+ return ArgparseCommand(
2699
+ (name if name is not None else fn.__name__).replace('_', '-'),
2700
+ fn,
2701
+ args,
2702
+ aliases=tuple(aliases) if aliases is not None else None,
2703
+ parent=parent,
2704
+ accepts_unknown=accepts_unknown,
2705
+ )
2706
+
2707
+ return inner
2708
+
2709
+
2710
+ ##
2711
+
2712
+
2713
+ def _get_argparse_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
2714
+ if ann is str:
2715
+ return {}
2716
+ elif ann is int:
2717
+ return {'type': int}
2718
+ elif ann is bool:
2719
+ return {'action': 'store_true'}
2720
+ elif ann is list:
2721
+ return {'action': 'append'}
2722
+ else:
2723
+ raise TypeError(ann)
2724
+
2725
+
2726
+ class _ArgparseCliAnnotationBox:
2727
+ def __init__(self, annotations: ta.Mapping[str, ta.Any]) -> None:
2728
+ super().__init__()
2729
+ self.__annotations__ = annotations # type: ignore
2730
+
2731
+
2732
+ class ArgparseCli:
2733
+ def __init__(self, argv: ta.Optional[ta.Sequence[str]] = None) -> None:
2734
+ super().__init__()
2735
+
2736
+ self._argv = argv if argv is not None else sys.argv[1:]
2737
+
2738
+ self._args, self._unknown_args = self.get_parser().parse_known_args(self._argv)
2739
+
2740
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
2741
+ super().__init_subclass__(**kwargs)
2742
+
2743
+ ns = cls.__dict__
2744
+
2745
+ objs = {}
2746
+ mro = cls.__mro__[::-1]
2747
+ for bns in [bcls.__dict__ for bcls in reversed(mro)] + [ns]:
2748
+ bseen = set() # type: ignore
2749
+ for k, v in bns.items():
2750
+ if isinstance(v, (ArgparseCommand, ArgparseArg)):
2751
+ check.not_in(v, bseen)
2752
+ bseen.add(v)
2753
+ objs[k] = v
2754
+ elif k in objs:
2755
+ del [k]
2756
+
2757
+ anns = ta.get_type_hints(_ArgparseCliAnnotationBox({
2758
+ **{k: v for bcls in reversed(mro) for k, v in getattr(bcls, '__annotations__', {}).items()},
2759
+ **ns.get('__annotations__', {}),
2760
+ }), globalns=ns.get('__globals__', {}))
2761
+
2762
+ if '_parser' in ns:
2763
+ parser = check.isinstance(ns['_parser'], argparse.ArgumentParser)
2764
+ else:
2765
+ parser = argparse.ArgumentParser()
2766
+ setattr(cls, '_parser', parser)
2767
+
2768
+ subparsers = parser.add_subparsers()
2769
+ for att, obj in objs.items():
2770
+ if isinstance(obj, ArgparseCommand):
2771
+ if obj.parent is not None:
2772
+ raise NotImplementedError
2773
+ for cn in [obj.name, *(obj.aliases or [])]:
2774
+ cparser = subparsers.add_parser(cn)
2775
+ for arg in (obj.args or []):
2776
+ if (
2777
+ len(arg.args) == 1 and
2778
+ isinstance(arg.args[0], str) and
2779
+ not (n := check.isinstance(arg.args[0], str)).startswith('-') and
2780
+ 'metavar' not in arg.kwargs
2781
+ ):
2782
+ cparser.add_argument(
2783
+ n.replace('-', '_'),
2784
+ **arg.kwargs,
2785
+ metavar=n,
2786
+ )
2787
+ else:
2788
+ cparser.add_argument(*arg.args, **arg.kwargs)
2789
+ cparser.set_defaults(_cmd=obj)
2790
+
2791
+ elif isinstance(obj, ArgparseArg):
2792
+ if att in anns:
2793
+ akwargs = _get_argparse_arg_ann_kwargs(anns[att])
2794
+ obj.kwargs = {**akwargs, **obj.kwargs}
2795
+ if not obj.dest:
2796
+ if 'dest' in obj.kwargs:
2797
+ obj.dest = obj.kwargs['dest']
2798
+ else:
2799
+ obj.dest = obj.kwargs['dest'] = att # type: ignore
2800
+ parser.add_argument(*obj.args, **obj.kwargs)
2801
+
2802
+ else:
2803
+ raise TypeError(obj)
2804
+
2805
+ _parser: ta.ClassVar[argparse.ArgumentParser]
2806
+
2807
+ @classmethod
2808
+ def get_parser(cls) -> argparse.ArgumentParser:
2809
+ return cls._parser
2810
+
2811
+ @property
2812
+ def argv(self) -> ta.Sequence[str]:
2813
+ return self._argv
2814
+
2815
+ @property
2816
+ def args(self) -> argparse.Namespace:
2817
+ return self._args
2818
+
2819
+ @property
2820
+ def unknown_args(self) -> ta.Sequence[str]:
2821
+ return self._unknown_args
2822
+
2823
+ def _run_cmd(self, cmd: ArgparseCommand) -> ta.Optional[int]:
2824
+ return cmd.__get__(self, type(self))()
2825
+
2826
+ def __call__(self) -> ta.Optional[int]:
2827
+ cmd = getattr(self.args, '_cmd', None)
2828
+
2829
+ if self._unknown_args and not (cmd is not None and cmd.accepts_unknown):
2830
+ msg = f'unrecognized arguments: {" ".join(self._unknown_args)}'
2831
+ if (parser := self.get_parser()).exit_on_error: # type: ignore
2832
+ parser.error(msg)
2833
+ else:
2834
+ raise argparse.ArgumentError(None, msg)
2835
+
2836
+ if cmd is None:
2837
+ self.get_parser().print_help()
2838
+ return 0
2839
+
2840
+ return self._run_cmd(cmd)
2841
+
2842
+ def call_and_exit(self) -> ta.NoReturn:
2843
+ sys.exit(rc if isinstance(rc := self(), int) else 0)
2844
+
2845
+
2256
2846
  ########################################
2257
2847
  # ../../../omlish/lite/inject.py
2258
2848
 
@@ -2397,7 +2987,7 @@ class FnInjectorProvider(InjectorProvider):
2397
2987
  fn: ta.Any
2398
2988
 
2399
2989
  def __post_init__(self) -> None:
2400
- check_not_isinstance(self.fn, type)
2990
+ check.not_isinstance(self.fn, type)
2401
2991
 
2402
2992
  def provider_fn(self) -> InjectorProviderFn:
2403
2993
  def pfn(i: Injector) -> ta.Any:
@@ -2411,7 +3001,7 @@ class CtorInjectorProvider(InjectorProvider):
2411
3001
  cls_: type
2412
3002
 
2413
3003
  def __post_init__(self) -> None:
2414
- check_isinstance(self.cls_, type)
3004
+ check.isinstance(self.cls_, type)
2415
3005
 
2416
3006
  def provider_fn(self) -> InjectorProviderFn:
2417
3007
  def pfn(i: Injector) -> ta.Any:
@@ -2433,7 +3023,7 @@ class SingletonInjectorProvider(InjectorProvider):
2433
3023
  p: InjectorProvider
2434
3024
 
2435
3025
  def __post_init__(self) -> None:
2436
- check_isinstance(self.p, InjectorProvider)
3026
+ check.isinstance(self.p, InjectorProvider)
2437
3027
 
2438
3028
  def provider_fn(self) -> InjectorProviderFn:
2439
3029
  v = not_set = object()
@@ -2453,7 +3043,7 @@ class LinkInjectorProvider(InjectorProvider):
2453
3043
  k: InjectorKey
2454
3044
 
2455
3045
  def __post_init__(self) -> None:
2456
- check_isinstance(self.k, InjectorKey)
3046
+ check.isinstance(self.k, InjectorKey)
2457
3047
 
2458
3048
  def provider_fn(self) -> InjectorProviderFn:
2459
3049
  def pfn(i: Injector) -> ta.Any:
@@ -2650,7 +3240,7 @@ def build_injection_kwargs_target(
2650
3240
 
2651
3241
  skip_names: ta.Set[str] = set()
2652
3242
  if skip_kwargs is not None:
2653
- skip_names.update(check_not_isinstance(skip_kwargs, str))
3243
+ skip_names.update(check.not_isinstance(skip_kwargs, str))
2654
3244
 
2655
3245
  seen: ta.Set[InjectorKey] = set()
2656
3246
  kws: ta.List[InjectionKwarg] = []
@@ -2711,8 +3301,8 @@ class _Injector(Injector):
2711
3301
  def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
2712
3302
  super().__init__()
2713
3303
 
2714
- self._bs = check_isinstance(bs, InjectorBindings)
2715
- self._p: ta.Optional[Injector] = check_isinstance(p, (Injector, type(None)))
3304
+ self._bs = check.isinstance(bs, InjectorBindings)
3305
+ self._p: ta.Optional[Injector] = check.isinstance(p, (Injector, type(None)))
2716
3306
 
2717
3307
  self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
2718
3308
 
@@ -2743,8 +3333,8 @@ class _Injector(Injector):
2743
3333
  return Maybe.empty()
2744
3334
 
2745
3335
  def handle_provision(self, key: InjectorKey, mv: Maybe) -> Maybe:
2746
- check_in(key, self._seen_keys)
2747
- check_not_in(key, self._provisions)
3336
+ check.in_(key, self._seen_keys)
3337
+ check.not_in(key, self._provisions)
2748
3338
  self._provisions[key] = mv
2749
3339
  return mv
2750
3340
 
@@ -2858,7 +3448,7 @@ class InjectorBinder:
2858
3448
 
2859
3449
  @classmethod
2860
3450
  def bind_as_fn(cls, icls: ta.Type[T]) -> ta.Type[T]:
2861
- check_isinstance(icls, type)
3451
+ check.isinstance(icls, type)
2862
3452
  if icls not in cls._FN_TYPES:
2863
3453
  cls._FN_TYPES = (*cls._FN_TYPES, icls)
2864
3454
  return icls
@@ -2915,7 +3505,7 @@ class InjectorBinder:
2915
3505
  to_fn = obj
2916
3506
  if key is None:
2917
3507
  insp = _injection_inspect(obj)
2918
- key_cls: ta.Any = check_valid_injector_key_cls(check_not_none(insp.type_hints.get('return')))
3508
+ key_cls: ta.Any = check_valid_injector_key_cls(check.not_none(insp.type_hints.get('return')))
2919
3509
  key = InjectorKey(key_cls)
2920
3510
  else:
2921
3511
  if to_const is not None:
@@ -3457,10 +4047,10 @@ class ProxyObjMarshaler(ObjMarshaler):
3457
4047
  m: ta.Optional[ObjMarshaler] = None
3458
4048
 
3459
4049
  def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3460
- return check_not_none(self.m).marshal(o, ctx)
4050
+ return check.not_none(self.m).marshal(o, ctx)
3461
4051
 
3462
4052
  def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3463
- return check_not_none(self.m).unmarshal(o, ctx)
4053
+ return check.not_none(self.m).unmarshal(o, ctx)
3464
4054
 
3465
4055
 
3466
4056
  @dc.dataclass(frozen=True)
@@ -3619,19 +4209,19 @@ class DatetimeObjMarshaler(ObjMarshaler):
3619
4209
 
3620
4210
  class DecimalObjMarshaler(ObjMarshaler):
3621
4211
  def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3622
- return str(check_isinstance(o, decimal.Decimal))
4212
+ return str(check.isinstance(o, decimal.Decimal))
3623
4213
 
3624
4214
  def unmarshal(self, v: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3625
- return decimal.Decimal(check_isinstance(v, str))
4215
+ return decimal.Decimal(check.isinstance(v, str))
3626
4216
 
3627
4217
 
3628
4218
  class FractionObjMarshaler(ObjMarshaler):
3629
4219
  def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3630
- fr = check_isinstance(o, fractions.Fraction)
4220
+ fr = check.isinstance(o, fractions.Fraction)
3631
4221
  return [fr.numerator, fr.denominator]
3632
4222
 
3633
4223
  def unmarshal(self, v: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3634
- num, denom = check_isinstance(v, list)
4224
+ num, denom = check.isinstance(v, list)
3635
4225
  return fractions.Fraction(num, denom)
3636
4226
 
3637
4227
 
@@ -4436,7 +5026,7 @@ class _RemoteCommandHandler:
4436
5026
  ], return_when=asyncio.FIRST_COMPLETED)
4437
5027
 
4438
5028
  if recv_task in done:
4439
- msg: ta.Optional[_RemoteProtocol.Message] = check_isinstance(
5029
+ msg: ta.Optional[_RemoteProtocol.Message] = check.isinstance(
4440
5030
  recv_task.result(),
4441
5031
  (_RemoteProtocol.Message, type(None)),
4442
5032
  )
@@ -4505,8 +5095,8 @@ class RemoteCommandExecutor(CommandExecutor):
4505
5095
  #
4506
5096
 
4507
5097
  async def start(self) -> None:
4508
- check_none(self._loop_task)
4509
- check_state(not self._stop.is_set())
5098
+ check.none(self._loop_task)
5099
+ check.state(not self._stop.is_set())
4510
5100
  self._loop_task = asyncio.create_task(self._loop())
4511
5101
 
4512
5102
  async def aclose(self) -> None:
@@ -4542,12 +5132,12 @@ class RemoteCommandExecutor(CommandExecutor):
4542
5132
  ], return_when=asyncio.FIRST_COMPLETED)
4543
5133
 
4544
5134
  if queue_task in done:
4545
- req = check_isinstance(queue_task.result(), RemoteCommandExecutor._Request)
5135
+ req = check.isinstance(queue_task.result(), RemoteCommandExecutor._Request)
4546
5136
  queue_task = None
4547
5137
  await self._handle_request(req)
4548
5138
 
4549
5139
  if recv_task in done:
4550
- msg: ta.Optional[_RemoteProtocol.Message] = check_isinstance(
5140
+ msg: ta.Optional[_RemoteProtocol.Message] = check.isinstance(
4551
5141
  recv_task.result(),
4552
5142
  (_RemoteProtocol.Message, type(None)),
4553
5143
  )
@@ -4615,7 +5205,7 @@ class RemoteCommandExecutor(CommandExecutor):
4615
5205
  if (e := r.exception) is not None:
4616
5206
  raise RemoteCommandError(e)
4617
5207
  else:
4618
- return check_not_none(r.output)
5208
+ return check.not_none(r.output)
4619
5209
 
4620
5210
  # @ta.override
4621
5211
  async def try_execute(
@@ -4660,7 +5250,7 @@ async def asyncio_subprocess_popen(
4660
5250
  if shell:
4661
5251
  fac = functools.partial(
4662
5252
  asyncio.create_subprocess_shell,
4663
- check_single(cmd),
5253
+ check.single(cmd),
4664
5254
  )
4665
5255
  else:
4666
5256
  fac = functools.partial(
@@ -4700,7 +5290,7 @@ class AsyncioProcessCommunicator:
4700
5290
  self._proc = proc
4701
5291
  self._loop = loop
4702
5292
 
4703
- self._transport: asyncio.base_subprocess.BaseSubprocessTransport = check_isinstance(
5293
+ self._transport: asyncio.base_subprocess.BaseSubprocessTransport = check.isinstance(
4704
5294
  proc._transport, # type: ignore # noqa
4705
5295
  asyncio.base_subprocess.BaseSubprocessTransport,
4706
5296
  )
@@ -4710,7 +5300,7 @@ class AsyncioProcessCommunicator:
4710
5300
  return self._loop.get_debug()
4711
5301
 
4712
5302
  async def _feed_stdin(self, input: bytes) -> None: # noqa
4713
- stdin = check_not_none(self._proc.stdin)
5303
+ stdin = check.not_none(self._proc.stdin)
4714
5304
  try:
4715
5305
  if input is not None:
4716
5306
  stdin.write(input)
@@ -4734,13 +5324,13 @@ class AsyncioProcessCommunicator:
4734
5324
  return None
4735
5325
 
4736
5326
  async def _read_stream(self, fd: int) -> bytes:
4737
- transport: ta.Any = check_not_none(self._transport.get_pipe_transport(fd))
5327
+ transport: ta.Any = check.not_none(self._transport.get_pipe_transport(fd))
4738
5328
 
4739
5329
  if fd == 2:
4740
- stream = check_not_none(self._proc.stderr)
5330
+ stream = check.not_none(self._proc.stderr)
4741
5331
  else:
4742
- check_equal(fd, 1)
4743
- stream = check_not_none(self._proc.stdout)
5332
+ check.equal(fd, 1)
5333
+ stream = check.not_none(self._proc.stdout)
4744
5334
 
4745
5335
  if self._debug:
4746
5336
  name = 'stdout' if fd == 1 else 'stderr'
@@ -4860,7 +5450,7 @@ async def asyncio_subprocess_check_output(
4860
5450
  **kwargs,
4861
5451
  )
4862
5452
 
4863
- return check_not_none(stdout)
5453
+ return check.not_none(stdout)
4864
5454
 
4865
5455
 
4866
5456
  async def asyncio_subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
@@ -5038,7 +5628,7 @@ class SubprocessCommand(Command['SubprocessCommand.Output']):
5038
5628
  timeout: ta.Optional[float] = None
5039
5629
 
5040
5630
  def __post_init__(self) -> None:
5041
- check_not_isinstance(self.cmd, str)
5631
+ check.not_isinstance(self.cmd, str)
5042
5632
 
5043
5633
  @dc.dataclass(frozen=True)
5044
5634
  class Output(Command.Output):
@@ -5079,7 +5669,7 @@ class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCom
5079
5669
  end_time = time.time()
5080
5670
 
5081
5671
  return SubprocessCommand.Output(
5082
- rc=check_not_none(proc.returncode),
5672
+ rc=check.not_none(proc.returncode),
5083
5673
  pid=proc.pid,
5084
5674
 
5085
5675
  elapsed_s=end_time - start_time,
@@ -5123,11 +5713,11 @@ class _RemoteExecutionMain:
5123
5713
 
5124
5714
  @property
5125
5715
  def _bootstrap(self) -> MainBootstrap:
5126
- return check_not_none(self.__bootstrap)
5716
+ return check.not_none(self.__bootstrap)
5127
5717
 
5128
5718
  @property
5129
5719
  def _injector(self) -> Injector:
5130
- return check_not_none(self.__injector)
5720
+ return check.not_none(self.__injector)
5131
5721
 
5132
5722
  #
5133
5723
 
@@ -5171,12 +5761,12 @@ class _RemoteExecutionMain:
5171
5761
  #
5172
5762
 
5173
5763
  async def _setup(self) -> None:
5174
- check_none(self.__bootstrap)
5175
- check_none(self.__injector)
5764
+ check.none(self.__bootstrap)
5765
+ check.none(self.__injector)
5176
5766
 
5177
5767
  # Bootstrap
5178
5768
 
5179
- self.__bootstrap = check_not_none(await self._chan.recv_obj(MainBootstrap))
5769
+ self.__bootstrap = check.not_none(await self._chan.recv_obj(MainBootstrap))
5180
5770
 
5181
5771
  if (prd := self._bootstrap.remote_config.pycharm_remote_debug) is not None:
5182
5772
  pycharm_debug_connect(prd)
@@ -5318,8 +5908,8 @@ class SubprocessRemoteSpawning(RemoteSpawning):
5318
5908
  ),
5319
5909
  timeout=timeout,
5320
5910
  ) as proc:
5321
- stdin = check_not_none(proc.stdin)
5322
- stdout = check_not_none(proc.stdout)
5911
+ stdin = check.not_none(proc.stdin)
5912
+ stdout = check.not_none(proc.stdout)
5323
5913
 
5324
5914
  try:
5325
5915
  yield RemoteSpawning.Spawned(
@@ -5535,7 +6125,7 @@ class Pyenv:
5535
6125
 
5536
6126
  @async_cached_nullary
5537
6127
  async def exe(self) -> str:
5538
- return os.path.join(check_not_none(await self.root()), 'bin', 'pyenv')
6128
+ return os.path.join(check.not_none(await self.root()), 'bin', 'pyenv')
5539
6129
 
5540
6130
  async def version_exes(self) -> ta.List[ta.Tuple[str, str]]:
5541
6131
  if (root := await self.root()) is None:
@@ -5761,7 +6351,7 @@ class PyenvVersionInstaller:
5761
6351
 
5762
6352
  @async_cached_nullary
5763
6353
  async def install_dir(self) -> str:
5764
- return str(os.path.join(check_not_none(await self._pyenv.root()), 'versions', self.install_name()))
6354
+ return str(os.path.join(check.not_none(await self._pyenv.root()), 'versions', self.install_name()))
5765
6355
 
5766
6356
  @async_cached_nullary
5767
6357
  async def install(self) -> str:
@@ -5784,7 +6374,7 @@ class PyenvVersionInstaller:
5784
6374
 
5785
6375
  if self._given_install_name is not None:
5786
6376
  full_args = [
5787
- os.path.join(check_not_none(await self._pyenv.root()), 'plugins', 'python-build', 'bin', 'python-build'), # noqa
6377
+ os.path.join(check.not_none(await self._pyenv.root()), 'plugins', 'python-build', 'bin', 'python-build'), # noqa
5788
6378
  *conf_args,
5789
6379
  self.install_dir(),
5790
6380
  ]
@@ -5856,7 +6446,7 @@ class PyenvInterpProvider(InterpProvider):
5856
6446
  iv: ta.Optional[InterpVersion]
5857
6447
  if self._inspect:
5858
6448
  try:
5859
- iv = check_not_none(await self._inspector.inspect(ep)).iv
6449
+ iv = check.not_none(await self._inspector.inspect(ep)).iv
5860
6450
  except Exception as e: # noqa
5861
6451
  return None
5862
6452
  else:
@@ -6191,8 +6781,8 @@ class InterpCommand(Command['InterpCommand.Output']):
6191
6781
 
6192
6782
  class InterpCommandExecutor(CommandExecutor[InterpCommand, InterpCommand.Output]):
6193
6783
  async def execute(self, cmd: InterpCommand) -> InterpCommand.Output:
6194
- i = InterpSpecifier.parse(check_not_none(cmd.spec))
6195
- o = check_not_none(await DEFAULT_INTERP_RESOLVER.resolve(i, install=cmd.install))
6784
+ i = InterpSpecifier.parse(check.not_none(cmd.spec))
6785
+ o = check.not_none(await DEFAULT_INTERP_RESOLVER.resolve(i, install=cmd.install))
6196
6786
  return InterpCommand.Output(
6197
6787
  exe=o.exe,
6198
6788
  version=str(o.version.version),
@@ -6383,108 +6973,102 @@ def main_bootstrap(bs: MainBootstrap) -> Injector:
6383
6973
  # main.py
6384
6974
 
6385
6975
 
6386
- ##
6976
+ class MainCli(ArgparseCli):
6977
+ @argparse_command(
6978
+ argparse_arg('--payload-file'),
6387
6979
 
6980
+ argparse_arg('-s', '--shell'),
6981
+ argparse_arg('-q', '--shell-quote', action='store_true'),
6982
+ argparse_arg('--python', default='python3'),
6388
6983
 
6389
- async def _async_main(args: ta.Any) -> None:
6390
- bs = MainBootstrap(
6391
- main_config=MainConfig(
6392
- log_level='DEBUG' if args.debug else 'INFO',
6984
+ argparse_arg('--pycharm-debug-port', type=int),
6985
+ argparse_arg('--pycharm-debug-host'),
6986
+ argparse_arg('--pycharm-debug-version'),
6393
6987
 
6394
- debug=bool(args.debug),
6395
- ),
6988
+ argparse_arg('--remote-timebomb-delay-s', type=float),
6396
6989
 
6397
- remote_config=RemoteConfig(
6398
- payload_file=args._payload_file, # noqa
6990
+ argparse_arg('--debug', action='store_true'),
6399
6991
 
6400
- pycharm_remote_debug=PycharmRemoteDebug(
6401
- port=args.pycharm_debug_port,
6402
- **(dict(host=args.pycharm_debug_host) if args.pycharm_debug_host is not None else {}),
6403
- install_version=args.pycharm_debug_version,
6404
- ) if args.pycharm_debug_port is not None else None,
6992
+ argparse_arg('--local', action='store_true'),
6405
6993
 
6406
- timebomb_delay_s=args.remote_timebomb_delay_s,
6407
- ),
6994
+ argparse_arg('command', nargs='+'),
6408
6995
  )
6996
+ def run(self) -> None:
6997
+ asyncio.run(self._async_run())
6409
6998
 
6410
- #
6411
-
6412
- injector = main_bootstrap(
6413
- bs,
6414
- )
6999
+ async def _async_run(self) -> None:
7000
+ bs = MainBootstrap(
7001
+ main_config=MainConfig(
7002
+ log_level='DEBUG' if self.args.debug else 'INFO',
6415
7003
 
6416
- #
6417
-
6418
- msh = injector[ObjMarshalerManager]
6419
-
6420
- cmds: ta.List[Command] = []
6421
- cmd: Command
6422
- for c in args.command:
6423
- if not c.startswith('{'):
6424
- c = json.dumps({c: {}})
6425
- cmd = msh.unmarshal_obj(json.loads(c), Command)
6426
- cmds.append(cmd)
6427
-
6428
- #
6429
-
6430
- async with contextlib.AsyncExitStack() as es:
6431
- ce: CommandExecutor
6432
-
6433
- if args.local:
6434
- ce = injector[LocalCommandExecutor]
7004
+ debug=bool(self.args.debug),
7005
+ ),
6435
7006
 
6436
- else:
6437
- tgt = RemoteSpawning.Target(
6438
- shell=args.shell,
6439
- shell_quote=args.shell_quote,
6440
- python=args.python,
6441
- )
7007
+ remote_config=RemoteConfig(
7008
+ payload_file=self.args.payload_file, # noqa
6442
7009
 
6443
- ce = await es.enter_async_context(injector[RemoteExecutionConnector].connect(tgt, bs)) # noqa
7010
+ pycharm_remote_debug=PycharmRemoteDebug(
7011
+ port=self.args.pycharm_debug_port,
7012
+ **(dict(host=self.args.pycharm_debug_host) if self.args.pycharm_debug_host is not None else {}),
7013
+ install_version=self.args.pycharm_debug_version,
7014
+ ) if self.args.pycharm_debug_port is not None else None,
6444
7015
 
6445
- async def run_command(cmd: Command) -> None:
6446
- res = await ce.try_execute(
6447
- cmd,
6448
- log=log,
6449
- omit_exc_object=True,
6450
- )
7016
+ timebomb_delay_s=self.args.remote_timebomb_delay_s,
7017
+ ),
7018
+ )
6451
7019
 
6452
- print(msh.marshal_obj(res, opts=ObjMarshalOptions(raw_bytes=True)))
7020
+ #
6453
7021
 
6454
- await asyncio.gather(*[
6455
- run_command(cmd)
6456
- for cmd in cmds
6457
- ])
7022
+ injector = main_bootstrap(
7023
+ bs,
7024
+ )
6458
7025
 
7026
+ #
6459
7027
 
6460
- def _main() -> None:
6461
- import argparse
7028
+ msh = injector[ObjMarshalerManager]
6462
7029
 
6463
- parser = argparse.ArgumentParser()
7030
+ cmds: ta.List[Command] = []
7031
+ cmd: Command
7032
+ for c in self.args.command:
7033
+ if not c.startswith('{'):
7034
+ c = json.dumps({c: {}})
7035
+ cmd = msh.unmarshal_obj(json.loads(c), Command)
7036
+ cmds.append(cmd)
6464
7037
 
6465
- parser.add_argument('--_payload-file')
7038
+ #
6466
7039
 
6467
- parser.add_argument('-s', '--shell')
6468
- parser.add_argument('-q', '--shell-quote', action='store_true')
6469
- parser.add_argument('--python', default='python3')
7040
+ async with contextlib.AsyncExitStack() as es:
7041
+ ce: CommandExecutor
6470
7042
 
6471
- parser.add_argument('--pycharm-debug-port', type=int)
6472
- parser.add_argument('--pycharm-debug-host')
6473
- parser.add_argument('--pycharm-debug-version')
7043
+ if self.args.local:
7044
+ ce = injector[LocalCommandExecutor]
6474
7045
 
6475
- parser.add_argument('--remote-timebomb-delay-s', type=float)
7046
+ else:
7047
+ tgt = RemoteSpawning.Target(
7048
+ shell=self.args.shell,
7049
+ shell_quote=self.args.shell_quote,
7050
+ python=self.args.python,
7051
+ )
6476
7052
 
6477
- parser.add_argument('--debug', action='store_true')
7053
+ ce = await es.enter_async_context(injector[RemoteExecutionConnector].connect(tgt, bs)) # noqa
6478
7054
 
6479
- parser.add_argument('--local', action='store_true')
7055
+ async def run_command(cmd: Command) -> None:
7056
+ res = await ce.try_execute(
7057
+ cmd,
7058
+ log=log,
7059
+ omit_exc_object=True,
7060
+ )
6480
7061
 
6481
- parser.add_argument('command', nargs='+')
7062
+ print(msh.marshal_obj(res, opts=ObjMarshalOptions(raw_bytes=True)))
6482
7063
 
6483
- args = parser.parse_args()
7064
+ await asyncio.gather(*[
7065
+ run_command(cmd)
7066
+ for cmd in cmds
7067
+ ])
6484
7068
 
6485
- #
6486
7069
 
6487
- asyncio.run(_async_main(args))
7070
+ def _main() -> None:
7071
+ MainCli().call_and_exit()
6488
7072
 
6489
7073
 
6490
7074
  if __name__ == '__main__':