ominfra 0.0.0.dev448__py3-none-any.whl → 0.0.0.dev488__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.
Files changed (25) hide show
  1. ominfra/__about__.py +4 -1
  2. ominfra/clouds/aws/instancetypes/cache.json.gz +0 -0
  3. ominfra/clouds/aws/models/{base.py → base/__init__.py} +6 -0
  4. ominfra/clouds/aws/models/base/_dataclasses.py +721 -0
  5. ominfra/clouds/aws/models/gen/cli.py +2 -1
  6. ominfra/clouds/aws/models/gen/gen.py +16 -7
  7. ominfra/clouds/aws/models/services/{ec2.py → ec2/__init__.py} +121 -1
  8. ominfra/clouds/aws/models/services/ec2/_dataclasses.py +30654 -0
  9. ominfra/clouds/aws/models/services/{lambda_.py → lambda_/__init__.py} +139 -1
  10. ominfra/clouds/aws/models/services/lambda_/_dataclasses.py +4182 -0
  11. ominfra/clouds/aws/models/services/{rds.py → rds/__init__.py} +244 -78
  12. ominfra/clouds/aws/models/services/rds/_dataclasses.py +8231 -0
  13. ominfra/clouds/aws/models/services/{s3.py → s3/__init__.py} +9 -1
  14. ominfra/clouds/aws/models/services/s3/_dataclasses.py +5014 -0
  15. ominfra/manage/main.py +1 -2
  16. ominfra/manage/targets/bestpython.sh +1 -1
  17. ominfra/scripts/journald2aws.py +253 -29
  18. ominfra/scripts/manage.py +276 -42
  19. ominfra/scripts/supervisor.py +299 -25
  20. {ominfra-0.0.0.dev448.dist-info → ominfra-0.0.0.dev488.dist-info}/METADATA +5 -3
  21. {ominfra-0.0.0.dev448.dist-info → ominfra-0.0.0.dev488.dist-info}/RECORD +25 -20
  22. {ominfra-0.0.0.dev448.dist-info → ominfra-0.0.0.dev488.dist-info}/WHEEL +0 -0
  23. {ominfra-0.0.0.dev448.dist-info → ominfra-0.0.0.dev488.dist-info}/entry_points.txt +0 -0
  24. {ominfra-0.0.0.dev448.dist-info → ominfra-0.0.0.dev488.dist-info}/licenses/LICENSE +0 -0
  25. {ominfra-0.0.0.dev448.dist-info → ominfra-0.0.0.dev488.dist-info}/top_level.txt +0 -0
ominfra/scripts/manage.py CHANGED
@@ -34,6 +34,7 @@ import io
34
34
  import itertools
35
35
  import json
36
36
  import logging
37
+ import operator
37
38
  import os
38
39
  import os.path
39
40
  import platform
@@ -65,6 +66,142 @@ if sys.version_info < (3, 8):
65
66
  raise OSError(f'Requires python (3, 8), got {sys.version_info} from {sys.executable}') # noqa
66
67
 
67
68
 
