ominfra 0.0.0.dev433__py3-none-any.whl → 0.0.0.dev501__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ominfra might be problematic. Click here for more details.

Files changed (29) hide show
  1. ominfra/README.md +26 -0
  2. ominfra/__about__.py +5 -2
  3. ominfra/clouds/aws/instancetypes/cache.json.gz +0 -0
  4. ominfra/clouds/aws/journald2aws/main.py +1 -1
  5. ominfra/clouds/aws/models/{base.py → base/__init__.py} +6 -0
  6. ominfra/clouds/aws/models/base/_dataclasses.py +721 -0
  7. ominfra/clouds/aws/models/gen/cli.py +2 -1
  8. ominfra/clouds/aws/models/gen/gen.py +16 -7
  9. ominfra/clouds/aws/models/services/{ec2.py → ec2/__init__.py} +123 -1
  10. ominfra/clouds/aws/models/services/ec2/_dataclasses.py +30654 -0
  11. ominfra/clouds/aws/models/services/{lambda_.py → lambda_/__init__.py} +139 -1
  12. ominfra/clouds/aws/models/services/lambda_/_dataclasses.py +4182 -0
  13. ominfra/clouds/aws/models/services/{rds.py → rds/__init__.py} +244 -78
  14. ominfra/clouds/aws/models/services/rds/_dataclasses.py +8231 -0
  15. ominfra/clouds/aws/models/services/{s3.py → s3/__init__.py} +9 -1
  16. ominfra/clouds/aws/models/services/s3/_dataclasses.py +5014 -0
  17. ominfra/manage/bootstrap_.py +1 -1
  18. ominfra/manage/main.py +1 -2
  19. ominfra/manage/targets/bestpython.sh +1 -1
  20. ominfra/scripts/journald2aws.py +748 -71
  21. ominfra/scripts/manage.py +824 -99
  22. ominfra/scripts/supervisor.py +925 -123
  23. ominfra/supervisor/main.py +1 -1
  24. {ominfra-0.0.0.dev433.dist-info → ominfra-0.0.0.dev501.dist-info}/METADATA +7 -5
  25. {ominfra-0.0.0.dev433.dist-info → ominfra-0.0.0.dev501.dist-info}/RECORD +29 -23
  26. {ominfra-0.0.0.dev433.dist-info → ominfra-0.0.0.dev501.dist-info}/WHEEL +0 -0
  27. {ominfra-0.0.0.dev433.dist-info → ominfra-0.0.0.dev501.dist-info}/entry_points.txt +0 -0
  28. {ominfra-0.0.0.dev433.dist-info → ominfra-0.0.0.dev501.dist-info}/licenses/LICENSE +0 -0
  29. {ominfra-0.0.0.dev433.dist-info → ominfra-0.0.0.dev501.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,22 +66,160 @@ 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/asyncs.py', sha1='b3f2251c56617ce548abf9c333ac996b63edb23e'),
86
+ dict(path='../../omlish/lite/attrops.py', sha1='c1ebfb8573d766d34593c452a2377208d02726dc'),
87
+ dict(path='../../omlish/lite/cached.py', sha1='0c33cf961ac8f0727284303c7a30c5ea98f714f2'),
88
+ dict(path='../../omlish/lite/check.py', sha1='bb6b6b63333699b84462951a854d99ae83195b94'),
89
+ dict(path='../../omlish/lite/contextmanagers.py', sha1='993f5ed96d3410f739a20363f55670d5e5267fa3'),
90
+ dict(path='../../omlish/lite/json.py', sha1='57eeddc4d23a17931e00284ffa5cb6e3ce089486'),
91
+ dict(path='../../omlish/lite/objects.py', sha1='9566bbf3530fd71fcc56321485216b592fae21e9'),
92
+ dict(path='../../omlish/lite/pycharm.py', sha1='6f84e57f02e2f1075918002f89e4201910d2a15e'),
93
+ dict(path='../../omlish/lite/reflect.py', sha1='c4fec44bf144e9d93293c996af06f6c65fc5e63d'),
94
+ dict(path='../../omlish/lite/resources.py', sha1='1365cb6046eb929358e7c86a3fda20d95fd4a296'),
95
+ dict(path='../../omlish/lite/strings.py', sha1='89831ecbc34ad80e118a865eceb390ed399dc4d6'),
96
+ dict(path='../../omlish/lite/typing.py', sha1='048bb5fb8ecad5be101516f8f3b7996707f5bc42'),
97
+ dict(path='../../omlish/logs/levels.py', sha1='91405563d082a5eba874da82aac89d83ce7b6152'),
98
+ dict(path='../../omlish/logs/std/filters.py', sha1='f36aab646d84d31e295b33aaaaa6f8b67ff38b3d'),
99
+ dict(path='../../omlish/logs/std/proxy.py', sha1='3e7301a2aa351127f9c85f61b2f85dcc3f15aafb'),
100
+ dict(path='../../omlish/logs/warnings.py', sha1='c4eb694b24773351107fcc058f3620f1dbfb6799'),
101
+ dict(path='../../omlish/os/deathsig.py', sha1='5d3f1a22132b7029d32e29b13c1cc20497c8f7a8'),
102
+ dict(path='../../omlish/os/environ.py', sha1='5e9ed4817af65683b496af49fef996630c3113b1'),
103
+ dict(path='../../omlish/os/linux.py', sha1='b6433d321eba7afab353b04107819fdc72f1d836'),
104
+ dict(path='../../omlish/os/paths.py', sha1='56c40b7c2aa84d1778d60ee4cda498f8c380cc8d'),
105
+ dict(path='../../omlish/shlex.py', sha1='a69721913bcd4f4008600e390fb7822637c2a8ec'),
106
+ dict(path='../../omdev/home/paths.py', sha1='a83516c97a2e99e79153a414db3d23091186bb23'),
107
+ dict(path='../../omdev/packaging/specifiers.py', sha1='a56ab4e8c9b174adb523921f6280ac41e0fce749'),
108
+ dict(path='deploy/paths/specs.py', sha1='023167da1ad9fcf09d9d44963177175591a97377'),
109
+ dict(path='remote/config.py', sha1='48f9367e9db4b23166657ff34eb644c9869d48a8'),
110
+ dict(path='remote/payload.py', sha1='acacf4c2901b7708224af5d4414ecb823947297a'),
111
+ dict(path='targets/bestpython.py', sha1='75c16ab86397a8e81017f148a2ef711567b6ab27'),
112
+ dict(path='targets/targets.py', sha1='d07f2d30c31bad89bd4a3b44bb6a5b6c95c05888'),
113
+ dict(path='../../omlish/argparse/cli.py', sha1='f4dc3cd353d14386b5da0306768700e396afd2b3'),
114
+ dict(path='../../omlish/configs/formats.py', sha1='9bc4f953b4b8700f6f109e6f49e2d70f8e48ce7c'),
115
+ dict(path='../../omlish/lite/marshal.py', sha1='96348f5f2a26dc27d842d33cc3927e9da163436b'),
116
+ dict(path='../../omlish/lite/maybes.py', sha1='04d2fcbea17028a5e6b8e7a7fb742375495ed233'),
117
+ dict(path='../../omlish/lite/runtime.py', sha1='2e752a27ae2bf89b1bb79b4a2da522a3ec360c70'),
118
+ dict(path='../../omlish/lite/timeouts.py', sha1='a0f673033a6943f242e35848d78a41892b9c62a1'),
119
+ dict(path='../../omlish/logs/infos.py', sha1='4dd104bd468a8c438601dd0bbda619b47d2f1620'),
120
+ dict(path='../../omlish/logs/protocols.py', sha1='05ca4d1d7feb50c4e3b9f22ee371aa7bf4b3dbd1'),
121
+ dict(path='../../omlish/logs/std/json.py', sha1='2a75553131e4d5331bb0cedde42aa183f403fc3b'),
122
+ dict(path='../../omlish/os/atomics.py', sha1='ccb62620b95f60ac50561c283d50e5fcfdccb215'),
123
+ dict(path='../../omlish/text/indent.py', sha1='cc23647bdcd8d26c8afe9e36a0aefb32da58cbb8'),
124
+ dict(path='../../omdev/interp/types.py', sha1='caf068a6e81fb6e221d777b341ac5777d92b8091'),
125
+ dict(path='commands/base.py', sha1='17310f7272b6ac7b6438e32bfd7b24004d284399'),
126
+ dict(path='deploy/conf/specs.py', sha1='d191fa887c59198f5eff5c62414031204a76fa65'),
127
+ dict(path='deploy/tags.py', sha1='e6e7a1f4fcee9f5764acbe7f307b31e26f81bbc3'),
128
+ dict(path='marshal.py', sha1='175f59215a92afd42468f6258f1202b9ec3362cb'),
129
+ dict(path='remote/channel.py', sha1='2b6498da48ff89901b88a6a1ef84c58b37ddb410'),
130
+ dict(path='../../omlish/asyncs/asyncio/timeouts.py', sha1='4d31b02b3c39b8f2fa7e94db36552fde6942e36a'),
131
+ dict(path='../../omlish/configs/nginx.py', sha1='d0ad33c21674fc02f23281247517c827f455eb0b'),
132
+ dict(path='../../omlish/lite/configs.py', sha1='c8602e0e197ef1133e7e8e248935ac745bfd46cb'),
133
+ dict(path='../../omlish/lite/inject.py', sha1='6f097e3170019a34ff6834d36fcc9cbeed3a7ab4'),
134
+ dict(path='../../omlish/logs/contexts.py', sha1='1000a6d5ddfb642865ca532e34b1d50759781cf0'),
135
+ dict(path='../../omlish/logs/std/standard.py', sha1='5c97c1b9f7ead58d6127d047b873398f708f288d'),
136
+ dict(path='../../omlish/subprocesses/run.py', sha1='8200e48f0c49d164df3503cd0143038d0c4d30aa'),
137
+ dict(path='../../omlish/subprocesses/wrap.py', sha1='8a9b7d2255481fae15c05f5624b0cdc0766f4b3f'),
138
+ dict(path='../../omdev/interp/providers/base.py', sha1='f5d068c21f230d742e9015b033cd6320f4c68898'),
139
+ dict(path='commands/injection.py', sha1='f7d8aec3c33efc61da1f0c6700bdfbe7bcc10e56'),
140
+ dict(path='commands/marshal.py', sha1='a21c3a75fe17bb80d32d10a3d5524d67a96ea210'),
141
+ dict(path='commands/ping.py', sha1='af4c34e9b1811269c954cf502b336a6446639a2a'),
142
+ dict(path='commands/types.py', sha1='10b88571981b9964287f30f27abf6d09400b51c6'),
143
+ dict(path='deploy/paths/paths.py', sha1='bf7794e998caa1611277ac5809eb7ec91a76d1e8'),
144
+ dict(path='deploy/specs.py', sha1='b3a411b32b47f81f5ad673d8b0338970a6eb6ff9'),
145
+ dict(path='../../omlish/logs/base.py', sha1='8d06faee05fead6b1dd98c9035a5b042af4aebb1'),
146
+ dict(path='../../omlish/logs/std/records.py', sha1='8bbf6ef9eccb3a012c6ca416ddf3969450fd8fc9'),
147
+ dict(path='../../omlish/subprocesses/base.py', sha1='cb9f668be5422fecb27222caabb67daac6c1bab9'),
148
+ dict(path='../../omdev/interp/resolvers.py', sha1='817b8e76401cd7a19eb43ca54d65272e4c8a4b0e'),
149
+ dict(path='commands/local.py', sha1='db3c5b0a1f067f54e2133234e36e7db393e4dec3'),
150
+ dict(path='deploy/conf/manager.py', sha1='7450a8616dbc46f6c68387192035a6ea258aebe2'),
151
+ dict(path='deploy/paths/owners.py', sha1='382bcec4824f0fc71dddf083c6e88748b5c62ef2'),
152
+ dict(path='../../omlish/logs/asyncs.py', sha1='ab11b70033d9f2e9a4e70254185aa1c6130c6077'),
153
+ dict(path='../../omlish/logs/std/loggers.py', sha1='a569179445d6a8a942b5dcfad1d1f77702868803'),
154
+ dict(path='../../omlish/subprocesses/asyncs.py', sha1='bba44d524c24c6ac73168aee6343488414e5bf48'),
155
+ dict(path='../../omlish/subprocesses/sync.py', sha1='8434919eba4da67825773d56918fdc0cb2f1883b'),
156
+ dict(path='../../omdev/git/shallow.py', sha1='7b5f9d77b7a01df5828ca61a2adc6dae54cf676b'),
157
+ dict(path='deploy/injection.py', sha1='7d641dd20ff0c75de5679079c52647653849d6cc'),
158
+ dict(path='deploy/paths/manager.py', sha1='eb1c84e0ca03083f69b53cee25bf7ffd752cc7a9'),
159
+ dict(path='deploy/tmp.py', sha1='d8b7aeaa26ab58e64aba371d46bc661462d47c5e'),
160
+ dict(path='../../omlish/asyncs/asyncio/subprocesses.py', sha1='b6b5f9ae3fd0b9c83593bad2e04a08f726e5904d'),
161
+ dict(path='../../omlish/logs/modules.py', sha1='dd7d5f8e63fe8829dfb49460f3929ab64b68ee14'),
162
+ dict(path='../../omdev/interp/inspect.py', sha1='736287b4ec8d14a8c30afa0ba23996fdc0662caa'),
163
+ dict(path='../../omdev/interp/pyenv/pyenv.py', sha1='d1f6e657c671c1b1a5b0e627284df656fe2d10d3'),
164
+ dict(path='../../omdev/interp/uv/uv.py', sha1='8c6515cd6755efab3972da92a285e94ccb255515'),
165
+ dict(path='commands/subprocess.py', sha1='788bd859701fce066bd00c820919f826b43b8b57'),
166
+ dict(path='deploy/conf/inject.py', sha1='d006b45d92f3b5f30a797b65fbed23f90c3db490'),
167
+ dict(path='deploy/git.py', sha1='5ee2e816e18fef493cb2ccc33f33ad673175ad7a'),
168
+ dict(path='deploy/paths/inject.py', sha1='1c501d086fcbde9c2b9ead21fc3c7b175bbf4f76'),
169
+ dict(path='deploy/systemd.py', sha1='773c4482e85a974443bb26237a86b1dfbcd9936c'),
170
+ dict(path='remote/execution.py', sha1='005da809e58790a0e5255df8e57afd5cd6268d7d'),
171
+ dict(path='remote/spawning.py', sha1='9cb6b5da1ba6daabb35a5742be647748c01d40d3'),
172
+ dict(path='system/packages.py', sha1='9988fc93dbca9336c378bf5fad6f68f5b8c0260e'),
173
+ dict(path='system/platforms.py', sha1='f3fc312318cff15f97dd9b10fa5f2408abc45a1b'),
174
+ dict(path='../../omdev/interp/providers/running.py', sha1='85c9cc69ff6fbd6c8cf78ed6262619a30856c2f1'),
175
+ dict(path='../../omdev/interp/providers/system.py', sha1='9638a154475ca98775159d27739563ac7fb2eb16'),
176
+ dict(path='../../omdev/interp/pyenv/install.py', sha1='4a10a19717364b4ba9f3b8bf1d12621cf21ba8b8'),
177
+ dict(path='../../omdev/interp/uv/provider.py', sha1='3c3980878ad2b9fd2cd02172f9424954759c7f06'),
178
+ dict(path='commands/inject.py', sha1='7a95b6487b01230dd2fe0d9f67382d8889039e7b'),
179
+ dict(path='system/commands.py', sha1='17bbaa945b6ded0a88d31c52b410cbce8fc324a0'),
180
+ dict(path='system/config.py', sha1='fd1ebc2cf36fd312ff69d1af100a7e9c638f1fcc'),
181
+ dict(path='../../omdev/interp/providers/inject.py', sha1='7cc9ebf58cf2ec09545321456bd9da9f9a3a79fb'),
182
+ dict(path='../../omdev/interp/pyenv/provider.py', sha1='377542ce01a35849e2a5b4a4dbafedc26882f983'),
183
+ dict(path='../../omdev/interp/uv/inject.py', sha1='e95d058c2340baa5a3155ec3440f311d1daa10a8'),
184
+ dict(path='bootstrap.py', sha1='e66138947a41e8a49576885cf4b1390315d44f88'),
185
+ dict(path='system/inject.py', sha1='8a34be1b982cb42981c08306f12994a0fff258bd'),
186
+ dict(path='../../omdev/interp/pyenv/inject.py', sha1='b8fb68f5a7cae86c70fe1bad6c29a8b2dfc985c3'),
187
+ dict(path='remote/_main.py', sha1='5ae1dc673ce22f2d40612c66b3a5c3d01ebb6718'),
188
+ dict(path='../../omdev/interp/inject.py', sha1='b039abbadf0b096d2724182af2e0ebda2a230852'),
189
+ dict(path='remote/connection.py', sha1='18bc6c4a446a9bef3c54b861a01418b1b7ba39ff'),
190
+ dict(path='../../omdev/interp/default.py', sha1='a799969a0d3f4b57538587b13ceb08f6334ebc16'),
191
+ dict(path='remote/inject.py', sha1='8713a421b18ff7625c95017616380f0500c3c39c'),
192
+ dict(path='targets/connection.py', sha1='891f1d35ee3814bed32e6de71e1ca47574635da1'),
193
+ dict(path='deploy/interp.py', sha1='89371a87a275fea2e8566a0983e4906cda46a105'),
194
+ dict(path='deploy/venvs.py', sha1='7ad41a11098dd68d83a0804d2ea95779d0be4de0'),
195
+ dict(path='targets/inject.py', sha1='e4bfba31b044da9545d4c00965e7e15b97a40cce'),
196
+ dict(path='deploy/apps.py', sha1='6df5d728b6715583a792d1e92268c0c07502509f'),
197
+ dict(path='deploy/deploy.py', sha1='635f84ad370b22797406d2ae55d78621ea1f7b2b'),
198
+ dict(path='deploy/commands.py', sha1='0e9fdd122fe3a4028efede0862b059c091cc13cb'),
199
+ dict(path='deploy/inject.py', sha1='d84e9c3e980c5a1ec62d18628d8911f5b6bb125f'),
200
+ dict(path='inject.py', sha1='b1c173df021f190d3631f3828470fa1564e0b4b4'),
201
+ dict(path='bootstrap_.py', sha1='96fff62e0152795271c79ea29face58dadab0894'),
202
+ dict(path='main.py', sha1='ddddcdb54aabe67188dce6a5f65e04ce296a75b0'),
203
+ ],
204
+ )
205
+
206
+
68
207
  ########################################
