ominfra 0.0.0.dev148__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.
Files changed (36) hide show
  1. ominfra/clouds/aws/auth.py +7 -9
  2. ominfra/clouds/aws/cli.py +1 -1
  3. ominfra/clouds/aws/journald2aws/driver.py +4 -4
  4. ominfra/clouds/aws/logs.py +4 -5
  5. ominfra/clouds/gcp/auth.py +1 -1
  6. ominfra/configs.py +3 -4
  7. ominfra/journald/messages.py +3 -3
  8. ominfra/journald/tailer.py +2 -2
  9. ominfra/manage/commands/base.py +2 -2
  10. ominfra/manage/commands/interp.py +3 -3
  11. ominfra/manage/commands/subprocess.py +3 -4
  12. ominfra/manage/deploy/paths.py +12 -15
  13. ominfra/manage/main.py +72 -75
  14. ominfra/manage/remote/_main.py +6 -7
  15. ominfra/manage/remote/execution.py +7 -10
  16. ominfra/manage/remote/spawning.py +3 -3
  17. ominfra/scripts/journald2aws.py +508 -147
  18. ominfra/scripts/manage.py +772 -183
  19. ominfra/scripts/supervisor.py +1144 -783
  20. ominfra/supervisor/dispatchers.py +1 -1
  21. ominfra/supervisor/groupsimpl.py +2 -2
  22. ominfra/supervisor/http.py +7 -7
  23. ominfra/supervisor/inject.py +4 -4
  24. ominfra/supervisor/io.py +1 -1
  25. ominfra/supervisor/main.py +1 -1
  26. ominfra/supervisor/processimpl.py +2 -2
  27. ominfra/supervisor/spawningimpl.py +9 -10
  28. ominfra/supervisor/supervisor.py +3 -3
  29. ominfra/supervisor/types.py +1 -1
  30. ominfra/tailscale/cli.py +1 -1
  31. {ominfra-0.0.0.dev148.dist-info → ominfra-0.0.0.dev150.dist-info}/METADATA +3 -3
  32. {ominfra-0.0.0.dev148.dist-info → ominfra-0.0.0.dev150.dist-info}/RECORD +36 -36
  33. {ominfra-0.0.0.dev148.dist-info → ominfra-0.0.0.dev150.dist-info}/LICENSE +0 -0
  34. {ominfra-0.0.0.dev148.dist-info → ominfra-0.0.0.dev150.dist-info}/WHEEL +0 -0
  35. {ominfra-0.0.0.dev148.dist-info → ominfra-0.0.0.dev150.dist-info}/entry_points.txt +0 -0
  36. {ominfra-0.0.0.dev148.dist-info → ominfra-0.0.0.dev150.dist-info}/top_level.txt +0 -0
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,99 +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]
1155
1212
 
1213
+ def _late_configure(self) -> None:
1214
+ if not self._late_configure_fns:
1215
+ return
1156
1216
 
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
1217
+ with self._config_lock:
1218
+ if not (lc := self._late_configure_fns):
1219
+ return
1161
1220
 
1221
+ for fn in lc:
1222
+ fn(self)
1162
1223
 
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
1224
+ self._late_configure_fns = []
1167
1225
 
1226
+ #
1168
1227
 
1169
- def check_none(v: T) -> None:
1170
- if v is not None:
1171
- raise ValueError(v)
1228
+ class _ArgsKwargs:
1229
+ def __init__(self, *args, **kwargs):
1230
+ self.args = args
1231
+ self.kwargs = kwargs
1172
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
+ )
1173
1265
 
1174
- def check_not_none(v: ta.Optional[T]) -> T:
1175
- if v is None:
1176
- raise ValueError
1177
- return v
1266
+ for fn in self._on_raise_fns:
1267
+ fn(exc)
1178
1268
 
1269
+ raise exc
1179
1270
 
1180
- def check_not(v: ta.Any) -> None:
1181
- if v:
1182
- raise ValueError(v)
1183
- return v
1271
+ #
1184
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
+ )
1185
1293
 