69
+ def __omlish_amalg__(): # noqa
70
+ return dict(
71
+ src_files=[
72
+ dict(path='../../omdev/packaging/versions.py', sha1='71627ad600b3529b829b0e227b0952f2c63c7271'),
73
+ dict(path='config.py', sha1='6ff640634488fa142d9aadee5aec95db462ce46f'),
74
+ dict(path='deploy/config.py', sha1='b11f480014b42206531ea897e76dd0220eb59969'),
75
+ dict(path='deploy/paths/types.py', sha1='4364179744afb2344f2b44d188e37f786c955970'),
76
+ dict(path='deploy/types.py', sha1='41b2becf7a9d009e18235a8b49cfbe0419785190'),
77
+ dict(path='../pyremote.py', sha1='2131faabec13af3d29747d9430286394603445ca'),
78
+ dict(path='../../omlish/asyncs/asyncio/channels.py', sha1='36cec6ea48887baaf536ae6301ec6ebc70f9f19b'),
79
+ dict(path='../../omlish/asyncs/asyncio/streams.py', sha1='78a498b78b51805d3b44ba7fe8c10c575389c6a9'),
80
+ dict(path='../../omlish/configs/types.py', sha1='f7a5584cd6eccb77d18d729796072a162e9a8790'),
81
+ dict(path='../../omlish/formats/ini/sections.py', sha1='731c92cce82e183d1d4bdc23fc781fad62187394'),
82
+ dict(path='../../omlish/formats/toml/parser.py', sha1='73dac82289350ab951c4bcdbfe61167fa221f26f'),
83
+ dict(path='../../omlish/formats/toml/writer.py', sha1='6ea41d7e724bb1dcf6bd84b88993ff4e8798e021'),
84
+ dict(path='../../omlish/lite/abstract.py', sha1='a2fc3f3697fa8de5247761e9d554e70176f37aac'),
85
+ dict(path='../../omlish/lite/attrops.py', sha1='c1ebfb8573d766d34593c452a2377208d02726dc'),
86
+ dict(path='../../omlish/lite/cached.py', sha1='0c33cf961ac8f0727284303c7a30c5ea98f714f2'),
87
+ dict(path='../../omlish/lite/check.py', sha1='bb6b6b63333699b84462951a854d99ae83195b94'),
88
+ dict(path='../../omlish/lite/contextmanagers.py', sha1='993f5ed96d3410f739a20363f55670d5e5267fa3'),
89
+ dict(path='../../omlish/lite/json.py', sha1='57eeddc4d23a17931e00284ffa5cb6e3ce089486'),
90
+ dict(path='../../omlish/lite/objects.py', sha1='9566bbf3530fd71fcc56321485216b592fae21e9'),
91
+ dict(path='../../omlish/lite/pycharm.py', sha1='6f84e57f02e2f1075918002f89e4201910d2a15e'),
92
+ dict(path='../../omlish/lite/reflect.py', sha1='c4fec44bf144e9d93293c996af06f6c65fc5e63d'),
93
+ dict(path='../../omlish/lite/resources.py', sha1='1365cb6046eb929358e7c86a3fda20d95fd4a296'),
94
+ dict(path='../../omlish/lite/strings.py', sha1='89831ecbc34ad80e118a865eceb390ed399dc4d6'),
95
+ dict(path='../../omlish/lite/typing.py', sha1='deaaa560b63d9a0e40991ec0006451f5f0df04c1'),
96
+ dict(path='../../omlish/logs/levels.py', sha1='91405563d082a5eba874da82aac89d83ce7b6152'),
97
+ dict(path='../../omlish/logs/std/filters.py', sha1='f36aab646d84d31e295b33aaaaa6f8b67ff38b3d'),
98
+ dict(path='../../omlish/logs/std/proxy.py', sha1='3e7301a2aa351127f9c85f61b2f85dcc3f15aafb'),
99
+ dict(path='../../omlish/logs/warnings.py', sha1='c4eb694b24773351107fcc058f3620f1dbfb6799'),
100
+ dict(path='../../omlish/os/deathsig.py', sha1='5d3f1a22132b7029d32e29b13c1cc20497c8f7a8'),
101
+ dict(path='../../omlish/os/environ.py', sha1='5e9ed4817af65683b496af49fef996630c3113b1'),
102
+ dict(path='../../omlish/os/linux.py', sha1='b6433d321eba7afab353b04107819fdc72f1d836'),
103
+ dict(path='../../omlish/os/paths.py', sha1='56c40b7c2aa84d1778d60ee4cda498f8c380cc8d'),
104
+ dict(path='../../omlish/shlex.py', sha1='a69721913bcd4f4008600e390fb7822637c2a8ec'),
105
+ dict(path='../../omdev/home/paths.py', sha1='a83516c97a2e99e79153a414db3d23091186bb23'),
106
+ dict(path='../../omdev/packaging/specifiers.py', sha1='a56ab4e8c9b174adb523921f6280ac41e0fce749'),
107
+ dict(path='deploy/paths/specs.py', sha1='023167da1ad9fcf09d9d44963177175591a97377'),
108
+ dict(path='remote/config.py', sha1='48f9367e9db4b23166657ff34eb644c9869d48a8'),
109
+ dict(path='remote/payload.py', sha1='acacf4c2901b7708224af5d4414ecb823947297a'),
110
+ dict(path='targets/bestpython.py', sha1='75c16ab86397a8e81017f148a2ef711567b6ab27'),
111
+ dict(path='targets/targets.py', sha1='d07f2d30c31bad89bd4a3b44bb6a5b6c95c05888'),
112
+ dict(path='../../omlish/argparse/cli.py', sha1='f4dc3cd353d14386b5da0306768700e396afd2b3'),
113
+ dict(path='../../omlish/configs/formats.py', sha1='9bc4f953b4b8700f6f109e6f49e2d70f8e48ce7c'),
114
+ dict(path='../../omlish/lite/marshal.py', sha1='96348f5f2a26dc27d842d33cc3927e9da163436b'),
115
+ dict(path='../../omlish/lite/maybes.py', sha1='bdf5136654ccd14b6a072588cad228925bdfbabd'),
116
+ dict(path='../../omlish/lite/runtime.py', sha1='2e752a27ae2bf89b1bb79b4a2da522a3ec360c70'),
117
+ dict(path='../../omlish/lite/timeouts.py', sha1='a0f673033a6943f242e35848d78a41892b9c62a1'),
118
+ dict(path='../../omlish/logs/infos.py', sha1='4dd104bd468a8c438601dd0bbda619b47d2f1620'),
119
+ dict(path='../../omlish/logs/protocols.py', sha1='05ca4d1d7feb50c4e3b9f22ee371aa7bf4b3dbd1'),
120
+ dict(path='../../omlish/logs/std/json.py', sha1='2a75553131e4d5331bb0cedde42aa183f403fc3b'),
121
+ dict(path='../../omlish/os/atomics.py', sha1='ccb62620b95f60ac50561c283d50e5fcfdccb215'),
122
+ dict(path='../../omlish/text/indent.py', sha1='cc23647bdcd8d26c8afe9e36a0aefb32da58cbb8'),
123
+ dict(path='../../omdev/interp/types.py', sha1='caf068a6e81fb6e221d777b341ac5777d92b8091'),
124
+ dict(path='commands/base.py', sha1='17310f7272b6ac7b6438e32bfd7b24004d284399'),
125
+ dict(path='deploy/conf/specs.py', sha1='d191fa887c59198f5eff5c62414031204a76fa65'),
126
+ dict(path='deploy/tags.py', sha1='e6e7a1f4fcee9f5764acbe7f307b31e26f81bbc3'),
127
+ dict(path='marshal.py', sha1='175f59215a92afd42468f6258f1202b9ec3362cb'),
128
+ dict(path='remote/channel.py', sha1='2b6498da48ff89901b88a6a1ef84c58b37ddb410'),
129
+ dict(path='../../omlish/asyncs/asyncio/timeouts.py', sha1='4d31b02b3c39b8f2fa7e94db36552fde6942e36a'),
130
+ dict(path='../../omlish/configs/nginx.py', sha1='d0ad33c21674fc02f23281247517c827f455eb0b'),
131
+ dict(path='../../omlish/lite/configs.py', sha1='c8602e0e197ef1133e7e8e248935ac745bfd46cb'),
132
+ dict(path='../../omlish/lite/inject.py', sha1='6f097e3170019a34ff6834d36fcc9cbeed3a7ab4'),
133
+ dict(path='../../omlish/logs/contexts.py', sha1='7456964ade9ac66460e9ade4e242dbdc24b39501'),
134
+ dict(path='../../omlish/logs/standard.py', sha1='818b674f7d15012f25b79f52f6e8e7368b633038'),
135
+ dict(path='../../omlish/subprocesses/run.py', sha1='8200e48f0c49d164df3503cd0143038d0c4d30aa'),
136
+ dict(path='../../omlish/subprocesses/wrap.py', sha1='8a9b7d2255481fae15c05f5624b0cdc0766f4b3f'),
137
+ dict(path='../../omdev/interp/providers/base.py', sha1='f5d068c21f230d742e9015b033cd6320f4c68898'),
138
+ dict(path='commands/injection.py', sha1='f7d8aec3c33efc61da1f0c6700bdfbe7bcc10e56'),
139
+ dict(path='commands/marshal.py', sha1='a21c3a75fe17bb80d32d10a3d5524d67a96ea210'),
140
+ dict(path='commands/ping.py', sha1='af4c34e9b1811269c954cf502b336a6446639a2a'),
141
+ dict(path='commands/types.py', sha1='10b88571981b9964287f30f27abf6d09400b51c6'),
142
+ dict(path='deploy/paths/paths.py', sha1='bf7794e998caa1611277ac5809eb7ec91a76d1e8'),
143
+ dict(path='deploy/specs.py', sha1='b3a411b32b47f81f5ad673d8b0338970a6eb6ff9'),
144
+ dict(path='../../omlish/logs/base.py', sha1='a376460b11b9dc0555fd4ead5437af62c2109a4b'),
145
+ dict(path='../../omlish/logs/std/records.py', sha1='8bbf6ef9eccb3a012c6ca416ddf3969450fd8fc9'),
146
+ dict(path='../../omlish/subprocesses/base.py', sha1='cb9f668be5422fecb27222caabb67daac6c1bab9'),
147
+ dict(path='../../omdev/interp/resolvers.py', sha1='817b8e76401cd7a19eb43ca54d65272e4c8a4b0e'),
148
+ dict(path='commands/local.py', sha1='db3c5b0a1f067f54e2133234e36e7db393e4dec3'),
149
+ dict(path='deploy/conf/manager.py', sha1='7450a8616dbc46f6c68387192035a6ea258aebe2'),
150
+ dict(path='deploy/paths/owners.py', sha1='382bcec4824f0fc71dddf083c6e88748b5c62ef2'),
151
+ dict(path='../../omlish/logs/std/loggers.py', sha1='daa35bdc4adea5006e442688017f0de3392579b7'),
152
+ dict(path='../../omlish/subprocesses/asyncs.py', sha1='bba44d524c24c6ac73168aee6343488414e5bf48'),
153
+ dict(path='../../omlish/subprocesses/sync.py', sha1='8434919eba4da67825773d56918fdc0cb2f1883b'),
154
+ dict(path='../../omdev/git/shallow.py', sha1='7b5f9d77b7a01df5828ca61a2adc6dae54cf676b'),
155
+ dict(path='deploy/injection.py', sha1='7d641dd20ff0c75de5679079c52647653849d6cc'),
156
+ dict(path='deploy/paths/manager.py', sha1='eb1c84e0ca03083f69b53cee25bf7ffd752cc7a9'),
157
+ dict(path='deploy/tmp.py', sha1='d8b7aeaa26ab58e64aba371d46bc661462d47c5e'),
158
+ dict(path='../../omlish/asyncs/asyncio/subprocesses.py', sha1='b6b5f9ae3fd0b9c83593bad2e04a08f726e5904d'),
159
+ dict(path='../../omlish/logs/modules.py', sha1='99e73cde6872fd5eda6af3dbf0fc9322bdeb641a'),
160
+ dict(path='../../omdev/interp/inspect.py', sha1='736287b4ec8d14a8c30afa0ba23996fdc0662caa'),
161
+ dict(path='../../omdev/interp/pyenv/pyenv.py', sha1='d1f6e657c671c1b1a5b0e627284df656fe2d10d3'),
162
+ dict(path='../../omdev/interp/uv/uv.py', sha1='8c6515cd6755efab3972da92a285e94ccb255515'),
163
+ dict(path='commands/subprocess.py', sha1='788bd859701fce066bd00c820919f826b43b8b57'),
164
+ dict(path='deploy/conf/inject.py', sha1='d006b45d92f3b5f30a797b65fbed23f90c3db490'),
165
+ dict(path='deploy/git.py', sha1='5ee2e816e18fef493cb2ccc33f33ad673175ad7a'),
166
+ dict(path='deploy/paths/inject.py', sha1='1c501d086fcbde9c2b9ead21fc3c7b175bbf4f76'),
167
+ dict(path='deploy/systemd.py', sha1='773c4482e85a974443bb26237a86b1dfbcd9936c'),
168
+ dict(path='remote/execution.py', sha1='005da809e58790a0e5255df8e57afd5cd6268d7d'),
169
+ dict(path='remote/spawning.py', sha1='9cb6b5da1ba6daabb35a5742be647748c01d40d3'),
170
+ dict(path='system/packages.py', sha1='9988fc93dbca9336c378bf5fad6f68f5b8c0260e'),
171
+ dict(path='system/platforms.py', sha1='f3fc312318cff15f97dd9b10fa5f2408abc45a1b'),
172
+ dict(path='../../omdev/interp/providers/running.py', sha1='85c9cc69ff6fbd6c8cf78ed6262619a30856c2f1'),
173
+ dict(path='../../omdev/interp/providers/system.py', sha1='9638a154475ca98775159d27739563ac7fb2eb16'),
174
+ dict(path='../../omdev/interp/pyenv/install.py', sha1='4a10a19717364b4ba9f3b8bf1d12621cf21ba8b8'),
175
+ dict(path='../../omdev/interp/uv/provider.py', sha1='3c3980878ad2b9fd2cd02172f9424954759c7f06'),
176
+ dict(path='commands/inject.py', sha1='7a95b6487b01230dd2fe0d9f67382d8889039e7b'),
177
+ dict(path='system/commands.py', sha1='17bbaa945b6ded0a88d31c52b410cbce8fc324a0'),
178
+ dict(path='system/config.py', sha1='fd1ebc2cf36fd312ff69d1af100a7e9c638f1fcc'),
179
+ dict(path='../../omdev/interp/providers/inject.py', sha1='7cc9ebf58cf2ec09545321456bd9da9f9a3a79fb'),
180
+ dict(path='../../omdev/interp/pyenv/provider.py', sha1='377542ce01a35849e2a5b4a4dbafedc26882f983'),
181
+ dict(path='../../omdev/interp/uv/inject.py', sha1='e95d058c2340baa5a3155ec3440f311d1daa10a8'),
182
+ dict(path='bootstrap.py', sha1='e66138947a41e8a49576885cf4b1390315d44f88'),
183
+ dict(path='system/inject.py', sha1='8a34be1b982cb42981c08306f12994a0fff258bd'),
184
+ dict(path='../../omdev/interp/pyenv/inject.py', sha1='b8fb68f5a7cae86c70fe1bad6c29a8b2dfc985c3'),
185
+ dict(path='remote/_main.py', sha1='5ae1dc673ce22f2d40612c66b3a5c3d01ebb6718'),
186
+ dict(path='../../omdev/interp/inject.py', sha1='b039abbadf0b096d2724182af2e0ebda2a230852'),
187
+ dict(path='remote/connection.py', sha1='18bc6c4a446a9bef3c54b861a01418b1b7ba39ff'),
188
+ dict(path='../../omdev/interp/default.py', sha1='a799969a0d3f4b57538587b13ceb08f6334ebc16'),
189
+ dict(path='remote/inject.py', sha1='8713a421b18ff7625c95017616380f0500c3c39c'),
190
+ dict(path='targets/connection.py', sha1='891f1d35ee3814bed32e6de71e1ca47574635da1'),
191
+ dict(path='deploy/interp.py', sha1='89371a87a275fea2e8566a0983e4906cda46a105'),
192
+ dict(path='deploy/venvs.py', sha1='7ad41a11098dd68d83a0804d2ea95779d0be4de0'),
193
+ dict(path='targets/inject.py', sha1='e4bfba31b044da9545d4c00965e7e15b97a40cce'),
194
+ dict(path='deploy/apps.py', sha1='6df5d728b6715583a792d1e92268c0c07502509f'),
195
+ dict(path='deploy/deploy.py', sha1='635f84ad370b22797406d2ae55d78621ea1f7b2b'),
196
+ dict(path='deploy/commands.py', sha1='0e9fdd122fe3a4028efede0862b059c091cc13cb'),
197
+ dict(path='deploy/inject.py', sha1='d84e9c3e980c5a1ec62d18628d8911f5b6bb125f'),
198
+ dict(path='inject.py', sha1='b1c173df021f190d3631f3828470fa1564e0b4b4'),
199
+ dict(path='bootstrap_.py', sha1='c7d7c3e88703200a836df63e395f7154bf871f36'),
200
+ dict(path='main.py', sha1='ddddcdb54aabe67188dce6a5f65e04ce296a75b0'),
201
+ ],
202
+ )
203
+
204
+
68
205
  ########################################