69
208
 
70
209
 
71
210
  # ../../omdev/packaging/versions.py
72
- VersionLocalType = ta.Tuple[ta.Union[int, str], ...]
73
- VersionCmpPrePostDevType = ta.Union['InfinityVersionType', 'NegativeInfinityVersionType', ta.Tuple[str, int]]
74
- _VersionCmpLocalType0 = ta.Tuple[ta.Union[ta.Tuple[int, str], ta.Tuple['NegativeInfinityVersionType', ta.Union[int, str]]], ...] # noqa
75
- VersionCmpLocalType = ta.Union['NegativeInfinityVersionType', _VersionCmpLocalType0]
76
- VersionCmpKey = ta.Tuple[int, ta.Tuple[int, ...], VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpLocalType] # noqa
77
- VersionComparisonMethod = ta.Callable[[VersionCmpKey, VersionCmpKey], bool]
211
+ VersionLocalType = ta.Tuple[ta.Union[int, str], ...] # ta.TypeAlias
212
+ VersionCmpPrePostDevType = ta.Union['InfinityVersionType', 'NegativeInfinityVersionType', ta.Tuple[str, int]] # ta.TypeAlias # noqa
213
+ _VersionCmpLocalType0 = ta.Tuple[ta.Union[ta.Tuple[int, str], ta.Tuple['NegativeInfinityVersionType', ta.Union[int, str]]], ...] # ta.TypeAlias # noqa
214
+ VersionCmpLocalType = ta.Union['NegativeInfinityVersionType', _VersionCmpLocalType0] # ta.TypeAlias
215
+ VersionCmpKey = ta.Tuple[int, ta.Tuple[int, ...], VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpLocalType] # ta.TypeAlias # noqa
216
+ VersionComparisonMethod = ta.Callable[[VersionCmpKey, VersionCmpKey], bool] # ta.TypeAlias
78
217
 