1186
- def check_non_empty_str(v: ta.Optional[str]) -> str:
1187
- if not v:
1188
- raise ValueError
1189
- return v
1294
+ return v
1190
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)
1191
1299
 
1192
- def check_state(v: bool, msg: str = 'Illegal state') -> None:
1193
- if not v:
1194
- raise ValueError(msg)
1300
+ return inner
1195
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
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)
1196
1316
 
1197
- def check_equal(l: T, r: T) -> T:
1198
- if l != r:
1199
- raise ValueError(l, r)
1200
- return l
1317
+ return inner
1201
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
+ )
1202
1328
 
1203
- def check_not_equal(l: T, r: T) -> T:
1204
- if l == r:
1205
- raise ValueError(l, r)
1206
- return l
1329
+ return v
1207
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)
1208
1334
 
1209
- def check_is(l: T, r: T) -> T:
1210
- if l is not r:
1211
- raise ValueError(l, r)
1212
- return l
1335
+ return inner
1213
1336
 
1337
+ ##
1214
1338
 
1215
- def check_is_not(l: T, r: ta.Any) -> T:
1216
- if l is r:
1217
- raise ValueError(l, r)
1218
- return l
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
+ )
1219
1348
 
1349
+ return v
1220
1350
 
1221
- def check_in(v: T, c: ta.Container[T]) -> T:
1222
- if v not in c:
1223
- raise ValueError(v, c)
1224
- 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
+ )
1225
1360
 
1361
+ return v
1226
1362
 
1227
- def check_not_in(v: T, c: ta.Container[T]) -> T:
1228
- if v in c:
1229
- raise ValueError(v, c)
1230
- return v
1363
+ #
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
+ )
1374
+
1375
+ return v
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
+ )
1386
+
1387
+ return v
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
+ )
1398
+
1399
+ return v
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
+ )
1415
+
1416
+ return v
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
+ )
1427
+
1428
+ return v
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
+ )
1439
+
1440
+ return it
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
1477
+
1478
+ #
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
+ )
1489
+
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
+ )
1499
+
1500
+ return v
1501
+
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
+ )
1231
1585
 
1586
+ return new
1232
1587
 
1233
- def check_single(vs: ta.Iterable[T]) -> T:
1234
- [v] = vs
1235
- return v
1588
+ #
1236
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
+ )
1237
1599
 
1238
- def check_empty(v: SizedT) -> SizedT:
1239
- if len(v):
1240
- raise ValueError(v)
1241
- 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
+ )
1242
1609
 
1243
1610
 
1244
- def check_non_empty(v: SizedT) -> SizedT:
1245
- if not len(v):
1246
- raise ValueError(v)
1247
- return v
1611
+ check = Checks()
1248
1612
 
1249
1613
 
1250
1614
  ########################################
@@ -2053,7 +2417,7 @@ class Command(abc.ABC, ta.Generic[CommandOutputT]):
2053
2417
 
2054
2418
  @ta.final
2055
2419
  async def execute(self, executor: 'CommandExecutor') -> CommandOutputT:
2056
- 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]
2057
2421
 
2058
2422
 
2059
2423
  ##