69
206
 
70
207
 
@@ -90,7 +227,7 @@ TomlParseFloat = ta.Callable[[str], ta.Any] # ta.TypeAlias
90
227
  TomlKey = ta.Tuple[str, ...] # ta.TypeAlias
91
228
  TomlPos = int # ta.TypeAlias
92
229
 
93
- # ../../omlish/lite/attrops.py
230
+ # ../../omlish/lite/abstract.py
94
231
  T = ta.TypeVar('T')
95
232
 
96
233
  # ../../omlish/lite/cached.py
@@ -281,12 +418,12 @@ class _BaseVersion:
281
418
 
282
419
  def __lt__(self, other: '_BaseVersion') -> bool:
283
420
  if not isinstance(other, _BaseVersion):
284
- return NotImplemented # type: ignore
421
+ return NotImplemented
285
422
  return self._key < other._key
286
423
 
287
424
  def __le__(self, other: '_BaseVersion') -> bool:
288
425
  if not isinstance(other, _BaseVersion):
289
- return NotImplemented # type: ignore
426
+ return NotImplemented
290
427
  return self._key <= other._key
291
428
 
292
429
  def __eq__(self, other: object) -> bool:
@@ -296,12 +433,12 @@ class _BaseVersion:
296
433
 
297
434
  def __ge__(self, other: '_BaseVersion') -> bool:
298
435
  if not isinstance(other, _BaseVersion):
299
- return NotImplemented # type: ignore
436
+ return NotImplemented
300
437
  return self._key >= other._key
301
438
 
302
439
  def __gt__(self, other: '_BaseVersion') -> bool:
303
440
  if not isinstance(other, _BaseVersion):
304
- return NotImplemented # type: ignore
441
+ return NotImplemented
305
442
  return self._key > other._key
306
443
 
307
444
  def __ne__(self, other: object) -> bool:
@@ -2394,25 +2531,49 @@ def is_abstract_method(obj: ta.Any) -> bool:
2394
2531
  return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
2395
2532
 
2396
2533
 
2397
- def update_abstracts(cls, *, force=False):
2534
+ def compute_abstract_methods(cls: type) -> ta.FrozenSet[str]:
2535
+ # ~> https://github.com/python/cpython/blob/f3476c6507381ca860eec0989f53647b13517423/Modules/_abc.c#L358
2536
+
2537
+ # Stage 1: direct abstract methods
2538
+
2539
+ abstracts = {
2540
+ a
2541
+ # Get items as a list to avoid mutation issues during iteration
2542
+ for a, v in list(cls.__dict__.items())
2543
+ if is_abstract_method(v)
2544
+ }
2545
+
2546
+ # Stage 2: inherited abstract methods
2547
+
2548
+ for base in cls.__bases__:
2549
+ # Get __abstractmethods__ from base if it exists
2550
+ if (base_abstracts := getattr(base, _ABSTRACT_METHODS_ATTR, None)) is None:
2551
+ continue
2552
+
2553
+ # Iterate over abstract methods in base
2554
+ for key in base_abstracts:
2555
+ # Check if this class has an attribute with this name
2556
+ try:
2557
+ value = getattr(cls, key)
2558
+ except AttributeError:
2559
+ # Attribute not found in this class, skip
2560
+ continue
2561
+
2562
+ # Check if it's still abstract
2563
+ if is_abstract_method(value):
2564
+ abstracts.add(key)
2565
+
2566
+ return frozenset(abstracts)
2567
+
2568
+
2569
+ def update_abstracts(cls: ta.Type[T], *, force: bool = False) -> ta.Type[T]:
2398
2570
  if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