79
218
  # deploy/paths/types.py
80
219
  DeployPathKind = ta.Literal['dir', 'file'] # ta.TypeAlias
81
220
 
82
221
  # ../../omlish/configs/types.py
83
- ConfigMap = ta.Mapping[str, ta.Any]
222
+ ConfigMap = ta.Mapping[str, ta.Any] # ta.TypeAlias
84
223
 
85
224
  # ../../omlish/formats/ini/sections.py
86
225
  IniSectionSettingsMap = ta.Mapping[str, ta.Mapping[str, ta.Union[str, ta.Sequence[str]]]] # ta.TypeAlias
@@ -90,7 +229,7 @@ TomlParseFloat = ta.Callable[[str], ta.Any] # ta.TypeAlias
90
229
  TomlKey = ta.Tuple[str, ...] # ta.TypeAlias
91
230
  TomlPos = int # ta.TypeAlias
92
231
 
93
- # ../../omlish/lite/attrops.py
232
+ # ../../omlish/lite/abstract.py
94
233
  T = ta.TypeVar('T')
95
234
 
96
235
  # ../../omlish/lite/cached.py
@@ -117,9 +256,9 @@ A2 = ta.TypeVar('A2')
117
256
  LogLevel = int # ta.TypeAlias
118
257
 
119
258
  # ../../omdev/packaging/specifiers.py
120
- UnparsedVersion = ta.Union['Version', str]
259
+ UnparsedVersion = ta.Union['Version', str] # ta.TypeAlias
121
260
  UnparsedVersionVar = ta.TypeVar('UnparsedVersionVar', bound=UnparsedVersion)
122
- CallableVersionOperator = ta.Callable[['Version', str], bool]
261
+ CallableVersionOperator = ta.Callable[['Version', str], bool] # ta.TypeAlias
123
262
 
124
263
  # ../../omlish/argparse/cli.py
125
264
  ArgparseCmdFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
@@ -141,7 +280,7 @@ LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
141
280
  LoggingContextInfo = ta.Any # ta.TypeAlias
142
281
 
143
282
  # ../../omlish/os/atomics.py
144
- AtomicPathSwapKind = ta.Literal['dir', 'file']
283
+ AtomicPathSwapKind = ta.Literal['dir', 'file'] # ta.TypeAlias
145
284
  AtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
146
285
 
147
286
  # commands/base.py
@@ -152,10 +291,10 @@ CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
152
291
  AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
153
292
 
154
293
  # ../../omlish/lite/inject.py
155
- InjectorKeyCls = ta.Union[type, ta.NewType]
156
- InjectorProviderFn = ta.Callable[['Injector'], ta.Any]
157
- InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
158
- InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
294
+ InjectorKeyCls = ta.Union[type, ta.NewType] # ta.TypeAlias
295
+ InjectorProviderFn = ta.Callable[['Injector'], ta.Any] # ta.TypeAlias
296
+ InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn'] # ta.TypeAlias
297
+ InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings'] # ta.TypeAlias
159
298
 
160
299
  # ../../omlish/logs/contexts.py
161
300
  LoggingContextInfoT = ta.TypeVar('LoggingContextInfoT', bound=LoggingContextInfo)
@@ -281,12 +420,12 @@ class _BaseVersion:
281
420
 
282
421
  def __lt__(self, other: '_BaseVersion') -> bool:
283
422
  if not isinstance(other, _BaseVersion):
284
- return NotImplemented # type: ignore
423
+ return NotImplemented
285
424
  return self._key < other._key
286
425
 
287
426
  def __le__(self, other: '_BaseVersion') -> bool:
288
427
  if not isinstance(other, _BaseVersion):
289
- return NotImplemented # type: ignore
428
+ return NotImplemented
290
429
  return self._key <= other._key
291
430
 
292
431
  def __eq__(self, other: object) -> bool:
@@ -296,12 +435,12 @@ class _BaseVersion:
296
435
 
297
436
  def __ge__(self, other: '_BaseVersion') -> bool:
298
437
  if not isinstance(other, _BaseVersion):
299
- return NotImplemented # type: ignore
438
+ return NotImplemented
300
439
  return self._key >= other._key
301
440
 
302
441
  def __gt__(self, other: '_BaseVersion') -> bool:
303
442
  if not isinstance(other, _BaseVersion):
304
- return NotImplemented # type: ignore
443
+ return NotImplemented
305
444
  return self._key > other._key
306
445
 
307
446
  def __ne__(self, other: object) -> bool:
@@ -2394,25 +2533,49 @@ def is_abstract_method(obj: ta.Any) -> bool:
2394
2533
  return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
2395
2534
 
2396
2535
 
2397
- def update_abstracts(cls, *, force=False):
2536
+ def compute_abstract_methods(cls: type) -> ta.FrozenSet[str]:
2537
+ # ~> https://github.com/python/cpython/blob/f3476c6507381ca860eec0989f53647b13517423/Modules/_abc.c#L358
2538
+
2539
+ # Stage 1: direct abstract methods
2540
+
2541
+ abstracts = {
2542
+ a
2543
+ # Get items as a list to avoid mutation issues during iteration
2544
+ for a, v in list(cls.__dict__.items())
2545
+ if is_abstract_method(v)
2546
+ }
2547
+
2548
+ # Stage 2: inherited abstract methods
2549
+
2550
+ for base in cls.__bases__:
2551
+ # Get __abstractmethods__ from base if it exists
2552
+ if (base_abstracts := getattr(base, _ABSTRACT_METHODS_ATTR, None)) is None:
2553
+ continue
2554
+
2555
+ # Iterate over abstract methods in base
2556
+ for key in base_abstracts:
2557
+ # Check if this class has an attribute with this name
2558
+ try:
2559
+ value = getattr(cls, key)
2560
+ except AttributeError:
2561
+ # Attribute not found in this class, skip
2562
+ continue
2563
+
2564
+ # Check if it's still abstract
2565
+ if is_abstract_method(value):
2566
+ abstracts.add(key)
2567
+
2568
+ return frozenset(abstracts)
2569
+
2570
+
2571
+ def update_abstracts(cls: ta.Type[T], *, force: bool = False) -> ta.Type[T]:
2398
2572
  if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
2399
2573
  # Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
2400
2574
  # implementation (especially during testing), and we want to handle both cases.
2401
2575
  return cls
2402
2576
 
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))
2577
+ abstracts = compute_abstract_methods(cls)
2578
+ setattr(cls, _ABSTRACT_METHODS_ATTR, abstracts)
2416
2579
  return cls
2417
2580
 
2418
2581
 
@@ -2466,23 +2629,26 @@ class Abstract:
2466
2629
  super().__init_subclass__(**kwargs)
2467
2630
 
2468
2631
  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))
2632
+ if ams := compute_abstract_methods(cls):
2633
+ amd = {
2634
+ a: mcls
2635
+ for mcls in cls.__mro__[::-1]
2636
+ for a in ams
2637
+ if a in mcls.__dict__
2638
+ }
2475
2639
 