@@ -2248,6 +2612,237 @@ def get_remote_payload_src(
2248
2612
  return importlib.resources.files(__package__.split('.')[0] + '.scripts').joinpath('manage.py').read_text()
2249
2613
 
2250
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
+
2251
2846
  ########################################
2252
2847
  # ../../../omlish/lite/inject.py
2253
2848
 
@@ -2392,7 +2987,7 @@ class FnInjectorProvider(InjectorProvider):
2392
2987
  fn: ta.Any
2393
2988
 
2394
2989
  def __post_init__(self) -> None:
2395
- check_not_isinstance(self.fn, type)
2990
+ check.not_isinstance(self.fn, type)
2396
2991
 
2397
2992
  def provider_fn(self) -> InjectorProviderFn:
2398
2993
  def pfn(i: Injector) -> ta.Any:
@@ -2406,7 +3001,7 @@ class CtorInjectorProvider(InjectorProvider):
2406
3001
  cls_: type
2407
3002
 
2408
3003
  def __post_init__(self) -> None:
2409
- check_isinstance(self.cls_, type)
3004
+ check.isinstance(self.cls_, type)
2410
3005
 
2411
3006
  def provider_fn(self) -> InjectorProviderFn:
2412
3007
  def pfn(i: Injector) -> ta.Any:
@@ -2428,7 +3023,7 @@ class SingletonInjectorProvider(InjectorProvider):
2428
3023
  p: InjectorProvider
2429
3024
 
2430
3025
  def __post_init__(self) -> None:
2431
- check_isinstance(self.p, InjectorProvider)
3026
+ check.isinstance(self.p, InjectorProvider)
2432
3027
 
2433
3028
  def provider_fn(self) -> InjectorProviderFn:
2434
3029
  v = not_set = object()
@@ -2448,7 +3043,7 @@ class LinkInjectorProvider(InjectorProvider):
2448
3043
  k: InjectorKey
2449
3044
 
2450
3045
  def __post_init__(self) -> None:
2451
- check_isinstance(self.k, InjectorKey)
3046
+ check.isinstance(self.k, InjectorKey)
2452
3047
 
2453
3048
  def provider_fn(self) -> InjectorProviderFn:
2454
3049
  def pfn(i: Injector) -> ta.Any:
@@ -2645,7 +3240,7 @@ def build_injection_kwargs_target(
2645
3240
 
2646
3241
  skip_names: ta.Set[str] = set()
2647
3242
  if skip_kwargs is not None:
2648
- skip_names.update(check_not_isinstance(skip_kwargs, str))
3243
+ skip_names.update(check.not_isinstance(skip_kwargs, str))
2649
3244
 
2650
3245
  seen: ta.Set[InjectorKey] = set()
2651
3246
  kws: ta.List[InjectionKwarg] = []
@@ -2706,8 +3301,8 @@ class _Injector(Injector):
2706
3301
  def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
2707
3302
  super().__init__()
2708
3303
 
2709
- self._bs = check_isinstance(bs, InjectorBindings)
2710
- 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)))
2711
3306
 
2712
3307
  self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
2713
3308
 
@@ -2738,8 +3333,8 @@ class _Injector(Injector):
2738
3333
  return Maybe.empty()
2739
3334
 
2740
3335
  def handle_provision(self, key: InjectorKey, mv: Maybe) -> Maybe:
2741
- check_in(key, self._seen_keys)
2742
- check_not_in(key, self._provisions)
3336
+ check.in_(key, self._seen_keys)
3337
+ check.not_in(key, self._provisions)
2743
3338
  self._provisions[key] = mv
2744
3339
  return mv
2745
3340
 
@@ -2853,7 +3448,7 @@ class InjectorBinder:
2853
3448
 
2854
3449
  @classmethod
2855
3450
  def bind_as_fn(cls, icls: ta.Type[T]) -> ta.Type[T]:
2856
- check_isinstance(icls, type)
3451
+ check.isinstance(icls, type)
2857
3452
  if icls not in cls._FN_TYPES:
2858
3453
  cls._FN_TYPES = (*cls._FN_TYPES, icls)
2859
3454
  return icls
@@ -2910,7 +3505,7 @@ class InjectorBinder:
2910
3505
  to_fn = obj
2911
3506
  if key is None:
2912
3507
  insp = _injection_inspect(obj)
2913
- 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')))
2914
3509
  key = InjectorKey(key_cls)
2915
3510
  else:
2916
3511
  if to_const is not None:
@@ -3452,10 +4047,10 @@ class ProxyObjMarshaler(ObjMarshaler):
3452
4047
  m: ta.Optional[ObjMarshaler] = None
3453
4048
 
3454
4049
  def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3455
- return check_not_none(self.m).marshal(o, ctx)
4050
+ return check.not_none(self.m).marshal(o, ctx)
3456
4051
 
3457
4052
  def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3458
- return check_not_none(self.m).unmarshal(o, ctx)
4053
+ return check.not_none(self.m).unmarshal(o, ctx)
3459
4054
 
3460
4055
 
3461
4056
  @dc.dataclass(frozen=True)
@@ -3614,19 +4209,19 @@ class DatetimeObjMarshaler(ObjMarshaler):
3614
4209
 