2399
2571
  # Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
2400
2572
  # implementation (especially during testing), and we want to handle both cases.
2401
2573
  return cls
2402
2574
 
2403
- abstracts: ta.Set[str] = set()
2404
-
2405
- for scls in cls.__bases__:
2406
- for name in getattr(scls, _ABSTRACT_METHODS_ATTR, ()):
2407
- value = getattr(cls, name, None)
2408
- if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
2409
- abstracts.add(name)
2410
-
2411
- for name, value in cls.__dict__.items():
2412
- if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
2413
- abstracts.add(name)
2414
-
2415
- setattr(cls, _ABSTRACT_METHODS_ATTR, frozenset(abstracts))
2575
+ abstracts = compute_abstract_methods(cls)
2576
+ setattr(cls, _ABSTRACT_METHODS_ATTR, abstracts)
2416
2577
  return cls
2417
2578
 
2418
2579
 
@@ -2466,23 +2627,26 @@ class Abstract:
2466
2627
  super().__init_subclass__(**kwargs)
2467
2628
 
2468
2629
  if not (Abstract in cls.__bases__ or abc.ABC in cls.__bases__):
2469
- ams = {a: cls for a, o in cls.__dict__.items() if is_abstract_method(o)}
2470
-
2471
- seen = set(cls.__dict__)
2472
- for b in cls.__bases__:
2473
- ams.update({a: b for a in set(getattr(b, _ABSTRACT_METHODS_ATTR, [])) - seen}) # noqa
2474
- seen.update(dir(b))
2630
+ if ams := compute_abstract_methods(cls):
2631
+ amd = {
2632
+ a: mcls
2633
+ for mcls in cls.__mro__[::-1]
2634
+ for a in ams
2635
+ if a in mcls.__dict__
2636
+ }
2475
2637
 