2476
- if ams:
2477
2640
  raise AbstractTypeError(
2478
2641
  f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
2479
2642
  ', '.join(sorted([
2480
2643
  '.'.join([
2481
- *([m] if (m := getattr(c, '__module__')) else []),
2482
- getattr(c, '__qualname__', getattr(c, '__name__')),
2644
+ *([
2645
+ *([m] if (m := getattr(c, '__module__')) else []),
2646
+ getattr(c, '__qualname__', getattr(c, '__name__')),
2647
+ ] if c is not None else '?'),
2483
2648
  a,
2484
2649
  ])
2485
- for a, c in ams.items()
2650
+ for a in ams
2651
+ for c in [amd.get(a)]
2486
2652
  ])),
2487
2653
  )
2488
2654
 
@@ -2499,6 +2665,150 @@ class Abstract:
2499
2665
  update_abstracts(cls, force=True)
2500
2666
 
2501
2667
 
2668
+ ########################################
2669
+ # ../../../omlish/lite/asyncs.py
2670
+
2671
+
2672
+ ##
2673
+
2674
+
2675
+ async def opt_await(aw: ta.Optional[ta.Awaitable[T]]) -> ta.Optional[T]:
2676
+ return (await aw if aw is not None else None)
2677
+
2678
+
2679
+ async def async_list(ai: ta.AsyncIterable[T]) -> ta.List[T]:
2680
+ return [v async for v in ai]
2681
+
2682
+
2683
+ async def async_enumerate(ai: ta.AsyncIterable[T]) -> ta.AsyncIterable[ta.Tuple[int, T]]:
2684
+ i = 0
2685
+ async for e in ai:
2686
+ yield (i, e)
2687
+ i += 1
2688
+
2689
+
2690
+ ##
2691
+
2692
+
2693
+ def as_async(fn: ta.Callable[..., T], *, wrap: bool = False) -> ta.Callable[..., ta.Awaitable[T]]:
2694
+ async def inner(*args, **kwargs):
2695
+ return fn(*args, **kwargs)
2696
+
2697
+ return functools.wraps(fn)(inner) if wrap else inner
2698
+
2699
+
2700
+ ##
2701
+
2702
+
2703
+ class SyncAwaitCoroutineNotTerminatedError(Exception):
2704
+ pass
2705
+
2706
+
2707
+ def sync_await(aw: ta.Awaitable[T]) -> T:
2708
+ """
2709
+ Allows for the synchronous execution of async functions which will never actually *externally* await anything. These
2710
+ functions are allowed to await any number of other functions - including contextmanagers and generators - so long as
2711
+ nothing ever actually 'leaks' out of the function, presumably to an event loop.
2712
+ """
2713
+
2714
+ ret = missing = object()
2715
+
2716
+ async def thunk():
2717
+ nonlocal ret
2718
+
2719
+ ret = await aw
2720
+
2721
+ cr = thunk()
2722
+ try:
2723
+ try:
2724
+ cr.send(None)
2725
+ except StopIteration:
2726
+ pass
2727
+
2728
+ if ret is missing or cr.cr_await is not None or cr.cr_running:
2729
+ raise SyncAwaitCoroutineNotTerminatedError('Not terminated')
2730
+
2731
+ finally:
2732
+ cr.close()
2733
+
2734
+ return ta.cast(T, ret)
2735
+
2736
+
2737
+ #
2738
+
2739
+
2740
+ def sync_aiter(ai: ta.AsyncIterator[T]) -> ta.Iterator[T]:
2741
+ while True:
2742
+ try:
2743
+ o = sync_await(ai.__anext__())
2744
+ except StopAsyncIteration:
2745
+ break
2746
+ yield o
2747
+
2748
+
2749
+ def sync_async_list(ai: ta.AsyncIterable[T]) -> ta.List[T]:
2750
+ """
2751
+ Uses `sync_await` to synchronously read the full contents of a function call returning an async iterator, given that
2752
+ the function never externally awaits anything.
2753
+ """
2754
+
2755
+ lst: ta.Optional[ta.List[T]] = None
2756
+
2757
+ async def inner():
2758
+ nonlocal lst
2759
+
2760
+ lst = [v async for v in ai]
2761
+
2762
+ sync_await(inner())
2763
+
2764
+ if not isinstance(lst, list):
2765
+ raise TypeError(lst)
2766
+
2767
+ return lst
2768
+
2769
+
2770
+ #
2771
+
2772
+
2773
+ @ta.final
2774
+ class SyncAwaitContextManager(ta.Generic[T]):
2775
+ def __init__(self, acm: ta.AsyncContextManager[T]) -> None:
2776
+ self._acm = acm
2777
+
2778
+ def __repr__(self) -> str:
2779
+ return f'{self.__class__.__name__}({self._acm!r})'
2780
+
2781
+ def __enter__(self) -> T:
2782
+ return sync_await(self._acm.__aenter__())
2783
+
2784
+ def __exit__(self, exc_type, exc_val, exc_tb):
2785
+ return sync_await(self._acm.__aexit__(exc_type, exc_val, exc_tb))
2786
+
2787
+
2788
+ sync_async_with = SyncAwaitContextManager
2789
+
2790
+
2791
+ ##
2792
+
2793
+
2794
+ @ta.final
2795
+ class SyncToAsyncContextManager(ta.Generic[T]):
2796
+ def __init__(self, cm: ta.ContextManager[T]) -> None:
2797
+ self._cm = cm
2798
+
2799
+ def __repr__(self) -> str:
2800
+ return f'{self.__class__.__name__}({self._cm!r})'
2801
+
2802
+ async def __aenter__(self) -> T:
2803
+ return self._cm.__enter__()
2804
+
2805
+ async def __aexit__(self, exc_type, exc_value, traceback, /):
2806
+ return self._cm.__exit__(exc_type, exc_value, traceback)
2807
+
2808
+
2809
+ as_async_context_manager = SyncToAsyncContextManager
2810
+
2811
+
2502
2812
  ########################################
2503
2813
  # ../../../omlish/lite/attrops.py
2504
2814
  """
@@ -2507,6 +2817,8 @@ TODO:
2507
2817
  - per-attr repr transform / filter
2508
2818
  - __ne__ ? cases where it still matters
2509
2819
  - ordering ?
2820
+ - repr_filter: ta.Union[ta.Callable[[ta.Any], ta.Optional[str]], ta.Literal['not_none', 'truthy']]] ?
2821
+ - unify repr/repr_fn/repr_filter
2510
2822
  """
2511
2823
 
2512
2824
 
@@ -2524,6 +2836,8 @@ class AttrOps(ta.Generic[T]):
2524
2836
  display: ta.Optional[str] = None,
2525
2837
 
2526
2838
  repr: bool = True, # noqa
2839
+ repr_fn: ta.Optional[ta.Callable[[ta.Any], ta.Optional[str]]] = None,
2840
+
2527
2841
  hash: bool = True, # noqa
2528
2842
  eq: bool = True,
2529
2843
  ) -> None:
@@ -2538,6 +2852,8 @@ class AttrOps(ta.Generic[T]):
2538
2852
  self._display = display
2539
2853
 
2540
2854
  self._repr = repr
2855
+ self._repr_fn = repr_fn
2856
+
2541
2857
  self._hash = hash
2542
2858
  self._eq = eq
2543
2859
 
@@ -2545,21 +2861,30 @@ class AttrOps(ta.Generic[T]):
2545
2861
  def of(
2546
2862
  cls,
2547
2863
  o: ta.Union[
2548
- str,
2549
- ta.Tuple[str, str],
2550
2864
  'AttrOps.Attr',
2865
+ str,
2866
+ ta.Tuple[str, ta.Union[str, ta.Mapping[str, ta.Any]]],
2867
+ ta.Mapping[str, ta.Any],
2551
2868
  ],
2552
2869
  ) -> 'AttrOps.Attr':
2553
2870
  if isinstance(o, AttrOps.Attr):
2554
2871
  return o
2555
2872
  elif isinstance(o, str):
2556
2873
  return cls(o)
2874
+ elif isinstance(o, tuple):
2875
+ name, x = o
2876
+ kw: ta.Mapping[str, ta.Any]
2877
+ if isinstance(x, str):
2878
+ kw = dict(display=x)
2879
+ elif isinstance(x, ta.Mapping):
2880
+ kw = x
2881
+ else:
2882
+ raise TypeError(x)
2883
+ return cls(name, **kw)
2884
+ elif isinstance(o, ta.Mapping):
2885
+ return cls(**o)
2557
2886
  else:
2558
- name, disp = o
2559
- return cls(
2560
- name,
2561
- display=disp,
2562
- )
2887
+ raise TypeError(o)
2563
2888
 
2564
2889
  @property
2565
2890
  def name(self) -> str:
@@ -2577,19 +2902,34 @@ class AttrOps(ta.Generic[T]):
2577
2902
  def eq(self) -> bool:
2578
2903
  return self._eq
2579
2904
 
2905
+ @staticmethod
2906
+ def opt_repr(o: ta.Any) -> ta.Optional[str]:
2907
+ return repr(o) if o is not None else None
2908
+
2909
+ @staticmethod
2910
+ def truthy_repr(o: ta.Any) -> ta.Optional[str]:
2911
+ return repr(o) if o else None
2912
+
2913
+ #
2914
+
2580
2915
  @ta.overload
