ominfra 0.0.0.dev149__py3-none-any.whl → 0.0.0.dev150__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.
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__':