2476
- if ams:
2477
2638
  raise AbstractTypeError(
2478
2639
  f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
2479
2640
  ', '.join(sorted([
2480
2641
  '.'.join([
2481
- *([m] if (m := getattr(c, '__module__')) else []),
2482
- getattr(c, '__qualname__', getattr(c, '__name__')),
2642
+ *([
2643
+ *([m] if (m := getattr(c, '__module__')) else []),
2644
+ getattr(c, '__qualname__', getattr(c, '__name__')),
2645
+ ] if c is not None else '?'),
2483
2646
  a,
2484
2647
  ])
2485
- for a, c in ams.items()
2648
+ for a in ams
2649
+ for c in [amd.get(a)]
2486
2650
  ])),
2487
2651
  )
2488
2652
 
@@ -2507,6 +2671,8 @@ TODO:
2507
2671
  - per-attr repr transform / filter
2508
2672
  - __ne__ ? cases where it still matters
2509
2673
  - ordering ?
2674
+ - repr_filter: ta.Union[ta.Callable[[ta.Any], ta.Optional[str]], ta.Literal['not_none', 'truthy']]] ?
2675
+ - unify repr/repr_fn/repr_filter
2510
2676
  """
2511
2677
 
2512
2678
 
@@ -3561,7 +3727,7 @@ class ExitStacked:
3561
3727
  es.__enter__()
3562
3728
  try:
3563
3729
  self._enter_contexts()
3564
- except Exception: # noqa
3730
+ except BaseException: # noqa
3565
3731
  es.__exit__(*sys.exc_info())
3566
3732
  raise
3567
3733
  return self
@@ -3572,7 +3738,7 @@ class ExitStacked:
3572
3738
  return None
3573
3739
  try:
3574
3740
  self._exit_contexts()
3575
- except Exception: # noqa
3741
+ except BaseException: # noqa
3576
3742
  es.__exit__(*sys.exc_info())
3577
3743
  raise
3578
3744
  return es.__exit__(exc_type, exc_val, exc_tb)
@@ -3620,7 +3786,7 @@ class AsyncExitStacked:
3620
3786
  await es.__aenter__()
3621
3787
  try:
3622
3788
  await self._async_enter_contexts()
3623
- except Exception: # noqa
3789
+ except BaseException: # noqa
3624
3790
  await es.__aexit__(*sys.exc_info())
3625
3791
  raise
3626
3792
  return self
@@ -3631,7 +3797,7 @@ class AsyncExitStacked:
3631
3797
  return None
3632
3798
  try:
3633
3799
  await self._async_exit_contexts()
3634
- except Exception: # noqa
3800
+ except BaseException: # noqa
3635
3801
  await es.__aexit__(*sys.exc_info())
3636
3802
  raise
3637
3803
  return await es.__aexit__(exc_type, exc_val, exc_tb)
@@ -3829,7 +3995,7 @@ def dir_dict(o: ta.Any) -> ta.Dict[str, ta.Any]:
3829
3995
  ##
3830
3996
 
3831
3997
 
3832
- DEFAULT_PYCHARM_VERSION = '242.23726.102'
3998
+ DEFAULT_PYCHARM_VERSION = '252.26199.168'
3833
3999
 
3834
4000
 
3835
4001
  @dc.dataclass(frozen=True)
@@ -4073,6 +4239,12 @@ def format_num_bytes(num_bytes: int) -> str:
4073
4239
 
4074
4240
  ##
4075
4241
  # A workaround for typing deficiencies (like `Argument 2 to NewType(...) must be subclassable`).
4242
+ #
4243
+ # Note that this problem doesn't happen at runtime - it happens in mypy:
4244
+ #
4245
+ # mypy <(echo "import typing as ta; MyCallback = ta.NewType('MyCallback', ta.Callable[[], None])")
4246
+ # /dev/fd/11:1:22: error: Argument 2 to NewType(...) must be subclassable (got "Callable[[], None]") [valid-newtype]
4247
+ #
4076
4248
 
4077
4249
 
4078
4250
  @dc.dataclass(frozen=True)
@@ -5615,7 +5787,7 @@ class SpecifierSet(BaseSpecifier):
5615
5787
  if isinstance(other, str):
5616
5788
  other = SpecifierSet(other)
5617
5789
  elif not isinstance(other, SpecifierSet):
5618
- return NotImplemented # type: ignore
5790
+ return NotImplemented
5619
5791
 
5620
5792
  specifier = SpecifierSet()
5621
5793
  specifier._specs = frozenset(self._specs | other._specs)
@@ -5635,6 +5807,7 @@ class SpecifierSet(BaseSpecifier):
5635
5807
  if isinstance(other, (str, Specifier)):
5636
5808
  other = SpecifierSet(str(other))
5637
5809
  elif not isinstance(other, SpecifierSet):
5810
+
5638
5811
  return NotImplemented
5639
5812
 
5640
5813
  return self._specs == other._specs
@@ -5786,7 +5959,7 @@ BEST_PYTHON_SH = """\
5786
5959
  bv=""