2581
2916
  def __init__(
2582
2917
  self,
2583
2918
  *attrs: ta.Sequence[ta.Union[
2584
2919
  str,
2585
- ta.Tuple[str, str],
2920
+ ta.Tuple[str, ta.Union[str, ta.Mapping[str, ta.Any]]],
2921
+ ta.Mapping[str, ta.Any],
2586
2922
  Attr,
2587
2923
  ]],
2924
+
2588
2925
  with_module: bool = False,
2589
2926
  use_qualname: bool = False,
2590
2927
  with_id: bool = False,
2928
+ terse: bool = False,
2591
2929
  repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = None,
2592
2930
  recursive: bool = False,
2931
+
2932
+ cache_hash: ta.Union[bool, str] = False,
2593
2933
  subtypes_eq: bool = False,
2594
2934
  ) -> None:
2595
2935
  ...
@@ -2599,16 +2939,20 @@ class AttrOps(ta.Generic[T]):
2599
2939
  self,
2600
2940
  attrs_fn: ta.Callable[[T], ta.Tuple[ta.Union[
2601
2941
  ta.Any,
2602
- ta.Tuple[str, ta.Any],
2942
+ ta.Tuple[ta.Any, ta.Union[str, ta.Mapping[str, ta.Any]]],
2603
2943
  Attr,
2604
2944
  ], ...]],
2605
2945
  /,
2606
2946
  *,
2947
+
2607
2948
  with_module: bool = False,
2608
2949
  use_qualname: bool = False,
2609
2950
  with_id: bool = False,
2951
+ terse: bool = False,
2610
2952
  repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = None,
2611
2953
  recursive: bool = False,
2954
+
2955
+ cache_hash: ta.Union[bool, str] = False,
2612
2956
  subtypes_eq: bool = False,
2613
2957
  ) -> None:
2614
2958
  ...
@@ -2616,11 +2960,15 @@ class AttrOps(ta.Generic[T]):
2616
2960
  def __init__(
2617
2961
  self,
2618
2962
  *args,
2963
+
2619
2964
  with_module=False,
2620
2965
  use_qualname=False,
2621
2966
  with_id=False,
2967
+ terse=False,
2622
2968
  repr_filter=None,
2623
2969
  recursive=False,
2970
+
2971
+ cache_hash=False,
2624
2972
  subtypes_eq=False,
2625
2973
  ) -> None:
2626
2974
  if args and len(args) == 1 and callable(args[0]):
@@ -2631,8 +2979,11 @@ class AttrOps(ta.Generic[T]):
2631
2979
  self._with_module: bool = with_module
2632
2980
  self._use_qualname: bool = use_qualname
2633
2981
  self._with_id: bool = with_id
2982
+ self._terse: bool = terse
2634
2983
  self._repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = repr_filter
2635
2984
  self._recursive: bool = recursive
2985
+
2986
+ self._cache_hash: ta.Union[bool, str] = cache_hash
2636
2987
  self._subtypes_eq: bool = subtypes_eq
2637
2988
 
2638
2989
  @property
@@ -2667,20 +3018,27 @@ class AttrOps(ta.Generic[T]):
2667
3018
 
2668
3019
  attrs: ta.List[AttrOps.Attr] = []
2669
3020
  for o in raw:
2670
- if isinstance(o, AttrOps.Attr):
2671
- attrs.append(o)
3021
+ if isinstance(o, (AttrOps.Attr, ta.Mapping)):
3022
+ attrs.append(AttrOps.Attr.of(o))
2672
3023
  continue
2673
3024
 
3025
+ kw: ta.Mapping[str, ta.Any]
2674
3026
  if isinstance(o, tuple):
2675
- disp, cap, = o
3027
+ cap, x = o
3028
+ if isinstance(x, str):
3029
+ kw = dict(display=x)
3030
+ elif isinstance(x, ta.Mapping):
3031
+ kw = x
3032
+ else:
3033
+ raise TypeError(x)
2676
3034
  else:
2677
- disp, cap = None, o
3035
+ cap, kw = o, {}
2678
3036
 
2679
3037
  path = tuple(rec(cap))
2680
3038
 
2681
3039
  attrs.append(AttrOps.Attr(
2682
3040
  '.'.join(path),
2683
- display=disp,
3041
+ **kw,
2684
3042
  ))
2685
3043
 
2686
3044
  return attrs
@@ -2697,19 +3055,27 @@ class AttrOps(ta.Generic[T]):
2697
3055
  pass
2698
3056
 
2699
3057
  def _repr(o: T) -> str:
2700
- vs = ', '.join(
2701
- f'{a._display}={v!r}' # noqa
2702
- for a in self._attrs
2703
- if a._repr # noqa
2704
- for v in [getattr(o, a._name)] # noqa
2705
- if self._repr_filter is None or self._repr_filter(v)
2706
- )
3058
+ vs: ta.List[str] = []
3059
+ for a in self._attrs:
3060
+ if not a._repr: # noqa
3061
+ continue
3062
+ v = getattr(o, a._name) # noqa
3063
+ if self._repr_filter is not None and not self._repr_filter(v):
3064
+ continue
3065
+ if (rfn := a._repr_fn) is None: # noqa
3066
+ rfn = repr
3067
+ if (vr := rfn(v)) is None:
3068
+ continue
3069
+ if self._terse:
3070
+ vs.append(vr)
3071
+ else:
3072
+ vs.append(f'{a._display}={vr}') # noqa
2707
3073
 
2708
3074
  return (
2709
3075
  f'{o.__class__.__module__ + "." if self._with_module else ""}'
2710
3076
  f'{o.__class__.__qualname__ if self._use_qualname else o.__class__.__name__}'
2711
- f'{("@" + hex(id(o))[2:]) if self._with_id else ""}'
2712
- f'({vs})'
3077
+ f'{("@" + hex(id(o))[2:]) if self._with_id else ""}' # noqa
3078
+ f'({", ".join(vs)})'
2713
3079
  )
2714
3080
 
2715
3081
  if self._recursive:
@@ -2734,6 +3100,8 @@ class AttrOps(ta.Generic[T]):
2734
3100
 
2735
3101
  #
2736
3102
 
3103
+ _DEFAULT_CACHED_HASH_ATTR: ta.ClassVar[str] = '__cached_hash__'
3104
+
2737
3105
  _hash: ta.Callable[[T], int]
2738
3106
 
2739
3107
  @property
@@ -2743,13 +3111,33 @@ class AttrOps(ta.Generic[T]):
2743
3111
  except AttributeError:
2744
3112
  pass
2745
3113
 
2746
- def _hash(o: T) -> int:
3114
+ def _calc_hash(o: T) -> int:
2747
3115
  return hash(tuple(
2748
3116
  getattr(o, a._name) # noqa
2749
3117
  for a in self._attrs
2750
3118
  if a._hash # noqa
2751
3119
  ))
2752
3120
 
3121
+ if (ch := self._cache_hash) is not False:
3122
+ if ch is True:
3123
+ cha = self._DEFAULT_CACHED_HASH_ATTR
3124
+ elif isinstance(ch, str):
3125
+ cha = ch
3126
+ else:
3127
+ raise TypeError(ch)
3128
+
3129
+ def _cached_hash(o: T) -> int:
3130
+ try:
3131
+ return object.__getattribute__(o, cha)
3132
+ except AttributeError:
3133
+ object.__setattr__(o, cha, h := _calc_hash(o))
3134
+ return h
3135
+
3136
+ _hash = _cached_hash
3137
+
3138
+ else:
3139
+ _hash = _calc_hash
3140
+
2753
3141
  self._hash = _hash
2754
3142
  return _hash
2755
3143
 
@@ -2890,6 +3278,62 @@ def async_cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
2890
3278
  return _AsyncCachedNullary(fn)
2891
3279
 
2892
3280
 
3281
+ ##
3282
+
3283
+
3284
+ cached_property = functools.cached_property
3285
+
3286
+
3287
+ class _cached_property: # noqa
3288
+ """Backported to pick up https://github.com/python/cpython/commit/056dfc71dce15f81887f0bd6da09d6099d71f979 ."""
3289
+
3290
+ def __init__(self, func):
3291
+ self.func = func
3292
+ self.attrname = None # noqa
3293
+ self.__doc__ = func.__doc__
3294
+ self.__module__ = func.__module__
3295
+
3296
+ _NOT_FOUND = object()
3297
+
3298
+ def __set_name__(self, owner, name):
3299
+ if self.attrname is None:
3300
+ self.attrname = name # noqa
3301
+ elif name != self.attrname:
3302
+ raise TypeError(
3303
+ f'Cannot assign the same cached_property to two different names ({self.attrname!r} and {name!r}).',
3304
+ )
3305
+
3306
+ def __get__(self, instance, owner=None):
3307
+ if instance is None:
3308
+ return self
3309
+ if self.attrname is None:
3310
+ raise TypeError('Cannot use cached_property instance without calling __set_name__ on it.')
3311
+
3312
+ try:
3313
+ cache = instance.__dict__
3314
+ except AttributeError: # not all objects have __dict__ (e.g. class defines slots)
3315
+ raise TypeError(
3316
+ f"No '__dict__' attribute on {type(instance).__name__!r} instance to cache {self.attrname!r} property.",
3317
+ ) from None
3318
+
3319
+ val = cache.get(self.attrname, self._NOT_FOUND)
3320
+
3321
+ if val is self._NOT_FOUND:
3322
+ val = self.func(instance)
3323
+ try:
3324
+ cache[self.attrname] = val
3325
+ except TypeError:
3326
+ raise TypeError(
3327
+ f"The '__dict__' attribute on {type(instance).__name__!r} instance does not support item "
3328
+ f"assignment for caching {self.attrname!r} property.",
3329
+ ) from None
3330
+
3331
+ return val
3332
+
3333
+
3334
+ globals()['cached_property'] = _cached_property
3335
+
3336
+
2893
3337
  ########################################