3615
4210
  class DecimalObjMarshaler(ObjMarshaler):
3616
4211
  def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3617
- return str(check_isinstance(o, decimal.Decimal))
4212
+ return str(check.isinstance(o, decimal.Decimal))
3618
4213
 
3619
4214
  def unmarshal(self, v: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3620
- return decimal.Decimal(check_isinstance(v, str))
4215
+ return decimal.Decimal(check.isinstance(v, str))
3621
4216
 
3622
4217
 
3623
4218
  class FractionObjMarshaler(ObjMarshaler):
3624
4219
  def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3625
- fr = check_isinstance(o, fractions.Fraction)
4220
+ fr = check.isinstance(o, fractions.Fraction)
3626
4221
  return [fr.numerator, fr.denominator]
3627
4222
 
3628
4223
  def unmarshal(self, v: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3629
- num, denom = check_isinstance(v, list)
4224
+ num, denom = check.isinstance(v, list)
3630
4225
  return fractions.Fraction(num, denom)
3631
4226
 
3632
4227
 
@@ -4431,7 +5026,7 @@ class _RemoteCommandHandler:
4431
5026
  ], return_when=asyncio.FIRST_COMPLETED)
4432
5027
 
4433
5028
  if recv_task in done:
4434
- msg: ta.Optional[_RemoteProtocol.Message] = check_isinstance(
5029
+ msg: ta.Optional[_RemoteProtocol.Message] = check.isinstance(
4435
5030
  recv_task.result(),
4436
5031
  (_RemoteProtocol.Message, type(None)),
4437
5032
  )
@@ -4500,8 +5095,8 @@ class RemoteCommandExecutor(CommandExecutor):
4500
5095
  #
4501
5096
 
4502
5097
  async def start(self) -> None:
4503
- check_none(self._loop_task)
4504
- check_state(not self._stop.is_set())
5098
+ check.none(self._loop_task)
5099
+ check.state(not self._stop.is_set())
4505
5100
  self._loop_task = asyncio.create_task(self._loop())
4506
5101
 
4507
5102
  async def aclose(self) -> None:
@@ -4537,12 +5132,12 @@ class RemoteCommandExecutor(CommandExecutor):
4537
5132
  ], return_when=asyncio.FIRST_COMPLETED)
4538
5133
 
4539
5134
  if queue_task in done:
4540
- req = check_isinstance(queue_task.result(), RemoteCommandExecutor._Request)
5135
+ req = check.isinstance(queue_task.result(), RemoteCommandExecutor._Request)
4541
5136
  queue_task = None
4542
5137
  await self._handle_request(req)
4543
5138
 
4544
5139
  if recv_task in done:
4545
- msg: ta.Optional[_RemoteProtocol.Message] = check_isinstance(
5140
+ msg: ta.Optional[_RemoteProtocol.Message] = check.isinstance(
4546
5141
  recv_task.result(),
4547
5142
  (_RemoteProtocol.Message, type(None)),
4548
5143
  )
@@ -4610,7 +5205,7 @@ class RemoteCommandExecutor(CommandExecutor):
4610
5205
  if (e := r.exception) is not None:
4611
5206
  raise RemoteCommandError(e)
4612
5207
  else:
4613
- return check_not_none(r.output)
5208
+ return check.not_none(r.output)
4614
5209
 
4615
5210
  # @ta.override
4616
5211
  async def try_execute(
@@ -4655,7 +5250,7 @@ async def asyncio_subprocess_popen(
4655
5250
  if shell:
4656
5251
  fac = functools.partial(
4657
5252
  asyncio.create_subprocess_shell,
4658
- check_single(cmd),
5253
+ check.single(cmd),
4659
5254
  )
4660
5255
  else:
4661
5256
  fac = functools.partial(
@@ -4695,7 +5290,7 @@ class AsyncioProcessCommunicator:
4695
5290
  self._proc = proc
4696
5291
  self._loop = loop
4697
5292
 
4698
- self._transport: asyncio.base_subprocess.BaseSubprocessTransport = check_isinstance(
5293
+ self._transport: asyncio.base_subprocess.BaseSubprocessTransport = check.isinstance(
4699
5294
  proc._transport, # type: ignore # noqa
4700
5295
  asyncio.base_subprocess.BaseSubprocessTransport,
4701
5296
  )
@@ -4705,7 +5300,7 @@ class AsyncioProcessCommunicator:
4705
5300
  return self._loop.get_debug()
4706
5301
 
4707
5302
  async def _feed_stdin(self, input: bytes) -> None: # noqa
4708
- stdin = check_not_none(self._proc.stdin)
5303
+ stdin = check.not_none(self._proc.stdin)
4709
5304
  try:
4710
5305
  if input is not None:
4711
5306
  stdin.write(input)
@@ -4729,13 +5324,13 @@ class AsyncioProcessCommunicator:
4729
5324
  return None
4730
5325
 
4731
5326
  async def _read_stream(self, fd: int) -> bytes:
4732
- 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))
4733
5328
 
4734
5329
  if fd == 2:
4735
- stream = check_not_none(self._proc.stderr)
5330
+ stream = check.not_none(self._proc.stderr)
4736
5331
  else:
4737
- check_equal(fd, 1)
4738
- stream = check_not_none(self._proc.stdout)
5332
+ check.equal(fd, 1)
5333
+ stream = check.not_none(self._proc.stdout)
4739
5334
 
4740
5335
  if self._debug:
4741
5336
  name = 'stdout' if fd == 1 else 'stderr'
@@ -4855,7 +5450,7 @@ async def asyncio_subprocess_check_output(
4855
5450
  **kwargs,
4856
5451
  )
4857
5452
 
4858
- return check_not_none(stdout)
5453
+ return check.not_none(stdout)
4859
5454
 
4860
5455
 
4861
5456
  async def asyncio_subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
@@ -5033,7 +5628,7 @@ class SubprocessCommand(Command['SubprocessCommand.Output']):
5033
5628
  timeout: ta.Optional[float] = None
5034
5629
 
5035
5630
  def __post_init__(self) -> None:
5036
- check_not_isinstance(self.cmd, str)
5631
+ check.not_isinstance(self.cmd, str)
5037
5632
 
5038
5633
  @dc.dataclass(frozen=True)
5039
5634
  class Output(Command.Output):
@@ -5074,7 +5669,7 @@ class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCom
5074
5669
  end_time = time.time()
5075
5670
 
5076
5671
  return SubprocessCommand.Output(
5077
- rc=check_not_none(proc.returncode),
5672
+ rc=check.not_none(proc.returncode),
5078
5673
  pid=proc.pid,
5079
5674
 
5080
5675
  elapsed_s=end_time - start_time,
@@ -5118,11 +5713,11 @@ class _RemoteExecutionMain:
5118
5713
 
5119
5714
  @property
5120
5715
  def _bootstrap(self) -> MainBootstrap:
5121
- return check_not_none(self.__bootstrap)
5716
+ return check.not_none(self.__bootstrap)
5122
5717
 
5123
5718
  @property
5124
5719
  def _injector(self) -> Injector:
5125
- return check_not_none(self.__injector)
5720
+ return check.not_none(self.__injector)
5126
5721
 
5127
5722
  #
5128
5723
 
@@ -5166,12 +5761,12 @@ class _RemoteExecutionMain:
5166
5761
  #
5167
5762
 
5168
5763
  async def _setup(self) -> None:
5169
- check_none(self.__bootstrap)
5170
- check_none(self.__injector)
5764
+ check.none(self.__bootstrap)
5765
+ check.none(self.__injector)
5171
5766
 
5172
5767
  # Bootstrap
5173
5768
 
5174
- self.__bootstrap = check_not_none(await self._chan.recv_obj(MainBootstrap))
5769
+ self.__bootstrap = check.not_none(await self._chan.recv_obj(MainBootstrap))
5175
5770
 
5176
5771
  if (prd := self._bootstrap.remote_config.pycharm_remote_debug) is not None:
5177
5772
  pycharm_debug_connect(prd)
@@ -5313,8 +5908,8 @@ class SubprocessRemoteSpawning(RemoteSpawning):
5313
5908
  ),
5314
5909
  timeout=timeout,
5315
5910
  ) as proc:
5316
- stdin = check_not_none(proc.stdin)
5317
- stdout = check_not_none(proc.stdout)
5911
+ stdin = check.not_none(proc.stdin)
5912
+ stdout = check.not_none(proc.stdout)
5318
5913
 
5319
5914
  try:
5320
5915
  yield RemoteSpawning.Spawned(
@@ -5530,7 +6125,7 @@ class Pyenv:
5530
6125
 
5531
6126
  @async_cached_nullary
5532
6127
  async def exe(self) -> str:
5533
- 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')
5534
6129
 
5535
6130
  async def version_exes(self) -> ta.List[ta.Tuple[str, str]]:
5536
6131
  if (root := await self.root()) is None:
@@ -5756,7 +6351,7 @@ class PyenvVersionInstaller:
5756
6351
 
5757
6352
  @async_cached_nullary
5758
6353
  async def install_dir(self) -> str:
5759
- 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()))
5760
6355
 
5761
6356
  @async_cached_nullary
5762
6357
  async def install(self) -> str:
@@ -5779,7 +6374,7 @@ class PyenvVersionInstaller:
5779
6374
 
5780
6375
  if self._given_install_name is not None:
5781
6376
  full_args = [
5782
- 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
5783
6378
  *conf_args,
5784
6379
  self.install_dir(),
5785
6380
  ]
@@ -5851,7 +6446,7 @@ class PyenvInterpProvider(InterpProvider):
5851
6446
  iv: ta.Optional[InterpVersion]
5852
6447
  if self._inspect:
5853
6448
  try:
5854
- iv = check_not_none(await self._inspector.inspect(ep)).iv
6449
+ iv = check.not_none(await self._inspector.inspect(ep)).iv
5855
6450
  except Exception as e: # noqa
5856
6451
  return None
5857
6452
  else:
@@ -6186,8 +6781,8 @@ class InterpCommand(Command['InterpCommand.Output']):
6186
6781
 
6187
6782
  class InterpCommandExecutor(CommandExecutor[InterpCommand, InterpCommand.Output]):
6188
6783
  async def execute(self, cmd: InterpCommand) -> InterpCommand.Output:
6189
- i = InterpSpecifier.parse(check_not_none(cmd.spec))
6190
- 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))
6191
6786
  return InterpCommand.Output(
6192
6787
  exe=o.exe,
6193
6788
  version=str(o.version.version),
@@ -6378,108 +6973,102 @@ def main_bootstrap(bs: MainBootstrap) -> Injector:
6378
6973
  # main.py
6379
6974
 
6380
6975
 
6381
- ##
6976
+ class MainCli(ArgparseCli):
6977
+ @argparse_command(
6978
+ argparse_arg('--payload-file'),
6382
6979
 
6980
+ argparse_arg('-s', '--shell'),
6981
+ argparse_arg('-q', '--shell-quote', action='store_true'),
6982
+ argparse_arg('--python', default='python3'),
6383
6983
 
6384
- async def _async_main(args: ta.Any) -> None:
6385
- bs = MainBootstrap(
6386
- main_config=MainConfig(
6387
- 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'),
6388
6987
 
6389
- debug=bool(args.debug),
6390
- ),
6988
+ argparse_arg('--remote-timebomb-delay-s', type=float),
6391
6989
 
6392
- remote_config=RemoteConfig(
6393
- payload_file=args._payload_file, # noqa
6990
+ argparse_arg('--debug', action='store_true'),
6394
6991
 
6395
- pycharm_remote_debug=PycharmRemoteDebug(
6396
- port=args.pycharm_debug_port,
6397
- **(dict(host=args.pycharm_debug_host) if args.pycharm_debug_host is not None else {}),
6398
- install_version=args.pycharm_debug_version,
6399
- ) if args.pycharm_debug_port is not None else None,
6992
+ argparse_arg('--local', action='store_true'),
6400
6993
 
6401
- timebomb_delay_s=args.remote_timebomb_delay_s,
6402
- ),
6994
+ argparse_arg('command', nargs='+'),
6403
6995
  )
6996
+ def run(self) -> None:
6997
+ asyncio.run(self._async_run())
6404
6998
 
6405
- #
6406
-
6407
- injector = main_bootstrap(
6408
- bs,
6409
- )
6999
+ async def _async_run(self) -> None:
7000
+ bs = MainBootstrap(
7001
+ main_config=MainConfig(
7002
+ log_level='DEBUG' if self.args.debug else 'INFO',
6410
7003
 
6411
- #
6412
-
6413
- msh = injector[ObjMarshalerManager]
6414
-
6415
- cmds: ta.List[Command] = []
6416
- cmd: Command
6417
- for c in args.command:
6418
- if not c.startswith('{'):
6419
- c = json.dumps({c: {}})
6420
- cmd = msh.unmarshal_obj(json.loads(c), Command)
6421
- cmds.append(cmd)
6422
-
6423
- #
6424
-
6425
- async with contextlib.AsyncExitStack() as es:
6426
- ce: CommandExecutor
6427
-
6428
- if args.local:
6429
- ce = injector[LocalCommandExecutor]
7004
+ debug=bool(self.args.debug),
7005
+ ),
6430
7006
 
6431
- else:
6432
- tgt = RemoteSpawning.Target(
6433
- shell=args.shell,
6434
- shell_quote=args.shell_quote,
6435
- python=args.python,
6436
- )
7007
+ remote_config=RemoteConfig(
7008
+ payload_file=self.args.payload_file, # noqa
6437
7009
 
6438
- 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,
6439
7015
 
6440
- async def run_command(cmd: Command) -> None:
6441
- res = await ce.try_execute(
6442
- cmd,
6443
- log=log,
6444
- omit_exc_object=True,
6445
- )
7016
+ timebomb_delay_s=self.args.remote_timebomb_delay_s,
7017
+ ),
7018
+ )
6446
7019
 
6447
- print(msh.marshal_obj(res, opts=ObjMarshalOptions(raw_bytes=True)))
7020
+ #
6448
7021
 
6449
- await asyncio.gather(*[
6450
- run_command(cmd)
6451
- for cmd in cmds
6452
- ])
7022
+ injector = main_bootstrap(
7023
+ bs,
7024
+ )
6453
7025
 
7026
+ #
6454
7027
 
6455
- def _main() -> None:
6456
- import argparse
7028
+ msh = injector[ObjMarshalerManager]
6457
7029
 
6458
- 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)
6459
7037
 
6460
- parser.add_argument('--_payload-file')
7038
+ #
6461
7039
 
6462
- parser.add_argument('-s', '--shell')
6463
- parser.add_argument('-q', '--shell-quote', action='store_true')
6464
- parser.add_argument('--python', default='python3')
7040
+ async with contextlib.AsyncExitStack() as es:
7041
+ ce: CommandExecutor
6465
7042
 
6466
- parser.add_argument('--pycharm-debug-port', type=int)
6467
- parser.add_argument('--pycharm-debug-host')
6468
- parser.add_argument('--pycharm-debug-version')
7043
+ if self.args.local:
7044
+ ce = injector[LocalCommandExecutor]
6469
7045
 
6470
- 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
+ )
6471
7052
 
6472
- parser.add_argument('--debug', action='store_true')
7053
+ ce = await es.enter_async_context(injector[RemoteExecutionConnector].connect(tgt, bs)) # noqa
6473
7054
 
6474
- 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
+ )
6475
7061
 
6476
- parser.add_argument('command', nargs='+')
7062
+ print(msh.marshal_obj(res, opts=ObjMarshalOptions(raw_bytes=True)))
6477
7063
 
6478
- args = parser.parse_args()
7064
+ await asyncio.gather(*[
7065
+ run_command(cmd)
7066
+ for cmd in cmds
7067
+ ])
6479
7068
 
6480
- #
6481
7069
 
6482
- asyncio.run(_async_main(args))
7070
+ def _main() -> None:
7071
+ MainCli().call_and_exit()
6483
7072
 
6484
7073
 
6485
7074
  if __name__ == '__main__':