5787
5960
  bx=""
5788
5961
 
5789
- for v in "" 3 3.{8..13}; do
5962
+ for v in "" 3 3.{8..14}; do
5790
5963
  x="python$v"
5791
5964
  v=$($x -c "import sys; print((\\"%02d\\" * 3) % sys.version_info[:3])" 2>/dev/null)
5792
5965
  if [ $? -eq 0 ] && [ "$v" \\> 030799 ] && ([ -z "$bv" ] || [ "$v" \\> "$bv" ]); then
@@ -5921,6 +6094,7 @@ TODO:
5921
6094
  - pre-run, post-run hooks
5922
6095
  - exitstack?
5923
6096
  - suggestion - difflib.get_close_matches
6097
+ - add_argument_group - group kw on ArgparseKwarg?
5924
6098
  """
5925
6099
 
5926
6100
 
@@ -5931,6 +6105,7 @@ TODO:
5931
6105
  class ArgparseArg:
5932
6106
  args: ta.Sequence[ta.Any]
5933
6107
  kwargs: ta.Mapping[str, ta.Any]
6108
+ group: ta.Optional[str] = None
5934
6109
  dest: ta.Optional[str] = None
5935
6110
 
5936
6111
  def __get__(self, instance, owner=None):
@@ -5940,7 +6115,11 @@ class ArgparseArg:
5940
6115
 
5941
6116
 
5942
6117
  def argparse_arg(*args, **kwargs) -> ArgparseArg:
5943
- return ArgparseArg(args, kwargs)
6118
+ return ArgparseArg(
6119
+ args=args,
6120
+ group=kwargs.pop('group', None),
6121
+ kwargs=kwargs,
6122
+ )
5944
6123
 
5945
6124
 
5946
6125
  def argparse_arg_(*args, **kwargs) -> ta.Any:
@@ -6110,6 +6289,10 @@ class ArgparseCli:
6110
6289
  subparser.set_defaults(_cmd=obj)
6111
6290
 
6112
6291
  elif isinstance(obj, ArgparseArg):
6292
+ if obj.group is not None:
6293
+ # FIXME: add_argument_group
6294
+ raise NotImplementedError
6295
+
6113
6296
  if att in anns:
6114
6297
  ann_kwargs = _get_argparse_arg_ann_kwargs(anns[att])
6115
6298
  obj.kwargs = {**ann_kwargs, **obj.kwargs}
@@ -6155,7 +6338,7 @@ class ArgparseCli:
6155
6338
 
6156
6339
  if self._unknown_args and not (cmd is not None and cmd.accepts_unknown):
6157
6340
  msg = f'unrecognized arguments: {" ".join(self._unknown_args)}'
6158
- if (parser := self.get_parser()).exit_on_error:
6341
+ if (parser := self.get_parser()).exit_on_error: # noqa
6159
6342
  parser.error(msg)
6160
6343
  else:
6161
6344
  raise argparse.ArgumentError(None, msg)
@@ -6175,7 +6358,10 @@ class ArgparseCli:
6175
6358
  return fn()
6176
6359
 
6177
6360
  def cli_run_and_exit(self) -> ta.NoReturn:
6178
- sys.exit(rc if isinstance(rc := self.cli_run(), int) else 0)
6361
+ rc = self.cli_run()
6362
+ if not isinstance(rc, int):
6363
+ rc = 0
6364
+ raise SystemExit(rc)
6179
6365
 
6180
6366
  def __call__(self, *, exit: bool = False) -> ta.Optional[int]: # noqa
6181
6367
  if exit:
@@ -7417,6 +7603,13 @@ class _EmptyMaybe(_Maybe[T]):
7417
7603
  Maybe._empty = _EmptyMaybe() # noqa
7418
7604
 
7419
7605
 
7606
+ ##
7607
+
7608
+
7609
+ setattr(Maybe, 'just', _JustMaybe) # noqa
7610
+ setattr(Maybe, 'empty', functools.partial(operator.attrgetter('_empty'), Maybe))
7611
+
7612
+
7420
7613
  ########################################
7421
7614
  # ../../../omlish/lite/runtime.py
7422
7615
 
@@ -8419,9 +8612,10 @@ class InterpSpecifier:
8419
8612
  def parse(cls, s: str) -> 'InterpSpecifier':
8420
8613
  s, o = InterpOpts.parse_suffix(s)
8421
8614
  if not any(s.startswith(o) for o in Specifier.OPERATORS):
8422
- s = '~=' + s
8423
8615
  if s.count('.') < 2:
8424
- s += '.0'
8616
+ s = '~=' + s + '.0'
8617
+ else:
8618
+ s = '==' + s
8425
8619
  return cls(
8426
8620
  specifier=Specifier(s),
8427
8621
  opts=o,
@@ -12019,6 +12213,10 @@ class VerboseCalledProcessError(subprocess.CalledProcessError):
12019
12213
  class BaseSubprocesses(Abstract):
12020
12214
  DEFAULT_LOGGER: ta.ClassVar[ta.Optional[LoggerLike]] = None
12021
12215
 
12216
+ PIPE: ta.ClassVar[int] = subprocess.PIPE
12217
+ STDOUT: ta.ClassVar[int] = subprocess.STDOUT
12218
+ DEVNULL: ta.ClassVar[int] = subprocess.DEVNULL
12219
+
12022
12220
  def __init__(
12023
12221
  self,
12024
12222
  *,
@@ -15081,12 +15279,42 @@ uv run pip
15081
15279
  uv run --python 3.11.6 pip
15082
15280
  uv venv --python 3.11.6 --seed barf
15083
15281
  python3 -m venv barf && barf/bin/pip install uv && barf/bin/uv venv --python 3.11.6 --seed barf2
15282
+ uv python find '3.13.10'
15283
+ uv python list --output-format=json
15084
15284
  """