2894
3338
  # ../../../omlish/lite/check.py
2895
3339
  """
@@ -3429,7 +3873,7 @@ class ExitStacked:
3429
3873
  es.__enter__()
3430
3874
  try:
3431
3875
  self._enter_contexts()
3432
- except Exception: # noqa
3876
+ except BaseException: # noqa
3433
3877
  es.__exit__(*sys.exc_info())
3434
3878
  raise
3435
3879
  return self
@@ -3440,7 +3884,7 @@ class ExitStacked:
3440
3884
  return None
3441
3885
  try:
3442
3886
  self._exit_contexts()
3443
- except Exception: # noqa
3887
+ except BaseException: # noqa
3444
3888
  es.__exit__(*sys.exc_info())
3445
3889
  raise
3446
3890
  return es.__exit__(exc_type, exc_val, exc_tb)
@@ -3488,7 +3932,7 @@ class AsyncExitStacked:
3488
3932
  await es.__aenter__()
3489
3933
  try:
3490
3934
  await self._async_enter_contexts()
3491
- except Exception: # noqa
3935
+ except BaseException: # noqa
3492
3936
  await es.__aexit__(*sys.exc_info())
3493
3937
  raise
3494
3938
  return self
@@ -3499,7 +3943,7 @@ class AsyncExitStacked:
3499
3943
  return None
3500
3944
  try:
3501
3945
  await self._async_exit_contexts()
3502
- except Exception: # noqa
3946
+ except BaseException: # noqa
3503
3947
  await es.__aexit__(*sys.exc_info())
3504
3948
  raise
3505
3949
  return await es.__aexit__(exc_type, exc_val, exc_tb)
@@ -3697,7 +4141,7 @@ def dir_dict(o: ta.Any) -> ta.Dict[str, ta.Any]:
3697
4141
  ##
3698
4142
 
3699
4143
 
3700
- DEFAULT_PYCHARM_VERSION = '242.23726.102'
4144
+ DEFAULT_PYCHARM_VERSION = '252.26199.168'
3701
4145
 
3702
4146
 
3703
4147
  @dc.dataclass(frozen=True)
@@ -3941,6 +4385,12 @@ def format_num_bytes(num_bytes: int) -> str:
3941
4385
 
3942
4386
  ##
3943
4387
  # A workaround for typing deficiencies (like `Argument 2 to NewType(...) must be subclassable`).
4388
+ #
4389
+ # Note that this problem doesn't happen at runtime - it happens in mypy:
4390
+ #
4391
+ # mypy <(echo "import typing as ta; MyCallback = ta.NewType('MyCallback', ta.Callable[[], None])")
4392
+ # /dev/fd/11:1:22: error: Argument 2 to NewType(...) must be subclassable (got "Callable[[], None]") [valid-newtype]
4393
+ #
3944
4394
 
3945
4395
 
3946
4396
  @dc.dataclass(frozen=True)
@@ -3986,6 +4436,39 @@ class Func3(ta.Generic[A0, A1, A2, T]):
3986
4436
  ##
3987
4437
 
3988
4438
 
4439
+ @dc.dataclass(frozen=True)
4440
+ class CachedFunc0(ta.Generic[T]):
4441
+ fn: ta.Callable[[], T]
4442
+
4443
+ def __call__(self) -> T:
4444
+ try:
4445
+ return object.__getattribute__(self, '_value')
4446
+ except AttributeError:
4447
+ pass
4448
+
4449
+ value = self.fn()
4450
+ object.__setattr__(self, '_value', value)
4451
+ return value
4452
+
4453
+
4454
+ @dc.dataclass(frozen=True)
4455
+ class AsyncCachedFunc0(ta.Generic[T]):
4456
+ fn: ta.Callable[[], ta.Awaitable[T]]
4457
+
4458
+ async def __call__(self) -> T:
4459
+ try:
4460
+ return object.__getattribute__(self, '_value')
4461
+ except AttributeError:
4462
+ pass
4463
+
4464
+ value = await self.fn()
4465
+ object.__setattr__(self, '_value', value)
4466
+ return value
4467
+
4468
+
4469
+ ##
4470
+
4471
+
3989
4472
  _TYPING_ANNOTATIONS_ATTR = '__annotate__' if sys.version_info >= (3, 14) else '__annotations__'
3990
4473
 
3991
4474
 
@@ -5483,7 +5966,7 @@ class SpecifierSet(BaseSpecifier):
5483
5966
  if isinstance(other, str):
5484
5967
  other = SpecifierSet(other)
5485
5968
  elif not isinstance(other, SpecifierSet):
5486
- return NotImplemented # type: ignore
5969
+ return NotImplemented
5487
5970
 
5488
5971
  specifier = SpecifierSet()
5489
5972
  specifier._specs = frozenset(self._specs | other._specs)
@@ -5503,6 +5986,7 @@ class SpecifierSet(BaseSpecifier):
5503
5986
  if isinstance(other, (str, Specifier)):
5504
5987
  other = SpecifierSet(str(other))
5505
5988
  elif not isinstance(other, SpecifierSet):
5989
+
5506
5990
  return NotImplemented
5507
5991
 
5508
5992
  return self._specs == other._specs
@@ -5654,7 +6138,7 @@ BEST_PYTHON_SH = """\
5654
6138
  bv=""
5655
6139
  bx=""
5656
6140
 
5657
- for v in "" 3 3.{8..13}; do
6141
+ for v in "" 3 3.{8..14}; do
5658
6142
  x="python$v"
5659
6143
  v=$($x -c "import sys; print((\\"%02d\\" * 3) % sys.version_info[:3])" 2>/dev/null)
5660
6144
  if [ $? -eq 0 ] && [ "$v" \\> 030799 ] && ([ -z "$bv" ] || [ "$v" \\> "$bv" ]); then
@@ -5789,6 +6273,7 @@ TODO:
5789
6273
  - pre-run, post-run hooks
5790
6274
  - exitstack?
5791
6275
  - suggestion - difflib.get_close_matches
6276
+ - add_argument_group - group kw on ArgparseKwarg?
5792
6277
  """
5793
6278
 
5794
6279
 
@@ -5799,6 +6284,7 @@ TODO:
5799
6284
  class ArgparseArg:
5800
6285
  args: ta.Sequence[ta.Any]
5801
6286
  kwargs: ta.Mapping[str, ta.Any]
6287
+ group: ta.Optional[str] = None
5802
6288
  dest: ta.Optional[str] = None
5803
6289
 
5804
6290
  def __get__(self, instance, owner=None):
@@ -5808,7 +6294,11 @@ class ArgparseArg:
5808
6294
 
5809
6295
 
5810
6296
  def argparse_arg(*args, **kwargs) -> ArgparseArg:
5811
- return ArgparseArg(args, kwargs)
6297
+ return ArgparseArg(
6298
+ args=args,
6299
+ group=kwargs.pop('group', None),
6300
+ kwargs=kwargs,
6301
+ )
5812
6302
 
5813
6303
 
5814
6304
  def argparse_arg_(*args, **kwargs) -> ta.Any:
@@ -5978,6 +6468,10 @@ class ArgparseCli:
5978
6468
  subparser.set_defaults(_cmd=obj)
5979
6469
 
5980
6470
  elif isinstance(obj, ArgparseArg):
6471
+ if obj.group is not None:
6472
+ # FIXME: add_argument_group
6473
+ raise NotImplementedError
6474
+
5981
6475
  if att in anns:
5982
6476
  ann_kwargs = _get_argparse_arg_ann_kwargs(anns[att])
5983
6477
  obj.kwargs = {**ann_kwargs, **obj.kwargs}
@@ -6023,7 +6517,7 @@ class ArgparseCli:
6023
6517
 
6024
6518
  if self._unknown_args and not (cmd is not None and cmd.accepts_unknown):
6025
6519
  msg = f'unrecognized arguments: {" ".join(self._unknown_args)}'
6026
- if (parser := self.get_parser()).exit_on_error:
6520
+ if (parser := self.get_parser()).exit_on_error: # noqa
6027
6521
  parser.error(msg)
6028
6522
  else:
6029
6523
  raise argparse.ArgumentError(None, msg)
@@ -6043,7 +6537,10 @@ class ArgparseCli:
6043
6537
  return fn()
6044
6538
 
6045
6539
  def cli_run_and_exit(self) -> ta.NoReturn:
6046
- sys.exit(rc if isinstance(rc := self.cli_run(), int) else 0)
6540
+ rc = self.cli_run()
6541
+ if not isinstance(rc, int):
6542
+ rc = 0
6543
+ raise SystemExit(rc)
6047
6544
 
6048
6545
  def __call__(self, *, exit: bool = False) -> ta.Optional[int]: # noqa
6049
6546
  if exit:
@@ -7174,6 +7671,13 @@ class Maybe(ta.Generic[T]):
7174
7671
  else:
7175
7672
  return other
7176
7673
 
7674
+ @ta.final
7675
+ def or_none(self) -> ta.Optional[T]:
7676
+ if self.present:
7677
+ return self.must()
7678
+ else:
7679
+ return None
7680
+
7177
7681
  @ta.final
7178
7682
  def or_else_get(self, supplier: ta.Callable[[], ta.Union[T, U]]) -> ta.Union[T, U]:
7179
7683
  if self.present:
@@ -7228,8 +7732,6 @@ class _JustMaybe(_Maybe[T]):
7228
7732
  __slots__ = ('_v', '_hash')
7229
7733
 
7230
7734
  def __init__(self, v: T) -> None:
7231
- super().__init__()
7232
-
7233
7735
  self._v = v
7234
7736
 
7235
7737
  @property
@@ -7287,6 +7789,13 @@ class _EmptyMaybe(_Maybe[T]):
7287
7789
  Maybe._empty = _EmptyMaybe() # noqa
7288
7790
 
7289
7791
 
7792
+ ##
7793
+
7794
+
7795
+ setattr(Maybe, 'just', _JustMaybe) # noqa
7796
+ setattr(Maybe, 'empty', functools.partial(operator.attrgetter('_empty'), Maybe))
7797
+
7798
+
7290
7799
  ########################################
7291
7800
  # ../../../omlish/lite/runtime.py
7292
7801
 
@@ -8289,9 +8798,10 @@ class InterpSpecifier:
8289
8798
  def parse(cls, s: str) -> 'InterpSpecifier':
8290
8799
  s, o = InterpOpts.parse_suffix(s)
8291
8800
  if not any(s.startswith(o) for o in Specifier.OPERATORS):
8292
- s = '~=' + s
8293
8801
  if s.count('.') < 2:
8294
- s += '.0'
8802
+ s = '~=' + s + '.0'
8803
+ else:
8804
+ s = '==' + s
8295
8805
  return cls(
8296
8806
  specifier=Specifier(s),
8297
8807
  opts=o,
@@ -10190,6 +10700,9 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
10190
10700
  self._infos[type(info)] = info
10191
10701
  return self
10192
10702
 
10703
+ def get_infos(self) -> ta.Mapping[ta.Type[LoggingContextInfo], LoggingContextInfo]:
10704
+ return self._infos
10705
+
10193
10706
  def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
10194
10707
  return self._infos.get(ty)
10195
10708
 
@@ -10212,7 +10725,7 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
10212
10725
  _stack_offset: int
10213
10726
  _stack_info: bool
10214
10727
 
10215
- def inc_stack_offset(self, ofs: int = 1) -> 'CaptureLoggingContext':
10728
+ def inc_stack_offset(self, ofs: int = 1) -> 'CaptureLoggingContextImpl':
10216
10729
  if hasattr(self, '_stack_offset'):
10217
10730
  self._stack_offset += ofs
10218
10731
  return self
@@ -10244,10 +10757,9 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
10244
10757
 
10245
10758
 
10246
10759
  ########################################
10247
- # ../../../omlish/logs/standard.py
10760
+ # ../../../omlish/logs/std/standard.py
10248
10761
  """
10249
10762
  TODO:
10250
- - !! move to std !!
10251
10763
  - structured
10252
10764
  - prefixed
10253
10765
  - debug
@@ -11022,6 +11534,11 @@ class AnyLogger(Abstract, ta.Generic[T]):
11022
11534
 
11023
11535
  ##
11024
11536
 
11537
+ # This will be 1 for [Sync]Logger and 0 for AsyncLogger - in sync loggers these methods remain present on the stack,
11538
+ # in async loggers they return a coroutine to be awaited and thus aren't actually present when said coroutine is
11539
+ # awaited.
11540
+ _level_proxy_method_stack_offset: int
11541
+
11025
11542
  @ta.overload
11026
11543
  def log(self, level: LogLevel, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
11027
11544
  ...
@@ -11036,7 +11553,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
11036
11553
 
11037
11554
  @ta.final
11038
11555
  def log(self, level: LogLevel, *args, **kwargs):
11039
- return self._log(CaptureLoggingContextImpl(level, stack_offset=1), *args, **kwargs)
11556
+ return self._log(
11557
+ CaptureLoggingContextImpl(
11558
+ level,
11559
+ stack_offset=self._level_proxy_method_stack_offset,
11560
+ ),
11561
+ *args,
11562
+ **kwargs,
11563
+ )
11040
11564
 
11041
11565
  #
11042
11566
 
@@ -11054,7 +11578,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
11054
11578
 
11055
11579
  @ta.final
11056
11580
  def debug(self, *args, **kwargs):
11057
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.DEBUG, stack_offset=1), *args, **kwargs)
11581
+ return self._log(
11582
+ CaptureLoggingContextImpl(
11583
+ NamedLogLevel.DEBUG,
11584
+ stack_offset=self._level_proxy_method_stack_offset,
11585
+ ),
11586
+ *args,
11587
+ **kwargs,
11588
+ )
11058
11589
 
11059
11590
  #
11060
11591
 
@@ -11072,7 +11603,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
11072
11603
 
11073
11604
  @ta.final
11074
11605
  def info(self, *args, **kwargs):
11075
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.INFO, stack_offset=1), *args, **kwargs)
11606
+ return self._log(
11607
+ CaptureLoggingContextImpl(
11608
+ NamedLogLevel.INFO,
11609
+ stack_offset=self._level_proxy_method_stack_offset,
11610
+ ),
11611
+ *args,
11612
+ **kwargs,
11613
+ )
11076
11614
 
11077
11615
  #
11078
11616
 
@@ -11090,7 +11628,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
11090
11628
 
11091
11629
  @ta.final
11092
11630
  def warning(self, *args, **kwargs):
11093
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.WARNING, stack_offset=1), *args, **kwargs)
11631
+ return self._log(
11632
+ CaptureLoggingContextImpl(
11633
+ NamedLogLevel.WARNING,
11634
+ stack_offset=self._level_proxy_method_stack_offset,
11635
+ ),
11636
+ *args,
11637
+ **kwargs,
11638
+ )
11094
11639
 
11095
11640
  #
11096
11641
 
@@ -11108,7 +11653,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
11108
11653
 
11109
11654
  @ta.final
11110
11655
  def error(self, *args, **kwargs):
11111
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.ERROR, stack_offset=1), *args, **kwargs)
11656
+ return self._log(
11657
+ CaptureLoggingContextImpl(
11658
+ NamedLogLevel.ERROR,
11659
+ stack_offset=self._level_proxy_method_stack_offset,
11660
+ ),
11661
+ *args,
11662
+ **kwargs,
11663
+ )
11112
11664
 
11113
11665
  #
11114
11666
 
@@ -11126,7 +11678,15 @@ class AnyLogger(Abstract, ta.Generic[T]):
11126
11678
 
11127
11679
  @ta.final
11128
11680
  def exception(self, *args, exc_info: LoggingExcInfoArg = True, **kwargs):
11129
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.ERROR, exc_info=exc_info, stack_offset=1), *args, **kwargs) # noqa
11681
+ return self._log(
11682
+ CaptureLoggingContextImpl(
11683
+ NamedLogLevel.ERROR,
11684
+ exc_info=exc_info,
11685
+ stack_offset=self._level_proxy_method_stack_offset,
11686
+ ),
11687
+ *args,
11688
+ **kwargs,
11689
+ )
11130
11690
 
11131
11691
  #
11132
11692
 
@@ -11144,24 +11704,53 @@ class AnyLogger(Abstract, ta.Generic[T]):
11144
11704
 
11145
11705
  @ta.final
11146
11706
  def critical(self, *args, **kwargs):
11147
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.CRITICAL, stack_offset=1), *args, **kwargs)
11707
+ return self._log(
11708
+ CaptureLoggingContextImpl(
11709
+ NamedLogLevel.CRITICAL,
11710
+ stack_offset=self._level_proxy_method_stack_offset,
11711
+ ),
11712
+ *args,
11713
+ **kwargs,
11714
+ )
11148
11715
 
11149
11716
  ##
11150
11717
 
11151
11718
  @abc.abstractmethod
11152
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> T: # noqa
11719
+ def _log(
11720
+ self,
11721
+ ctx: CaptureLoggingContext,
11722
+ msg: ta.Union[str, tuple, LoggingMsgFn],
11723
+ *args: ta.Any,
11724
+ **kwargs: ta.Any,
11725
+ ) -> T:
11153
11726
  raise NotImplementedError
11154
11727
 
11155
11728
 
11156
11729
  class Logger(AnyLogger[None], Abstract):
11730
+ _level_proxy_method_stack_offset: int = 1
11731
+
11157
11732
  @abc.abstractmethod
11158
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
11733
+ def _log(
11734
+ self,
11735
+ ctx: CaptureLoggingContext,
11736
+ msg: ta.Union[str, tuple, LoggingMsgFn],
11737
+ *args: ta.Any,
11738
+ **kwargs: ta.Any,
11739
+ ) -> None:
11159
11740
  raise NotImplementedError
11160
11741
 
11161
11742
 
11162
11743
  class AsyncLogger(AnyLogger[ta.Awaitable[None]], Abstract):
11744
+ _level_proxy_method_stack_offset: int = 0
11745
+
11163
11746
  @abc.abstractmethod