15085
15285
 
15086
15286
 
15087
15287
  ##
15088
15288
 
15089
15289
 
15290
+ @dc.dataclass(frozen=True)
15291
+ class UvPythonListOutput:
15292
+ key: str
15293
+ version: str
15294
+
15295
+ @dc.dataclass(frozen=True)
15296
+ class VersionParts:
15297
+ major: int
15298
+ minor: int
15299
+ patch: int
15300
+
15301
+ version_parts: VersionParts
15302
+
15303
+ path: ta.Optional[str]
15304
+ symlink: ta.Optional[str]
15305
+
15306
+ url: str
15307
+
15308
+ os: str # emscripten linux macos
15309
+ variant: str # default freethreaded
15310
+ implementation: str # cpython graalpy pyodide pypy
15311
+ arch: str # aarch64 wasm32 x86_64
15312
+ libc: str # gnu musl none
15313
+
15314
+
15315
+ ##
15316
+
15317
+
15090
15318
  class UvInterpProvider(InterpProvider):
15091
15319
  def __init__(
15092
15320
  self,
@@ -15107,6 +15335,12 @@ class UvInterpProvider(InterpProvider):
15107
15335
  async def get_installed_version(self, version: InterpVersion) -> Interp:
15108
15336
  raise NotImplementedError
15109
15337
 
15338
+ # async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
15339
+ # return []
15340
+
15341
+ # async def install_version(self, version: InterpVersion) -> Interp:
15342
+ # raise TypeError
15343
+
15110
15344
 
15111
15345
  ########################################
15112
15346
  # ../commands/inject.py
@@ -16956,7 +17190,7 @@ class MainCli(ArgparseCli):
16956
17190
 
16957
17191
 
16958
17192
  def _main() -> None:
16959
- sys.exit(asyncio.run(MainCli().async_cli_run()))
17193
+ raise SystemExit(asyncio.run(MainCli().async_cli_run()))
16960
17194
 
16961
17195
 
16962
17196
  if __name__ == '__main__':