11164
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> ta.Awaitable[None]: # noqa
11747
+ def _log(
11748
+ self,
11749
+ ctx: CaptureLoggingContext,
11750
+ msg: ta.Union[str, tuple, LoggingMsgFn],
11751
+ *args: ta.Any,
11752
+ **kwargs: ta.Any,
11753
+ ) -> ta.Awaitable[None]:
11165
11754
  raise NotImplementedError
11166
11755
 
11167
11756
 
@@ -11176,13 +11765,25 @@ class AnyNopLogger(AnyLogger[T], Abstract):
11176
11765
 
11177
11766
  @ta.final
11178
11767
  class NopLogger(AnyNopLogger[None], Logger):
11179
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
11768
+ def _log(
11769
+ self,
11770
+ ctx: CaptureLoggingContext,
11771
+ msg: ta.Union[str, tuple, LoggingMsgFn],
11772
+ *args: ta.Any,
11773
+ **kwargs: ta.Any,
11774
+ ) -> None:
11180
11775
  pass
11181
11776
 
11182
11777
 
11183
11778
  @ta.final
11184
11779
  class AsyncNopLogger(AnyNopLogger[ta.Awaitable[None]], AsyncLogger):
11185
- async def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
11780
+ async def _log(
11781
+ self,
11782
+ ctx: CaptureLoggingContext,
11783
+ msg: ta.Union[str, tuple, LoggingMsgFn],
11784
+ *args: ta.Any,
11785
+ **kwargs: ta.Any,
11786
+ ) -> None:
11186
11787
  pass
11187
11788
 
11188
11789
 
@@ -11889,6 +12490,10 @@ class VerboseCalledProcessError(subprocess.CalledProcessError):
11889
12490
  class BaseSubprocesses(Abstract):
11890
12491
  DEFAULT_LOGGER: ta.ClassVar[ta.Optional[LoggerLike]] = None
11891
12492
 
12493
+ PIPE: ta.ClassVar[int] = subprocess.PIPE
12494
+ STDOUT: ta.ClassVar[int] = subprocess.STDOUT
12495
+ DEVNULL: ta.ClassVar[int] = subprocess.DEVNULL
12496
+
11892
12497
  def __init__(
11893
12498
  self,
11894
12499
  *,
@@ -12456,6 +13061,70 @@ class SingleDirDeployPathOwner(DeployPathOwner, Abstract):
12456
13061
  return self._owned_deploy_paths
12457
13062
 
12458
13063
 
13064
+ ########################################
13065
+ # ../../../omlish/logs/asyncs.py
13066
+
13067
+
13068
+ ##
13069
+
13070
+
13071
+ class AsyncLoggerToLogger(Logger):
13072
+ def __init__(self, u: AsyncLogger) -> None:
13073
+ super().__init__()
13074
+
13075
+ self._u = u
13076
+
13077
+ def get_effective_level(self) -> LogLevel:
13078
+ return self._u.get_effective_level()
13079
+
13080
+ def _log(
13081
+ self,
13082
+ ctx: CaptureLoggingContext,
13083
+ msg: ta.Union[str, tuple, LoggingMsgFn],
13084
+ *args: ta.Any,
13085
+ **kwargs: ta.Any,
13086
+ ) -> None:
13087
+ # Nope out early to avoid sync_await if possible - don't bother in the LoggerToAsyncLogger.
13088
+ if not self.is_enabled_for(ctx.must_get_info(LoggingContextInfos.Level).level):
13089
+ return
13090
+
13091
+ # Note: we hardcode the stack offset of sync_await (which is 2 - sync_await + sync_await.thunk). In non-lite
13092
+ # code, lang.sync_await uses a cext if present to avoid being on the py stack, which would obviously complicate
13093
+ # this, but this is lite code so we will always have the non-c version.
13094
+ sync_await(
13095
+ self._u._log( # noqa
13096
+ check.isinstance(ctx, CaptureLoggingContextImpl).inc_stack_offset(3),
13097
+ msg,
13098
+ *args,
13099
+ **kwargs,
13100
+ ),
13101
+ )
13102
+
13103
+
13104
+ class LoggerToAsyncLogger(AsyncLogger):
13105
+ def __init__(self, u: Logger) -> None:
13106
+ super().__init__()
13107
+
13108
+ self._u = u
13109
+
13110
+ def get_effective_level(self) -> LogLevel:
13111
+ return self._u.get_effective_level()
13112
+
13113
+ async def _log(
13114
+ self,
13115
+ ctx: CaptureLoggingContext,
13116
+ msg: ta.Union[str, tuple, LoggingMsgFn],
13117
+ *args: ta.Any,
13118
+ **kwargs: ta.Any,
13119
+ ) -> None:
13120
+ return self._u._log( # noqa
13121
+ check.isinstance(ctx, CaptureLoggingContextImpl).inc_stack_offset(),
13122
+ msg,
13123
+ *args,
13124
+ **kwargs,
13125
+ )
13126
+
13127
+
12459
13128
  ########################################
12460
13129
  # ../../../omlish/logs/std/loggers.py
12461
13130
 
@@ -12479,7 +13148,12 @@ class StdLogger(Logger):
12479
13148
  def get_effective_level(self) -> LogLevel:
12480
13149
  return self._std.getEffectiveLevel()
12481
13150
 
12482
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> None:
13151
+ def _log(
13152
+ self,
13153
+ ctx: CaptureLoggingContext,
13154
+ msg: ta.Union[str, tuple, LoggingMsgFn],
13155
+ *args: ta.Any,
13156
+ ) -> None:
12483
13157
  if not self.is_enabled_for(ctx.must_get_info(LoggingContextInfos.Level).level):
12484
13158
  return
12485
13159
 
@@ -13122,8 +13796,23 @@ asyncio_subprocesses = AsyncioSubprocesses()
13122
13796
  ##
13123
13797
 
13124
13798
 
13799
+ def _get_module_std_logger(mod_globals: ta.Mapping[str, ta.Any]) -> logging.Logger:
13800
+ return logging.getLogger(mod_globals.get('__name__'))
13801
+
13802
+
13125
13803
  def get_module_logger(mod_globals: ta.Mapping[str, ta.Any]) -> Logger:
13126
- return StdLogger(logging.getLogger(mod_globals.get('__name__'))) # noqa
13804
+ return StdLogger(_get_module_std_logger(mod_globals))
13805
+
13806
+
13807
+ def get_module_async_logger(mod_globals: ta.Mapping[str, ta.Any]) -> AsyncLogger:
13808
+ return LoggerToAsyncLogger(get_module_logger(mod_globals))
13809
+
13810
+
13811
+ def get_module_loggers(mod_globals: ta.Mapping[str, ta.Any]) -> ta.Tuple[Logger, AsyncLogger]:
13812
+ return (
13813
+ log := get_module_logger(mod_globals),
13814
+ LoggerToAsyncLogger(log),
13815
+ )
13127
13816
 
13128
13817
 
13129
13818
  ########################################
@@ -14951,12 +15640,42 @@ uv run pip
14951
15640
  uv run --python 3.11.6 pip
14952
15641
  uv venv --python 3.11.6 --seed barf
14953
15642
  python3 -m venv barf && barf/bin/pip install uv && barf/bin/uv venv --python 3.11.6 --seed barf2
15643
+ uv python find '3.13.10'
15644
+ uv python list --output-format=json
14954
15645
  """
14955
15646
 
14956
15647
 
14957
15648
  ##
14958
15649
 
14959
15650
 
15651
+ @dc.dataclass(frozen=True)
15652
+ class UvPythonListOutput:
15653
+ key: str
15654
+ version: str
15655
+
15656
+ @dc.dataclass(frozen=True)
15657
+ class VersionParts:
15658
+ major: int
15659
+ minor: int
15660
+ patch: int
15661
+
15662
+ version_parts: VersionParts
15663
+
15664
+ path: ta.Optional[str]
15665
+ symlink: ta.Optional[str]
15666
+
15667
+ url: str
15668
+
15669
+ os: str # emscripten linux macos
15670
+ variant: str # default freethreaded
15671
+ implementation: str # cpython graalpy pyodide pypy
15672
+ arch: str # aarch64 wasm32 x86_64
15673
+ libc: str # gnu musl none
15674
+
15675
+
15676
+ ##
15677
+
15678
+
14960
15679
  class UvInterpProvider(InterpProvider):
14961
15680
  def __init__(
14962
15681
  self,
@@ -14977,6 +15696,12 @@ class UvInterpProvider(InterpProvider):
14977
15696
  async def get_installed_version(self, version: InterpVersion) -> Interp:
14978
15697
  raise NotImplementedError
14979
15698
 
15699
+ # async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
15700
+ # return []
15701
+
15702
+ # async def install_version(self, version: InterpVersion) -> Interp:
15703
+ # raise TypeError
15704
+
14980
15705
 
14981
15706
  ########################################
14982
15707
  # ../commands/inject.py
@@ -16826,7 +17551,7 @@ class MainCli(ArgparseCli):
16826
17551
 
16827
17552
 
16828
17553
  def _main() -> None:
16829
- sys.exit(asyncio.run(MainCli().async_cli_run()))
17554
+ raise SystemExit(asyncio.run(MainCli().async_cli_run()))
16830
17555
 
16831
17556
 
16832
17557
  if __name__ == '__main__':