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
@@ -65,6 +65,7 @@ import io
65
65
  import itertools
66
66
  import json
67
67
  import logging
68
+ import operator
68
69
  import os
69
70
  import os.path
70
71
  import pwd
@@ -98,11 +99,98 @@ if sys.version_info < (3, 8):
98
99
  raise OSError(f'Requires python (3, 8), got {sys.version_info} from {sys.executable}') # noqa
99
100
 
100
101
 
102
+ def __omlish_amalg__(): # noqa
103
+ return dict(
104
+ src_files=[
105
+ dict(path='errors.py', sha1='eed49133c64621fb5f081ba7f249e8c4c8745025'),
106
+ dict(path='privileges.py', sha1='80fffb4966565e70b6300381800ff849616a1daa'),
107
+ dict(path='states.py', sha1='7e80da5abde756a47bb01fff1967e35ee9f754e5'),
108
+ dict(path='utils/diag.py', sha1='65f6491a57b3b8ff6dc166c4136c39e49e008c8d'),
109
+ dict(path='utils/fs.py', sha1='f18fd3d60c863e05d91c8e4735b86629334f5181'),
110
+ dict(path='utils/ostypes.py', sha1='81aa9dc830189ae7095c2b8c823e28ce4a808e8d'),
111
+ dict(path='utils/signals.py', sha1='445bab01dcd0144194f330e55accee1277992626'),
112
+ dict(path='utils/strings.py', sha1='c4ced4877e366a64b7d366353ab9e5691c587f38'),
113
+ dict(path='../../omlish/configs/types.py', sha1='f7a5584cd6eccb77d18d729796072a162e9a8790'),
114
+ dict(path='../../omlish/formats/ini/sections.py', sha1='731c92cce82e183d1d4bdc23fc781fad62187394'),
115
+ dict(path='../../omlish/formats/toml/parser.py', sha1='73dac82289350ab951c4bcdbfe61167fa221f26f'),
116
+ dict(path='../../omlish/formats/toml/writer.py', sha1='6ea41d7e724bb1dcf6bd84b88993ff4e8798e021'),
117
+ dict(path='../../omlish/http/versions.py', sha1='197685ffbb62a457a0e8d4047a9df26aebd7dae4'),
118
+ dict(path='../../omlish/io/readers.py', sha1='4b19ab4a87f2fa2a6f6c3cad7e1f3892b7cbd3a4'),
119
+ dict(path='../../omlish/lite/abstract.py', sha1='a2fc3f3697fa8de5247761e9d554e70176f37aac'),
120
+ dict(path='../../omlish/lite/asyncs.py', sha1='b3f2251c56617ce548abf9c333ac996b63edb23e'),
121
+ dict(path='../../omlish/lite/attrops.py', sha1='c1ebfb8573d766d34593c452a2377208d02726dc'),
122
+ dict(path='../../omlish/lite/cached.py', sha1='0c33cf961ac8f0727284303c7a30c5ea98f714f2'),
123
+ dict(path='../../omlish/lite/check.py', sha1='bb6b6b63333699b84462951a854d99ae83195b94'),
124
+ dict(path='../../omlish/lite/json.py', sha1='57eeddc4d23a17931e00284ffa5cb6e3ce089486'),
125
+ dict(path='../../omlish/lite/objects.py', sha1='9566bbf3530fd71fcc56321485216b592fae21e9'),
126
+ dict(path='../../omlish/lite/reflect.py', sha1='c4fec44bf144e9d93293c996af06f6c65fc5e63d'),
127
+ dict(path='../../omlish/lite/strings.py', sha1='89831ecbc34ad80e118a865eceb390ed399dc4d6'),
128
+ dict(path='../../omlish/lite/typing.py', sha1='048bb5fb8ecad5be101516f8f3b7996707f5bc42'),
129
+ dict(path='../../omlish/logs/levels.py', sha1='91405563d082a5eba874da82aac89d83ce7b6152'),
130
+ dict(path='../../omlish/logs/std/filters.py', sha1='f36aab646d84d31e295b33aaaaa6f8b67ff38b3d'),
131
+ dict(path='../../omlish/logs/std/proxy.py', sha1='3e7301a2aa351127f9c85f61b2f85dcc3f15aafb'),
132
+ dict(path='../../omlish/logs/warnings.py', sha1='c4eb694b24773351107fcc058f3620f1dbfb6799'),
133
+ dict(path='../../omlish/sockets/addresses.py', sha1='26533e88a8073f89646c0f77f1fbe0869282ab0e'),
134
+ dict(path='events.py', sha1='d30d903b7d664f76e738ed939b7ec0e6e6861a0a'),
135
+ dict(path='utils/collections.py', sha1='f9c3c8a52e6057e938730746eaa28e48a5b757c6'),
136
+ dict(path='utils/fds.py', sha1='cf9b2a52cc74b2aaebed656ba16888e4322746ec'),
137
+ dict(path='utils/users.py', sha1='d440d9deb2f03b4611bc0eb0ad186f9a994d84f7'),
138
+ dict(path='../../omlish/configs/formats.py', sha1='9bc4f953b4b8700f6f109e6f49e2d70f8e48ce7c'),
139
+ dict(path='../../omlish/configs/processing/names.py', sha1='3ae4c9e921929eb64cee6150cc86f35fee0f2070'),
140
+ dict(path='../../omlish/http/coro/io.py', sha1='2cdf6529c37a37cc0c1db2e02032157cf906d5d6'),
141
+ dict(path='../../omlish/http/parsing.py', sha1='3fea28dc6341908ba7c8fad42bf7bbe711f21b82'),
142
+ dict(path='../../omlish/io/buffers.py', sha1='45a5f79c6d71f02ab82082a48d63ebbd10959031'),
143
+ dict(path='../../omlish/io/fdio/handlers.py', sha1='e81356d4d73a670c35a972476a6338d0b737662b'),
144
+ dict(path='../../omlish/io/fdio/pollers.py', sha1='022d5a8a24412764864ca95186a167698b739baf'),
145
+ dict(path='../../omlish/lite/marshal.py', sha1='96348f5f2a26dc27d842d33cc3927e9da163436b'),
146
+ dict(path='../../omlish/lite/maybes.py', sha1='04d2fcbea17028a5e6b8e7a7fb742375495ed233'),
147
+ dict(path='../../omlish/lite/runtime.py', sha1='2e752a27ae2bf89b1bb79b4a2da522a3ec360c70'),
148
+ dict(path='../../omlish/logs/infos.py', sha1='4dd104bd468a8c438601dd0bbda619b47d2f1620'),
149
+ dict(path='../../omlish/logs/protocols.py', sha1='05ca4d1d7feb50c4e3b9f22ee371aa7bf4b3dbd1'),
150
+ dict(path='../../omlish/logs/std/json.py', sha1='2a75553131e4d5331bb0cedde42aa183f403fc3b'),
151
+ dict(path='../../omlish/os/journald.py', sha1='7485cad562f8b9b4f71efd41a6177660f7d62e55'),
152
+ dict(path='configs.py', sha1='61f986fc5c9591194f72c3b4ffa4b018770710ed'),
153
+ dict(path='pipes.py', sha1='ad9315c50bffe81ee204227163d85ab366ce5320'),
154
+ dict(path='setup.py', sha1='4be12354bb45cf7773fd98ad9695aa330ae07fe6'),
155
+ dict(path='utils/os.py', sha1='9f7314f1c0c34a8154e9acf38a5b916b2e310b4d'),
156
+ dict(path='../../omlish/http/handlers.py', sha1='40629060bac22ea5e94b720b57001861a4ec9031'),
157
+ dict(path='../../omlish/io/fdio/kqueue.py', sha1='c90ba13e9e5ee795b6af752a6f25f8bcfd7f88a0'),
158
+ dict(path='../../omlish/lite/configs.py', sha1='c8602e0e197ef1133e7e8e248935ac745bfd46cb'),
159
+ dict(path='../../omlish/lite/inject.py', sha1='6f097e3170019a34ff6834d36fcc9cbeed3a7ab4'),
160
+ dict(path='../../omlish/logs/contexts.py', sha1='1000a6d5ddfb642865ca532e34b1d50759781cf0'),
161
+ dict(path='../../omlish/logs/std/standard.py', sha1='5c97c1b9f7ead58d6127d047b873398f708f288d'),
162
+ dict(path='types.py', sha1='7ef67f710fb54c3af067aa596cb593f33eafe380'),
163
+ dict(path='../../omlish/http/coro/server/server.py', sha1='c0a980afa8346dbc20570acddb2b3b579bfc1ce0'),
164
+ dict(path='../../omlish/logs/base.py', sha1='8d06faee05fead6b1dd98c9035a5b042af4aebb1'),
165
+ dict(path='../../omlish/logs/std/records.py', sha1='8bbf6ef9eccb3a012c6ca416ddf3969450fd8fc9'),
166
+ dict(path='dispatchers.py', sha1='33fe5ae77e33b3cfabb97b1a1c0f06dd0cc54703'),
167
+ dict(path='groupsimpl.py', sha1='4fe587a6eaff7dd874b54450be62f9689283d230'),
168
+ dict(path='process.py', sha1='ec0903adbde7552ba8a6aad9030716ef57fc4a6c'),
169
+ dict(path='../../omlish/http/coro/server/fdio.py', sha1='3f1b4865e589a336f942a763dc11ce42fa5c8857'),
170
+ dict(path='../../omlish/logs/asyncs.py', sha1='ab11b70033d9f2e9a4e70254185aa1c6130c6077'),
171
+ dict(path='../../omlish/logs/std/loggers.py', sha1='a569179445d6a8a942b5dcfad1d1f77702868803'),
172
+ dict(path='groups.py', sha1='a02a602d28793e5c84fbe7bfbcfa6ccce2ee0788'),
173
+ dict(path='spawning.py', sha1='9e65e562395ad04e3f3a314f946b7a4e58a601da'),
174
+ dict(path='../../omlish/logs/modules.py', sha1='dd7d5f8e63fe8829dfb49460f3929ab64b68ee14'),
175
+ dict(path='dispatchersimpl.py', sha1='701947899daef9f68c4277495594031cf73d9a62'),
176
+ dict(path='http.py', sha1='6a144e4c93abefc5f9cdba207e807ea75f8f2d5d'),
177
+ dict(path='io.py', sha1='6ba708a8396c212afdd1d314c9b5804c2d66646e'),
178
+ dict(path='processimpl.py', sha1='7edbbcd39a8ed1fd195c760da894620617a9d969'),
179
+ dict(path='setupimpl.py', sha1='b4b8b8c3e1d71a0e6794fb0a845181f3662a6bfd'),
180
+ dict(path='signals.py', sha1='645361d922557b5cedddbd261b3f1485b96555dd'),
181
+ dict(path='spawningimpl.py', sha1='c770e0017c2388fe59897d12fe67c3b6b7b2ca5a'),
182
+ dict(path='supervisor.py', sha1='a97a13ec71deaf6eacabb1527f373b21b89209af'),
183
+ dict(path='inject.py', sha1='6ad254bcf1c78e0b8a1d7bb3940628857e3bb60c'),
184
+ dict(path='main.py', sha1='8bd55a46b4a4fc4ad0034205384b0b49b8374c7a'),
185
+ ],
186
+ )
187
+
188
+
101
189
  ########################################
102
190
 
103
191
 
104
192
  # ../../omlish/configs/types.py
105
- ConfigMap = ta.Mapping[str, ta.Any]
193
+ ConfigMap = ta.Mapping[str, ta.Any] # ta.TypeAlias
106
194
 
107
195
  # ../../omlish/formats/ini/sections.py
108
196
  IniSectionSettingsMap = ta.Mapping[str, ta.Mapping[str, ta.Union[str, ta.Sequence[str]]]] # ta.TypeAlias
@@ -112,7 +200,7 @@ TomlParseFloat = ta.Callable[[str], ta.Any] # ta.TypeAlias
112
200
  TomlKey = ta.Tuple[str, ...] # ta.TypeAlias
113
201
  TomlPos = int # ta.TypeAlias
114
202
 
115
- # ../../omlish/lite/attrops.py
203
+ # ../../omlish/lite/abstract.py
116
204
  T = ta.TypeVar('T')
117
205
 
118
206
  # ../../omlish/lite/cached.py
@@ -135,7 +223,7 @@ A2 = ta.TypeVar('A2')
135
223
  LogLevel = int # ta.TypeAlias
136
224
 
137
225
  # ../../omlish/sockets/addresses.py
138
- SocketAddress = ta.Any
226
+ SocketAddress = ta.Any # ta.TypeAlias
139
227
 
140
228
  # events.py
141
229
  EventCallback = ta.Callable[['Event'], None] # ta.TypeAlias
@@ -166,16 +254,16 @@ HttpHandler = ta.Callable[['HttpHandlerRequest'], 'HttpHandlerResponse'] # ta.T
166
254
  HttpHandlerResponseData = ta.Union[bytes, 'HttpHandlerResponseStreamedData'] # ta.TypeAlias # noqa
167
255
 
168
256
  # ../../omlish/lite/inject.py
169
- InjectorKeyCls = ta.Union[type, ta.NewType]
170
- InjectorProviderFn = ta.Callable[['Injector'], ta.Any]
171
- InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
172
- InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
257
+ InjectorKeyCls = ta.Union[type, ta.NewType] # ta.TypeAlias
258
+ InjectorProviderFn = ta.Callable[['Injector'], ta.Any] # ta.TypeAlias
259
+ InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn'] # ta.TypeAlias
260
+ InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings'] # ta.TypeAlias
173
261
 
174
262
  # ../../omlish/logs/contexts.py
175
263
  LoggingContextInfoT = ta.TypeVar('LoggingContextInfoT', bound=LoggingContextInfo)
176
264
 
177
265
  # ../../omlish/http/coro/server/server.py
178
- CoroHttpServerFactory = ta.Callable[[SocketAddress], 'CoroHttpServer']
266
+ CoroHttpServerFactory = ta.Callable[[SocketAddress], 'CoroHttpServer'] # ta.TypeAlias
179
267
 
180
268
 
181
269
  ########################################
@@ -1725,6 +1813,36 @@ class HttpProtocolVersions:
1725
1813
  HTTP_2_0 = HttpProtocolVersion(2, 0)
1726
1814
 
1727
1815
 
1816
+ ########################################
1817
+ # ../../../omlish/io/readers.py
1818
+
1819
+
1820
+ ##
1821
+
1822
+
1823
+ class RawBytesReader(ta.Protocol):
1824
+ def read1(self, n: int = -1, /) -> bytes: ...
1825
+
1826
+
1827
+ class BufferedBytesReader(RawBytesReader, ta.Protocol):
1828
+ def read(self, n: int = -1, /) -> bytes: ...
1829
+
1830
+ def readall(self) -> bytes: ...
1831
+
1832
+
1833
+ #
1834
+
1835
+
1836
+ class AsyncRawBytesReader(ta.Protocol):
1837
+ def read1(self, n: int = -1, /) -> ta.Awaitable[bytes]: ...
1838
+
1839
+
1840
+ class AsyncBufferedBytesReader(AsyncRawBytesReader, ta.Protocol):
1841
+ def read(self, n: int = -1, /) -> ta.Awaitable[bytes]: ...
1842
+
1843
+ def readall(self) -> ta.Awaitable[bytes]: ...
1844
+
1845
+
1728
1846
  ########################################
1729
1847
  # ../../../omlish/lite/abstract.py
1730
1848
 
@@ -1740,25 +1858,49 @@ def is_abstract_method(obj: ta.Any) -> bool:
1740
1858
  return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
1741
1859
 
1742
1860
 
1743
- def update_abstracts(cls, *, force=False):
1861
+ def compute_abstract_methods(cls: type) -> ta.FrozenSet[str]:
1862
+ # ~> https://github.com/python/cpython/blob/f3476c6507381ca860eec0989f53647b13517423/Modules/_abc.c#L358
1863
+
1864
+ # Stage 1: direct abstract methods
1865
+
1866
+ abstracts = {
1867
+ a
1868
+ # Get items as a list to avoid mutation issues during iteration
1869
+ for a, v in list(cls.__dict__.items())
1870
+ if is_abstract_method(v)
1871
+ }
1872
+
1873
+ # Stage 2: inherited abstract methods
1874
+
1875
+ for base in cls.__bases__:
1876
+ # Get __abstractmethods__ from base if it exists
1877
+ if (base_abstracts := getattr(base, _ABSTRACT_METHODS_ATTR, None)) is None:
1878
+ continue
1879
+
1880
+ # Iterate over abstract methods in base
1881
+ for key in base_abstracts:
1882
+ # Check if this class has an attribute with this name
1883
+ try:
1884
+ value = getattr(cls, key)
1885
+ except AttributeError:
1886
+ # Attribute not found in this class, skip
1887
+ continue
1888
+
1889
+ # Check if it's still abstract
1890
+ if is_abstract_method(value):
1891
+ abstracts.add(key)
1892
+
1893
+ return frozenset(abstracts)
1894
+
1895
+
1896
+ def update_abstracts(cls: ta.Type[T], *, force: bool = False) -> ta.Type[T]:
1744
1897
  if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
1745
1898
  # Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
1746
1899
  # implementation (especially during testing), and we want to handle both cases.
1747
1900
  return cls
1748
1901
 
1749
- abstracts: ta.Set[str] = set()
1750
-
1751
- for scls in cls.__bases__:
1752
- for name in getattr(scls, _ABSTRACT_METHODS_ATTR, ()):
1753
- value = getattr(cls, name, None)
1754
- if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
1755
- abstracts.add(name)
1756
-
1757
- for name, value in cls.__dict__.items():
1758
- if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
1759
- abstracts.add(name)
1760
-
1761
- setattr(cls, _ABSTRACT_METHODS_ATTR, frozenset(abstracts))
1902
+ abstracts = compute_abstract_methods(cls)
1903
+ setattr(cls, _ABSTRACT_METHODS_ATTR, abstracts)
1762
1904
  return cls
1763
1905
 
1764
1906
 
@@ -1812,23 +1954,26 @@ class Abstract:
1812
1954
  super().__init_subclass__(**kwargs)
1813
1955
 
1814
1956
  if not (Abstract in cls.__bases__ or abc.ABC in cls.__bases__):
1815
- ams = {a: cls for a, o in cls.__dict__.items() if is_abstract_method(o)}
1816
-
1817
- seen = set(cls.__dict__)
1818
- for b in cls.__bases__:
1819
- ams.update({a: b for a in set(getattr(b, _ABSTRACT_METHODS_ATTR, [])) - seen}) # noqa
1820
- seen.update(dir(b))
1957
+ if ams := compute_abstract_methods(cls):
1958
+ amd = {
1959
+ a: mcls
1960
+ for mcls in cls.__mro__[::-1]
1961
+ for a in ams
1962
+ if a in mcls.__dict__
1963
+ }
1821
1964
 
1822
- if ams:
1823
1965
  raise AbstractTypeError(
1824
1966
  f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
1825
1967
  ', '.join(sorted([
1826
1968
  '.'.join([
1827
- *([m] if (m := getattr(c, '__module__')) else []),
1828
- getattr(c, '__qualname__', getattr(c, '__name__')),
1969
+ *([
1970
+ *([m] if (m := getattr(c, '__module__')) else []),
1971
+ getattr(c, '__qualname__', getattr(c, '__name__')),
1972
+ ] if c is not None else '?'),
1829
1973
  a,
1830
1974
  ])
1831
- for a, c in ams.items()
1975
+ for a in ams
1976
+ for c in [amd.get(a)]
1832
1977
  ])),
1833
1978
  )
1834
1979
 
@@ -1845,6 +1990,150 @@ class Abstract:
1845
1990
  update_abstracts(cls, force=True)
1846
1991
 
1847
1992
 
1993
+ ########################################
1994
+ # ../../../omlish/lite/asyncs.py
1995
+
1996
+
1997
+ ##
1998
+
1999
+
2000
+ async def opt_await(aw: ta.Optional[ta.Awaitable[T]]) -> ta.Optional[T]:
2001
+ return (await aw if aw is not None else None)
2002
+
2003
+
2004
+ async def async_list(ai: ta.AsyncIterable[T]) -> ta.List[T]:
2005
+ return [v async for v in ai]
2006
+
2007
+
2008
+ async def async_enumerate(ai: ta.AsyncIterable[T]) -> ta.AsyncIterable[ta.Tuple[int, T]]:
2009
+ i = 0
2010
+ async for e in ai:
2011
+ yield (i, e)
2012
+ i += 1
2013
+
2014
+
2015
+ ##
2016
+
2017
+
2018
+ def as_async(fn: ta.Callable[..., T], *, wrap: bool = False) -> ta.Callable[..., ta.Awaitable[T]]:
2019
+ async def inner(*args, **kwargs):
2020
+ return fn(*args, **kwargs)
2021
+
2022
+ return functools.wraps(fn)(inner) if wrap else inner
2023
+
2024
+
2025
+ ##
2026
+
2027
+
2028
+ class SyncAwaitCoroutineNotTerminatedError(Exception):
2029
+ pass
2030
+
2031
+
2032
+ def sync_await(aw: ta.Awaitable[T]) -> T:
2033
+ """
2034
+ Allows for the synchronous execution of async functions which will never actually *externally* await anything. These
2035
+ functions are allowed to await any number of other functions - including contextmanagers and generators - so long as
2036
+ nothing ever actually 'leaks' out of the function, presumably to an event loop.
2037
+ """
2038
+
2039
+ ret = missing = object()
2040
+
2041
+ async def thunk():
2042
+ nonlocal ret
2043
+
2044
+ ret = await aw
2045
+
2046
+ cr = thunk()
2047
+ try:
2048
+ try:
2049
+ cr.send(None)
2050
+ except StopIteration:
2051
+ pass
2052
+
2053
+ if ret is missing or cr.cr_await is not None or cr.cr_running:
2054
+ raise SyncAwaitCoroutineNotTerminatedError('Not terminated')
2055
+
2056
+ finally:
2057
+ cr.close()
2058
+
2059
+ return ta.cast(T, ret)
2060
+
2061
+
2062
+ #
2063
+
2064
+
2065
+ def sync_aiter(ai: ta.AsyncIterator[T]) -> ta.Iterator[T]:
2066
+ while True:
2067
+ try:
2068
+ o = sync_await(ai.__anext__())
2069
+ except StopAsyncIteration:
2070
+ break
2071
+ yield o
2072
+
2073
+
2074
+ def sync_async_list(ai: ta.AsyncIterable[T]) -> ta.List[T]:
2075
+ """
2076
+ Uses `sync_await` to synchronously read the full contents of a function call returning an async iterator, given that
2077
+ the function never externally awaits anything.
2078
+ """
2079
+
2080
+ lst: ta.Optional[ta.List[T]] = None
2081
+
2082
+ async def inner():
2083
+ nonlocal lst
2084
+
2085
+ lst = [v async for v in ai]
2086
+
2087
+ sync_await(inner())
2088
+
2089
+ if not isinstance(lst, list):
2090
+ raise TypeError(lst)
2091
+
2092
+ return lst
2093
+
2094
+
2095
+ #
2096
+
2097
+
2098
+ @ta.final
2099
+ class SyncAwaitContextManager(ta.Generic[T]):
2100
+ def __init__(self, acm: ta.AsyncContextManager[T]) -> None:
2101
+ self._acm = acm
2102
+
2103
+ def __repr__(self) -> str:
2104
+ return f'{self.__class__.__name__}({self._acm!r})'
2105
+
2106
+ def __enter__(self) -> T:
2107
+ return sync_await(self._acm.__aenter__())
2108
+
2109
+ def __exit__(self, exc_type, exc_val, exc_tb):
2110
+ return sync_await(self._acm.__aexit__(exc_type, exc_val, exc_tb))
2111
+
2112
+
2113
+ sync_async_with = SyncAwaitContextManager
2114
+
2115
+
2116
+ ##
2117
+
2118
+
2119
+ @ta.final
2120
+ class SyncToAsyncContextManager(ta.Generic[T]):
2121
+ def __init__(self, cm: ta.ContextManager[T]) -> None:
2122
+ self._cm = cm
2123
+
2124
+ def __repr__(self) -> str:
2125
+ return f'{self.__class__.__name__}({self._cm!r})'
2126
+
2127
+ async def __aenter__(self) -> T:
2128
+ return self._cm.__enter__()
2129
+
2130
+ async def __aexit__(self, exc_type, exc_value, traceback, /):
2131
+ return self._cm.__exit__(exc_type, exc_value, traceback)
2132
+
2133
+
2134
+ as_async_context_manager = SyncToAsyncContextManager
2135
+
2136
+
1848
2137
  ########################################
1849
2138
  # ../../../omlish/lite/attrops.py
1850
2139
  """
@@ -1853,6 +2142,8 @@ TODO:
1853
2142
  - per-attr repr transform / filter
1854
2143
  - __ne__ ? cases where it still matters
1855
2144
  - ordering ?
2145
+ - repr_filter: ta.Union[ta.Callable[[ta.Any], ta.Optional[str]], ta.Literal['not_none', 'truthy']]] ?
2146
+ - unify repr/repr_fn/repr_filter
1856
2147
  """
1857
2148
 
1858
2149
 
@@ -1870,6 +2161,8 @@ class AttrOps(ta.Generic[T]):
1870
2161
  display: ta.Optional[str] = None,
1871
2162
 
1872
2163
  repr: bool = True, # noqa
2164
+ repr_fn: ta.Optional[ta.Callable[[ta.Any], ta.Optional[str]]] = None,
2165
+
1873
2166
  hash: bool = True, # noqa
1874
2167
  eq: bool = True,
1875
2168
  ) -> None:
@@ -1884,6 +2177,8 @@ class AttrOps(ta.Generic[T]):
1884
2177
  self._display = display
1885
2178
 
1886
2179
  self._repr = repr
2180
+ self._repr_fn = repr_fn
2181
+
1887
2182
  self._hash = hash
1888
2183
  self._eq = eq
1889
2184
 
@@ -1891,21 +2186,30 @@ class AttrOps(ta.Generic[T]):
1891
2186
  def of(
1892
2187
  cls,
1893
2188
  o: ta.Union[
1894
- str,
1895
- ta.Tuple[str, str],
1896
2189
  'AttrOps.Attr',
2190
+ str,
2191
+ ta.Tuple[str, ta.Union[str, ta.Mapping[str, ta.Any]]],
2192
+ ta.Mapping[str, ta.Any],
1897
2193
  ],
1898
2194
  ) -> 'AttrOps.Attr':
1899
2195
  if isinstance(o, AttrOps.Attr):
1900
2196
  return o
1901
2197
  elif isinstance(o, str):
1902
2198
  return cls(o)
2199
+ elif isinstance(o, tuple):
2200
+ name, x = o
2201
+ kw: ta.Mapping[str, ta.Any]
2202
+ if isinstance(x, str):
2203
+ kw = dict(display=x)
2204
+ elif isinstance(x, ta.Mapping):
2205
+ kw = x
2206
+ else:
2207
+ raise TypeError(x)
2208
+ return cls(name, **kw)
2209
+ elif isinstance(o, ta.Mapping):
2210
+ return cls(**o)
1903
2211
  else:
1904
- name, disp = o
1905
- return cls(
1906
- name,
1907
- display=disp,
1908
- )
2212
+ raise TypeError(o)
1909
2213
 
1910
2214
  @property
1911
2215
  def name(self) -> str:
@@ -1923,19 +2227,34 @@ class AttrOps(ta.Generic[T]):
1923
2227
  def eq(self) -> bool:
1924
2228
  return self._eq
1925
2229
 
2230
+ @staticmethod
2231
+ def opt_repr(o: ta.Any) -> ta.Optional[str]:
2232
+ return repr(o) if o is not None else None
2233
+
2234
+ @staticmethod
2235
+ def truthy_repr(o: ta.Any) -> ta.Optional[str]:
2236
+ return repr(o) if o else None
2237
+
2238
+ #
2239
+
1926
2240
  @ta.overload
1927
2241
  def __init__(
1928
2242
  self,
1929
2243
  *attrs: ta.Sequence[ta.Union[
1930
2244
  str,
1931
- ta.Tuple[str, str],
2245
+ ta.Tuple[str, ta.Union[str, ta.Mapping[str, ta.Any]]],
2246
+ ta.Mapping[str, ta.Any],
1932
2247
  Attr,
1933
2248
  ]],
2249
+
1934
2250
  with_module: bool = False,
1935
2251
  use_qualname: bool = False,
1936
2252
  with_id: bool = False,
2253
+ terse: bool = False,
1937
2254
  repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = None,
1938
2255
  recursive: bool = False,
2256
+
2257
+ cache_hash: ta.Union[bool, str] = False,
1939
2258
  subtypes_eq: bool = False,
1940
2259
  ) -> None:
1941
2260
  ...
@@ -1945,16 +2264,20 @@ class AttrOps(ta.Generic[T]):
1945
2264
  self,
1946
2265
  attrs_fn: ta.Callable[[T], ta.Tuple[ta.Union[
1947
2266
  ta.Any,
1948
- ta.Tuple[str, ta.Any],
2267
+ ta.Tuple[ta.Any, ta.Union[str, ta.Mapping[str, ta.Any]]],
1949
2268
  Attr,
1950
2269
  ], ...]],
1951
2270
  /,
1952
2271
  *,
2272
+
1953
2273
  with_module: bool = False,
1954
2274
  use_qualname: bool = False,
1955
2275
  with_id: bool = False,
2276
+ terse: bool = False,
1956
2277
  repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = None,
1957
2278
  recursive: bool = False,
2279
+
2280
+ cache_hash: ta.Union[bool, str] = False,
1958
2281
  subtypes_eq: bool = False,
1959
2282
  ) -> None:
1960
2283
  ...
@@ -1962,11 +2285,15 @@ class AttrOps(ta.Generic[T]):
1962
2285
  def __init__(
1963
2286
  self,
1964
2287
  *args,
2288
+
1965
2289
  with_module=False,
1966
2290
  use_qualname=False,
1967
2291
  with_id=False,
2292
+ terse=False,
1968
2293
  repr_filter=None,
1969
2294
  recursive=False,
2295
+
2296
+ cache_hash=False,
1970
2297
  subtypes_eq=False,
1971
2298
  ) -> None:
1972
2299
  if args and len(args) == 1 and callable(args[0]):
@@ -1977,8 +2304,11 @@ class AttrOps(ta.Generic[T]):
1977
2304
  self._with_module: bool = with_module
1978
2305
  self._use_qualname: bool = use_qualname
1979
2306
  self._with_id: bool = with_id
2307
+ self._terse: bool = terse
1980
2308
  self._repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = repr_filter
1981
2309
  self._recursive: bool = recursive
2310
+
2311
+ self._cache_hash: ta.Union[bool, str] = cache_hash
1982
2312
  self._subtypes_eq: bool = subtypes_eq
1983
2313
 
1984
2314
  @property
@@ -2013,20 +2343,27 @@ class AttrOps(ta.Generic[T]):
2013
2343
 
2014
2344
  attrs: ta.List[AttrOps.Attr] = []
2015
2345
  for o in raw:
2016
- if isinstance(o, AttrOps.Attr):
2017
- attrs.append(o)
2346
+ if isinstance(o, (AttrOps.Attr, ta.Mapping)):
2347
+ attrs.append(AttrOps.Attr.of(o))
2018
2348
  continue
2019
2349
 
2350
+ kw: ta.Mapping[str, ta.Any]
2020
2351
  if isinstance(o, tuple):
2021
- disp, cap, = o
2352
+ cap, x = o
2353
+ if isinstance(x, str):
2354
+ kw = dict(display=x)
2355
+ elif isinstance(x, ta.Mapping):
2356
+ kw = x
2357
+ else:
2358
+ raise TypeError(x)
2022
2359
  else:
2023
- disp, cap = None, o
2360
+ cap, kw = o, {}
2024
2361
 
2025
2362
  path = tuple(rec(cap))
2026
2363
 
2027
2364
  attrs.append(AttrOps.Attr(
2028
2365
  '.'.join(path),
2029
- display=disp,
2366
+ **kw,
2030
2367
  ))
2031
2368
 
2032
2369
  return attrs
@@ -2043,19 +2380,27 @@ class AttrOps(ta.Generic[T]):
2043
2380
  pass
2044
2381
 
2045
2382
  def _repr(o: T) -> str:
2046
- vs = ', '.join(
2047
- f'{a._display}={v!r}' # noqa
2048
- for a in self._attrs
2049
- if a._repr # noqa
2050
- for v in [getattr(o, a._name)] # noqa
2051
- if self._repr_filter is None or self._repr_filter(v)
2052
- )
2383
+ vs: ta.List[str] = []
2384
+ for a in self._attrs:
2385
+ if not a._repr: # noqa
2386
+ continue
2387
+ v = getattr(o, a._name) # noqa
2388
+ if self._repr_filter is not None and not self._repr_filter(v):
2389
+ continue
2390
+ if (rfn := a._repr_fn) is None: # noqa
2391
+ rfn = repr
2392
+ if (vr := rfn(v)) is None:
2393
+ continue
2394
+ if self._terse:
2395
+ vs.append(vr)
2396
+ else:
2397
+ vs.append(f'{a._display}={vr}') # noqa
2053
2398
 
2054
2399
  return (
2055
2400
  f'{o.__class__.__module__ + "." if self._with_module else ""}'
2056
2401
  f'{o.__class__.__qualname__ if self._use_qualname else o.__class__.__name__}'
2057
- f'{("@" + hex(id(o))[2:]) if self._with_id else ""}'
2058
- f'({vs})'
2402
+ f'{("@" + hex(id(o))[2:]) if self._with_id else ""}' # noqa
2403
+ f'({", ".join(vs)})'
2059
2404
  )
2060
2405
 
2061
2406
  if self._recursive:
@@ -2080,6 +2425,8 @@ class AttrOps(ta.Generic[T]):
2080
2425
 
2081
2426
  #
2082
2427
 
2428
+ _DEFAULT_CACHED_HASH_ATTR: ta.ClassVar[str] = '__cached_hash__'
2429
+
2083
2430
  _hash: ta.Callable[[T], int]
2084
2431
 
2085
2432
  @property
@@ -2089,13 +2436,33 @@ class AttrOps(ta.Generic[T]):
2089
2436
  except AttributeError:
2090
2437
  pass
2091
2438
 
2092
- def _hash(o: T) -> int:
2439
+ def _calc_hash(o: T) -> int:
2093
2440
  return hash(tuple(
2094
2441
  getattr(o, a._name) # noqa
2095
2442
  for a in self._attrs
2096
2443
  if a._hash # noqa
2097
2444
  ))
2098
2445
 
2446
+ if (ch := self._cache_hash) is not False:
2447
+ if ch is True:
2448
+ cha = self._DEFAULT_CACHED_HASH_ATTR
2449
+ elif isinstance(ch, str):
2450
+ cha = ch
2451
+ else:
2452
+ raise TypeError(ch)
2453
+
2454
+ def _cached_hash(o: T) -> int:
2455
+ try:
2456
+ return object.__getattribute__(o, cha)
2457
+ except AttributeError:
2458
+ object.__setattr__(o, cha, h := _calc_hash(o))
2459
+ return h
2460
+
2461
+ _hash = _cached_hash
2462
+
2463
+ else:
2464
+ _hash = _calc_hash
2465
+
2099
2466
  self._hash = _hash
2100
2467
  return _hash
2101
2468
 
@@ -2236,6 +2603,62 @@ def async_cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
2236
2603
  return _AsyncCachedNullary(fn)
2237
2604
 
2238
2605
 
2606
+ ##
2607
+
2608
+
2609
+ cached_property = functools.cached_property
2610
+
2611
+
2612
+ class _cached_property: # noqa
2613
+ """Backported to pick up https://github.com/python/cpython/commit/056dfc71dce15f81887f0bd6da09d6099d71f979 ."""
2614
+
2615
+ def __init__(self, func):
2616
+ self.func = func
2617
+ self.attrname = None # noqa
2618
+ self.__doc__ = func.__doc__
2619
+ self.__module__ = func.__module__
2620
+
2621
+ _NOT_FOUND = object()
2622
+
2623
+ def __set_name__(self, owner, name):
2624
+ if self.attrname is None:
2625
+ self.attrname = name # noqa
2626
+ elif name != self.attrname:
2627
+ raise TypeError(
2628
+ f'Cannot assign the same cached_property to two different names ({self.attrname!r} and {name!r}).',
2629
+ )
2630
+
2631
+ def __get__(self, instance, owner=None):
2632
+ if instance is None:
2633
+ return self
2634
+ if self.attrname is None:
2635
+ raise TypeError('Cannot use cached_property instance without calling __set_name__ on it.')
2636
+
2637
+ try:
2638
+ cache = instance.__dict__
2639
+ except AttributeError: # not all objects have __dict__ (e.g. class defines slots)
2640
+ raise TypeError(
2641
+ f"No '__dict__' attribute on {type(instance).__name__!r} instance to cache {self.attrname!r} property.",
2642
+ ) from None
2643
+
2644
+ val = cache.get(self.attrname, self._NOT_FOUND)
2645
+
2646
+ if val is self._NOT_FOUND:
2647
+ val = self.func(instance)
2648
+ try:
2649
+ cache[self.attrname] = val
2650
+ except TypeError:
2651
+ raise TypeError(
2652
+ f"The '__dict__' attribute on {type(instance).__name__!r} instance does not support item "
2653
+ f"assignment for caching {self.attrname!r} property.",
2654
+ ) from None
2655
+
2656
+ return val
2657
+
2658
+
2659
+ globals()['cached_property'] = _cached_property
2660
+
2661
+
2239
2662
  ########################################
2240
2663
  # ../../../omlish/lite/check.py
2241
2664
  """
@@ -3024,6 +3447,12 @@ def format_num_bytes(num_bytes: int) -> str:
3024
3447
 
3025
3448
  ##
3026
3449
  # A workaround for typing deficiencies (like `Argument 2 to NewType(...) must be subclassable`).
3450
+ #
3451
+ # Note that this problem doesn't happen at runtime - it happens in mypy:
3452
+ #
3453
+ # mypy <(echo "import typing as ta; MyCallback = ta.NewType('MyCallback', ta.Callable[[], None])")
3454
+ # /dev/fd/11:1:22: error: Argument 2 to NewType(...) must be subclassable (got "Callable[[], None]") [valid-newtype]
3455
+ #
3027
3456
 
3028
3457
 
3029
3458
  @dc.dataclass(frozen=True)
@@ -3069,6 +3498,39 @@ class Func3(ta.Generic[A0, A1, A2, T]):
3069
3498
  ##
3070
3499
 
3071
3500
 
3501
+ @dc.dataclass(frozen=True)
3502
+ class CachedFunc0(ta.Generic[T]):
3503
+ fn: ta.Callable[[], T]
3504
+
3505
+ def __call__(self) -> T:
3506
+ try:
3507
+ return object.__getattribute__(self, '_value')
3508
+ except AttributeError:
3509
+ pass
3510
+
3511
+ value = self.fn()
3512
+ object.__setattr__(self, '_value', value)
3513
+ return value
3514
+
3515
+
3516
+ @dc.dataclass(frozen=True)
3517
+ class AsyncCachedFunc0(ta.Generic[T]):
3518
+ fn: ta.Callable[[], ta.Awaitable[T]]
3519
+
3520
+ async def __call__(self) -> T:
3521
+ try:
3522
+ return object.__getattribute__(self, '_value')
3523
+ except AttributeError:
3524
+ pass
3525
+
3526
+ value = await self.fn()
3527
+ object.__setattr__(self, '_value', value)
3528
+ return value
3529
+
3530
+
3531
+ ##
3532
+
3533
+
3072
3534
  _TYPING_ANNOTATIONS_ATTR = '__annotate__' if sys.version_info >= (3, 14) else '__annotations__'
3073
3535
 
3074
3536
 
@@ -4114,6 +4576,72 @@ def build_config_named_children(
4114
4576
  return lst
4115
4577
 
4116
4578
 
4579
+ ########################################
4580
+ # ../../../omlish/http/coro/io.py
4581
+
4582
+
4583
+ ##
4584
+
4585
+
4586
+ class CoroHttpIo:
4587
+ def __new__(cls, *args, **kwargs): # noqa
4588
+ raise TypeError
4589
+
4590
+ def __init_subclass__(cls, **kwargs): # noqa
4591
+ raise TypeError
4592
+
4593
+ #
4594
+
4595
+ MAX_LINE: ta.ClassVar[int] = 65536
4596
+
4597
+ #
4598
+
4599
+ class Io(Abstract):
4600
+ pass
4601
+
4602
+ #
4603
+
4604
+ class AnyLogIo(Io, Abstract):
4605
+ pass
4606
+
4607
+ #
4608
+
4609
+ @dc.dataclass(frozen=True)
4610
+ class ConnectIo(Io):
4611
+ args: ta.Tuple[ta.Any, ...]
4612
+ kwargs: ta.Optional[ta.Dict[str, ta.Any]] = None
4613
+
4614
+ server_hostname: ta.Optional[str] = None
4615
+
4616
+ #
4617
+
4618
+ class CloseIo(Io):
4619
+ pass
4620
+
4621
+ #
4622
+
4623
+ class AnyReadIo(Io): # noqa
4624
+ pass
4625
+
4626
+ @dc.dataclass(frozen=True)
4627
+ class ReadIo(AnyReadIo):
4628
+ sz: ta.Optional[int]
4629
+
4630
+ @dc.dataclass(frozen=True)
4631
+ class ReadLineIo(AnyReadIo):
4632
+ sz: int
4633
+
4634
+ @dc.dataclass(frozen=True)
4635
+ class PeekIo(AnyReadIo):
4636
+ sz: int
4637
+
4638
+ #
4639
+
4640
+ @dc.dataclass(frozen=True)
4641
+ class WriteIo(Io):
4642
+ data: bytes
4643
+
4644
+
4117
4645
  ########################################
4118
4646
  # ../../../omlish/http/parsing.py
4119
4647
  # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -4529,6 +5057,10 @@ class HttpRequestParser:
4529
5057
 
4530
5058
  ########################################
4531
5059
  # ../../../omlish/io/buffers.py
5060
+ """
5061
+ TODO:
5062
+ - overhaul and just coro-ify pyio?
5063
+ """
4532
5064
 
4533
5065
 
4534
5066
  ##
@@ -4707,6 +5239,9 @@ class ReadableListBuffer:
4707
5239
 
4708
5240
  self._lst: list[bytes] = []
4709
5241
 
5242
+ def __bool__(self) -> ta.NoReturn:
5243
+ raise TypeError("Use 'buf is not None' or 'len(buf)'.")
5244
+
4710
5245
  def __len__(self) -> int:
4711
5246
  return sum(map(len, self._lst))
4712
5247
 
@@ -4732,6 +5267,9 @@ class ReadableListBuffer:
4732
5267
 
4733
5268
  def read(self, n: ta.Optional[int] = None) -> ta.Optional[bytes]:
4734
5269
  if n is None:
5270
+ if not self._lst:
5271
+ return b''
5272
+
4735
5273
  o = b''.join(self._lst)
4736
5274
  self._lst = []
4737
5275
  return o
@@ -4770,6 +5308,110 @@ class ReadableListBuffer:
4770
5308
  r = self.read_until_(delim)
4771
5309
  return r if isinstance(r, bytes) else None
4772
5310
 
5311
+ #
5312
+
5313
+ DEFAULT_BUFFERED_READER_CHUNK_SIZE: ta.ClassVar[int] = -1
5314
+
5315
+ @ta.final
5316
+ class _BufferedBytesReader(BufferedBytesReader):
5317
+ def __init__(
5318
+ self,
5319
+ raw: RawBytesReader,
5320
+ buf: 'ReadableListBuffer',
5321
+ *,
5322
+ chunk_size: ta.Optional[int] = None,
5323
+ ) -> None:
5324
+ self._raw = raw
5325
+ self._buf = buf
5326
+ self._chunk_size = chunk_size or ReadableListBuffer.DEFAULT_BUFFERED_READER_CHUNK_SIZE
5327
+
5328
+ def read1(self, n: int = -1, /) -> bytes:
5329
+ if n < 0:
5330
+ n = self._chunk_size
5331
+ if not n:
5332
+ return b''
5333
+ if 0 < n <= len(self._buf):
5334
+ return self._buf.read(n) or b''
5335
+ return self._raw.read1(n)
5336
+
5337
+ def read(self, /, n: int = -1) -> bytes:
5338
+ if n < 0:
5339
+ return self.readall()
5340
+ while len(self._buf) < n:
5341
+ if not (b := self._raw.read1(n)):
5342
+ break
5343
+ self._buf.feed(b)
5344
+ return self._buf.read(n) or b''
5345
+
5346
+ def readall(self) -> bytes:
5347
+ buf = io.BytesIO()
5348
+ buf.write(self._buf.read() or b'')
5349
+ while (b := self._raw.read1(self._chunk_size)):
5350
+ buf.write(b)
5351
+ return buf.getvalue()
5352
+
5353
+ def new_buffered_reader(
5354
+ self,
5355
+ raw: RawBytesReader,
5356
+ *,
5357
+ chunk_size: ta.Optional[int] = None,
5358
+ ) -> BufferedBytesReader:
5359
+ return self._BufferedBytesReader(
5360
+ raw,
5361
+ self,
5362
+ chunk_size=chunk_size,
5363
+ )
5364
+
5365
+ @ta.final
5366
+ class _AsyncBufferedBytesReader(AsyncBufferedBytesReader):
5367
+ def __init__(
5368
+ self,
5369
+ raw: AsyncRawBytesReader,
5370
+ buf: 'ReadableListBuffer',
5371
+ *,
5372
+ chunk_size: ta.Optional[int] = None,
5373
+ ) -> None:
5374
+ self._raw = raw
5375
+ self._buf = buf
5376
+ self._chunk_size = chunk_size or ReadableListBuffer.DEFAULT_BUFFERED_READER_CHUNK_SIZE
5377
+
5378
+ async def read1(self, n: int = -1, /) -> bytes:
5379
+ if n < 0:
5380
+ n = self._chunk_size
5381
+ if not n:
5382
+ return b''
5383
+ if 0 < n <= len(self._buf):
5384
+ return self._buf.read(n) or b''
5385
+ return await self._raw.read1(n)
5386
+
5387
+ async def read(self, /, n: int = -1) -> bytes:
5388
+ if n < 0:
5389
+ return await self.readall()
5390
+ while len(self._buf) < n:
5391
+ if not (b := await self._raw.read1(n)):
5392
+ break
5393
+ self._buf.feed(b)
5394
+ return self._buf.read(n) or b''
5395
+
5396
+ async def readall(self) -> bytes:
5397
+ buf = io.BytesIO()
5398
+ buf.write(self._buf.read() or b'')
5399
+ while b := await self._raw.read1(self._chunk_size):
5400
+ buf.write(b)
5401
+ return buf.getvalue()
5402
+
5403
+ def new_async_buffered_reader(
5404
+ self,
5405
+ raw: AsyncRawBytesReader,
5406
+ *,
5407
+ chunk_size: ta.Optional[int] = None,
5408
+ ) -> AsyncBufferedBytesReader:
5409
+ return self._AsyncBufferedBytesReader(
5410
+ raw,
5411
+ self,
5412
+ chunk_size=chunk_size,
5413
+ )
5414
+
4773
5415
 
4774
5416
  ##
4775
5417
 
@@ -5960,6 +6602,13 @@ class Maybe(ta.Generic[T]):
5960
6602
  else:
5961
6603
  return other
5962
6604
 
6605
+ @ta.final
6606
+ def or_none(self) -> ta.Optional[T]:
6607
+ if self.present:
6608
+ return self.must()
6609
+ else:
6610
+ return None
6611
+
5963
6612
  @ta.final
5964
6613
  def or_else_get(self, supplier: ta.Callable[[], ta.Union[T, U]]) -> ta.Union[T, U]:
5965
6614
  if self.present:
@@ -6014,8 +6663,6 @@ class _JustMaybe(_Maybe[T]):
6014
6663
  __slots__ = ('_v', '_hash')
6015
6664
 
6016
6665
  def __init__(self, v: T) -> None:
6017
- super().__init__()
6018
-
6019
6666
  self._v = v
6020
6667
 
6021
6668
  @property
@@ -6073,6 +6720,13 @@ class _EmptyMaybe(_Maybe[T]):
6073
6720
  Maybe._empty = _EmptyMaybe() # noqa
6074
6721
 
6075
6722
 
6723
+ ##
6724
+
6725
+
6726
+ setattr(Maybe, 'just', _JustMaybe) # noqa
6727
+ setattr(Maybe, 'empty', functools.partial(operator.attrgetter('_empty'), Maybe))
6728
+
6729
+
6076
6730
  ########################################
6077
6731
  # ../../../omlish/lite/runtime.py
6078
6732
 
@@ -8676,6 +9330,9 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
8676
9330
  self._infos[type(info)] = info
8677
9331
  return self
8678
9332
 
9333
+ def get_infos(self) -> ta.Mapping[ta.Type[LoggingContextInfo], LoggingContextInfo]:
9334
+ return self._infos
9335
+
8679
9336
  def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
8680
9337
  return self._infos.get(ty)
8681
9338
 
@@ -8698,7 +9355,7 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
8698
9355
  _stack_offset: int
8699
9356
  _stack_info: bool
8700
9357
 
8701
- def inc_stack_offset(self, ofs: int = 1) -> 'CaptureLoggingContext':
9358
+ def inc_stack_offset(self, ofs: int = 1) -> 'CaptureLoggingContextImpl':
8702
9359
  if hasattr(self, '_stack_offset'):
8703
9360
  self._stack_offset += ofs
8704
9361
  return self
@@ -8730,10 +9387,9 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
8730
9387
 
8731
9388
 
8732
9389
  ########################################
8733
- # ../../../omlish/logs/standard.py
9390
+ # ../../../omlish/logs/std/standard.py
8734
9391
  """
8735
9392
  TODO:
8736
- - !! move to std !!
8737
9393
  - structured
8738
9394
  - prefixed
8739
9395
  - debug
@@ -9392,48 +10048,21 @@ class CoroHttpServer:
9392
10048
 
9393
10049
  #
9394
10050
 
9395
- class Io(Abstract):
9396
- pass
9397
-
9398
- #
9399
-
9400
- class AnyLogIo(Io):
9401
- pass
9402
-
9403
10051
  @dc.dataclass(frozen=True)
9404
- class ParsedRequestLogIo(AnyLogIo):
10052
+ class ParsedRequestLogIo(CoroHttpIo.AnyLogIo):
9405
10053
  request: ParsedHttpRequest
9406
10054
 
9407
10055
  @dc.dataclass(frozen=True)
9408
- class ErrorLogIo(AnyLogIo):
10056
+ class ErrorLogIo(CoroHttpIo.AnyLogIo):
9409
10057
  error: 'CoroHttpServer.Error'
9410
10058
 
9411
10059
  #
9412
10060
 
9413
- class AnyReadIo(Io): # noqa
9414
- pass
9415
-
9416
- @dc.dataclass(frozen=True)
9417
- class ReadIo(AnyReadIo):
9418
- sz: int
9419
-
9420
- @dc.dataclass(frozen=True)
9421
- class ReadLineIo(AnyReadIo):
9422
- sz: int
9423
-
9424
- #
9425
-
9426
- @dc.dataclass(frozen=True)
9427
- class WriteIo(Io):
9428
- data: bytes
9429
-
9430
- #
9431
-
9432
10061
  @dc.dataclass(frozen=True)
9433
10062
  class CoroHandleResult:
9434
10063
  close_reason: ta.Literal['response', 'internal', None] = None
9435
10064
 
9436
- def coro_handle(self) -> ta.Generator[Io, ta.Optional[bytes], CoroHandleResult]:
10065
+ def coro_handle(self) -> ta.Generator[CoroHttpIo.Io, ta.Optional[bytes], CoroHandleResult]:
9437
10066
  return self._coro_run_handler(self._coro_handle_one())
9438
10067
 
9439
10068
  class Close(Exception): # noqa
@@ -9442,20 +10071,20 @@ class CoroHttpServer:
9442
10071
  def _coro_run_handler(
9443
10072
  self,
9444
10073
  gen: ta.Generator[
9445
- ta.Union[AnyLogIo, AnyReadIo, _Response],
10074
+ ta.Union[CoroHttpIo.AnyLogIo, CoroHttpIo.AnyReadIo, _Response],
9446
10075
  ta.Optional[bytes],
9447
10076
  None,
9448
10077
  ],
9449
- ) -> ta.Generator[Io, ta.Optional[bytes], CoroHandleResult]:
10078
+ ) -> ta.Generator[CoroHttpIo.Io, ta.Optional[bytes], CoroHandleResult]:
9450
10079
  i: ta.Optional[bytes]
9451
10080
  o: ta.Any = next(gen)
9452
10081
  while True:
9453
10082
  try:
9454
- if isinstance(o, self.AnyLogIo):
10083
+ if isinstance(o, CoroHttpIo.AnyLogIo):
9455
10084
  i = None
9456
10085
  yield o
9457
10086
 
9458
- elif isinstance(o, self.AnyReadIo):
10087
+ elif isinstance(o, CoroHttpIo.AnyReadIo):
9459
10088
  i = check.isinstance((yield o), bytes)
9460
10089
 
9461
10090
  elif isinstance(o, self._Response):
@@ -9463,10 +10092,10 @@ class CoroHttpServer:
9463
10092
 
9464
10093
  r = self._preprocess_response(o)
9465
10094
  hb = self._build_response_head_bytes(r)
9466
- check.none((yield self.WriteIo(hb)))
10095
+ check.none((yield CoroHttpIo.WriteIo(hb)))
9467
10096
 
9468
10097
  for b in self._yield_response_data(r):
9469
- yield self.WriteIo(b)
10098
+ yield CoroHttpIo.WriteIo(b)
9470
10099
 
9471
10100
  o.close()
9472
10101
  if o.close_connection:
@@ -9494,7 +10123,7 @@ class CoroHttpServer:
9494
10123
  raise
9495
10124
 
9496
10125
  def _coro_handle_one(self) -> ta.Generator[
9497
- ta.Union[AnyLogIo, AnyReadIo, _Response],
10126
+ ta.Union[CoroHttpIo.AnyLogIo, CoroHttpIo.AnyReadIo, _Response],
9498
10127
  ta.Optional[bytes],
9499
10128
  None,
9500
10129
  ]:
@@ -9504,7 +10133,7 @@ class CoroHttpServer:
9504
10133
  sz = next(gen)
9505
10134
  while True:
9506
10135
  try:
9507
- line = check.isinstance((yield self.ReadLineIo(sz)), bytes)
10136
+ line = check.isinstance((yield CoroHttpIo.ReadLineIo(sz)), bytes)
9508
10137
  sz = gen.send(line)
9509
10138
  except StopIteration as e:
9510
10139
  parsed = e.value
@@ -9543,7 +10172,7 @@ class CoroHttpServer:
9543
10172
 
9544
10173
  request_data: ta.Optional[bytes]
9545
10174
  if (cl := parsed.headers.get('Content-Length')) is not None:
9546
- request_data = check.isinstance((yield self.ReadIo(int(cl))), bytes)
10175
+ request_data = check.isinstance((yield CoroHttpIo.ReadIo(int(cl))), bytes)
9547
10176
  else:
9548
10177
  request_data = None
9549
10178
 
@@ -9630,6 +10259,11 @@ class AnyLogger(Abstract, ta.Generic[T]):
9630
10259
 
9631
10260
  ##
9632
10261
 
10262
+ # This will be 1 for [Sync]Logger and 0 for AsyncLogger - in sync loggers these methods remain present on the stack,
10263
+ # in async loggers they return a coroutine to be awaited and thus aren't actually present when said coroutine is
10264
+ # awaited.
10265
+ _level_proxy_method_stack_offset: int
10266
+
9633
10267
  @ta.overload
9634
10268
  def log(self, level: LogLevel, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
9635
10269
  ...
@@ -9644,7 +10278,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
9644
10278
 
9645
10279
  @ta.final
9646
10280
  def log(self, level: LogLevel, *args, **kwargs):
9647
- return self._log(CaptureLoggingContextImpl(level, stack_offset=1), *args, **kwargs)
10281
+ return self._log(
10282
+ CaptureLoggingContextImpl(
10283
+ level,
10284
+ stack_offset=self._level_proxy_method_stack_offset,
10285
+ ),
10286
+ *args,
10287
+ **kwargs,
10288
+ )
9648
10289
 
9649
10290
  #
9650
10291
 
@@ -9662,7 +10303,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
9662
10303
 
9663
10304
  @ta.final
9664
10305
  def debug(self, *args, **kwargs):
9665
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.DEBUG, stack_offset=1), *args, **kwargs)
10306
+ return self._log(
10307
+ CaptureLoggingContextImpl(
10308
+ NamedLogLevel.DEBUG,
10309
+ stack_offset=self._level_proxy_method_stack_offset,
10310
+ ),
10311
+ *args,
10312
+ **kwargs,
10313
+ )
9666
10314
 
9667
10315
  #
9668
10316
 
@@ -9680,7 +10328,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
9680
10328
 
9681
10329
  @ta.final
9682
10330
  def info(self, *args, **kwargs):
9683
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.INFO, stack_offset=1), *args, **kwargs)
10331
+ return self._log(
10332
+ CaptureLoggingContextImpl(
10333
+ NamedLogLevel.INFO,
10334
+ stack_offset=self._level_proxy_method_stack_offset,
10335
+ ),
10336
+ *args,
10337
+ **kwargs,
10338
+ )
9684
10339
 
9685
10340
  #
9686
10341
 
@@ -9698,7 +10353,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
9698
10353
 
9699
10354
  @ta.final
9700
10355
  def warning(self, *args, **kwargs):
9701
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.WARNING, stack_offset=1), *args, **kwargs)
10356
+ return self._log(
10357
+ CaptureLoggingContextImpl(
10358
+ NamedLogLevel.WARNING,
10359
+ stack_offset=self._level_proxy_method_stack_offset,
10360
+ ),
10361
+ *args,
10362
+ **kwargs,
10363
+ )
9702
10364
 
9703
10365
  #
9704
10366
 
@@ -9716,7 +10378,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
9716
10378
 
9717
10379
  @ta.final
9718
10380
  def error(self, *args, **kwargs):
9719
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.ERROR, stack_offset=1), *args, **kwargs)
10381
+ return self._log(
10382
+ CaptureLoggingContextImpl(
10383
+ NamedLogLevel.ERROR,
10384
+ stack_offset=self._level_proxy_method_stack_offset,
10385
+ ),
10386
+ *args,
10387
+ **kwargs,
10388
+ )
9720
10389
 
9721
10390
  #
9722
10391
 
@@ -9734,7 +10403,15 @@ class AnyLogger(Abstract, ta.Generic[T]):
9734
10403
 
9735
10404
  @ta.final
9736
10405
  def exception(self, *args, exc_info: LoggingExcInfoArg = True, **kwargs):
9737
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.ERROR, exc_info=exc_info, stack_offset=1), *args, **kwargs) # noqa
10406
+ return self._log(
10407
+ CaptureLoggingContextImpl(
10408
+ NamedLogLevel.ERROR,
10409
+ exc_info=exc_info,
10410
+ stack_offset=self._level_proxy_method_stack_offset,
10411
+ ),
10412
+ *args,
10413
+ **kwargs,
10414
+ )
9738
10415
 
9739
10416
  #
9740
10417
 
@@ -9752,24 +10429,53 @@ class AnyLogger(Abstract, ta.Generic[T]):
9752
10429
 
9753
10430
  @ta.final
9754
10431
  def critical(self, *args, **kwargs):
9755
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.CRITICAL, stack_offset=1), *args, **kwargs)
10432
+ return self._log(
10433
+ CaptureLoggingContextImpl(
10434
+ NamedLogLevel.CRITICAL,
10435
+ stack_offset=self._level_proxy_method_stack_offset,
10436
+ ),
10437
+ *args,
10438
+ **kwargs,
10439
+ )
9756
10440
 
9757
10441
  ##
9758
10442
 
9759
10443
  @abc.abstractmethod
9760
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> T: # noqa
10444
+ def _log(
10445
+ self,
10446
+ ctx: CaptureLoggingContext,
10447
+ msg: ta.Union[str, tuple, LoggingMsgFn],
10448
+ *args: ta.Any,
10449
+ **kwargs: ta.Any,
10450
+ ) -> T:
9761
10451
  raise NotImplementedError
9762
10452
 
9763
10453
 
9764
10454
  class Logger(AnyLogger[None], Abstract):
10455
+ _level_proxy_method_stack_offset: int = 1
10456
+
9765
10457
  @abc.abstractmethod
9766
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
10458
+ def _log(
10459
+ self,
10460
+ ctx: CaptureLoggingContext,
10461
+ msg: ta.Union[str, tuple, LoggingMsgFn],
10462
+ *args: ta.Any,
10463
+ **kwargs: ta.Any,
10464
+ ) -> None:
9767
10465
  raise NotImplementedError
9768
10466
 
9769
10467
 
9770
10468
  class AsyncLogger(AnyLogger[ta.Awaitable[None]], Abstract):
10469
+ _level_proxy_method_stack_offset: int = 0
10470
+
9771
10471
  @abc.abstractmethod
9772
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> ta.Awaitable[None]: # noqa
10472
+ def _log(
10473
+ self,
10474
+ ctx: CaptureLoggingContext,
10475
+ msg: ta.Union[str, tuple, LoggingMsgFn],
10476
+ *args: ta.Any,
10477
+ **kwargs: ta.Any,
10478
+ ) -> ta.Awaitable[None]:
9773
10479
  raise NotImplementedError
9774
10480
 
9775
10481
 
@@ -9784,13 +10490,25 @@ class AnyNopLogger(AnyLogger[T], Abstract):
9784
10490
 
9785
10491
  @ta.final
9786
10492
  class NopLogger(AnyNopLogger[None], Logger):
9787
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
10493
+ def _log(
10494
+ self,
10495
+ ctx: CaptureLoggingContext,
10496
+ msg: ta.Union[str, tuple, LoggingMsgFn],
10497
+ *args: ta.Any,
10498
+ **kwargs: ta.Any,
10499
+ ) -> None:
9788
10500
  pass
9789
10501
 
9790
10502
 
9791
10503
  @ta.final
9792
10504
  class AsyncNopLogger(AnyNopLogger[ta.Awaitable[None]], AsyncLogger):
9793
- async def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
10505
+ async def _log(
10506
+ self,
10507
+ ctx: CaptureLoggingContext,
10508
+ msg: ta.Union[str, tuple, LoggingMsgFn],
10509
+ *args: ta.Any,
10510
+ **kwargs: ta.Any,
10511
+ ) -> None:
9794
10512
  pass
9795
10513
 
9796
10514
 
@@ -10603,7 +11321,7 @@ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
10603
11321
  *,
10604
11322
  read_size: int = 0x10000,
10605
11323
  write_size: int = 0x10000,
10606
- log_handler: ta.Optional[ta.Callable[[CoroHttpServer, CoroHttpServer.AnyLogIo], None]] = None,
11324
+ log_handler: ta.Optional[ta.Callable[[CoroHttpServer, CoroHttpIo.AnyLogIo], None]] = None,
10607
11325
  ) -> None:
10608
11326
  check.state(not sock.getblocking())
10609
11327
 
@@ -10623,13 +11341,13 @@ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
10623
11341
  )
10624
11342
  self._srv_coro: ta.Optional[
10625
11343
  ta.Generator[
10626
- CoroHttpServer.Io,
11344
+ CoroHttpIo.Io,
10627
11345
  ta.Optional[bytes],
10628
11346
  CoroHttpServer.CoroHandleResult,
10629
11347
  ],
10630
11348
  ] = self._coro_srv.coro_handle()
10631
11349
 
10632
- self._cur_io: ta.Optional[CoroHttpServer.Io] = None
11350
+ self._cur_io: ta.Optional[CoroHttpIo.Io] = None
10633
11351
  self._next_io()
10634
11352
 
10635
11353
  #
@@ -10652,22 +11370,22 @@ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
10652
11370
  o = None
10653
11371
  break
10654
11372
 
10655
- if isinstance(o, CoroHttpServer.AnyLogIo):
11373
+ if isinstance(o, CoroHttpIo.AnyLogIo):
10656
11374
  if self._log_handler is not None:
10657
11375
  self._log_handler(self._coro_srv, o)
10658
11376
  o = None
10659
11377
 
10660
- elif isinstance(o, CoroHttpServer.ReadIo):
11378
+ elif isinstance(o, CoroHttpIo.ReadIo):
10661
11379
  if (d := self._read_buf.read(o.sz)) is None:
10662
11380
  break
10663
11381
  o = None
10664
11382
 
10665
- elif isinstance(o, CoroHttpServer.ReadLineIo):
11383
+ elif isinstance(o, CoroHttpIo.ReadLineIo):
10666
11384
  if (d := self._read_buf.read_until(b'\n')) is None:
10667
11385
  break
10668
11386
  o = None
10669
11387
 
10670
- elif isinstance(o, CoroHttpServer.WriteIo):
11388
+ elif isinstance(o, CoroHttpIo.WriteIo):
10671
11389
  check.none(self._write_buf)
10672
11390
  self._write_buf = IncrementalWriteBuffer(o.data, write_size=self._write_size)
10673
11391
  break
@@ -10701,11 +11419,11 @@ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
10701
11419
 
10702
11420
  self._read_buf.feed(buf)
10703
11421
 
10704
- if isinstance(self._cur_io, CoroHttpServer.AnyReadIo):
11422
+ if isinstance(self._cur_io, CoroHttpIo.AnyReadIo):
10705
11423
  self._next_io()
10706
11424
 
10707
11425
  def on_writable(self) -> None:
10708
- check.isinstance(self._cur_io, CoroHttpServer.WriteIo)
11426
+ check.isinstance(self._cur_io, CoroHttpIo.WriteIo)
10709
11427
  wb = check.not_none(self._write_buf)
10710
11428
  while wb.rem > 0:
10711
11429
  def send(d: bytes) -> int:
@@ -10725,6 +11443,70 @@ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
10725
11443
  self._next_io()
10726
11444
 
10727
11445
 
11446
+ ########################################
11447
+ # ../../../omlish/logs/asyncs.py
11448
+
11449
+
11450
+ ##
11451
+
11452
+
11453
+ class AsyncLoggerToLogger(Logger):
11454
+ def __init__(self, u: AsyncLogger) -> None:
11455
+ super().__init__()
11456
+
11457
+ self._u = u
11458
+
11459
+ def get_effective_level(self) -> LogLevel:
11460
+ return self._u.get_effective_level()
11461
+
11462
+ def _log(
11463
+ self,
11464
+ ctx: CaptureLoggingContext,
11465
+ msg: ta.Union[str, tuple, LoggingMsgFn],
11466
+ *args: ta.Any,
11467
+ **kwargs: ta.Any,
11468
+ ) -> None:
11469
+ # Nope out early to avoid sync_await if possible - don't bother in the LoggerToAsyncLogger.
11470
+ if not self.is_enabled_for(ctx.must_get_info(LoggingContextInfos.Level).level):
11471
+ return
11472
+
11473
+ # Note: we hardcode the stack offset of sync_await (which is 2 - sync_await + sync_await.thunk). In non-lite
11474
+ # code, lang.sync_await uses a cext if present to avoid being on the py stack, which would obviously complicate
11475
+ # this, but this is lite code so we will always have the non-c version.
11476
+ sync_await(
11477
+ self._u._log( # noqa
11478
+ check.isinstance(ctx, CaptureLoggingContextImpl).inc_stack_offset(3),
11479
+ msg,
11480
+ *args,
11481
+ **kwargs,
11482
+ ),
11483
+ )
11484
+
11485
+
11486
+ class LoggerToAsyncLogger(AsyncLogger):
11487
+ def __init__(self, u: Logger) -> None:
11488
+ super().__init__()
11489
+
11490
+ self._u = u
11491
+
11492
+ def get_effective_level(self) -> LogLevel:
11493
+ return self._u.get_effective_level()
11494
+
11495
+ async def _log(
11496
+ self,
11497
+ ctx: CaptureLoggingContext,
11498
+ msg: ta.Union[str, tuple, LoggingMsgFn],
11499
+ *args: ta.Any,
11500
+ **kwargs: ta.Any,
11501
+ ) -> None:
11502
+ return self._u._log( # noqa
11503
+ check.isinstance(ctx, CaptureLoggingContextImpl).inc_stack_offset(),
11504
+ msg,
11505
+ *args,
11506
+ **kwargs,
11507
+ )
11508
+
11509
+
10728
11510
  ########################################
10729
11511
  # ../../../omlish/logs/std/loggers.py
10730
11512
 
@@ -10748,7 +11530,12 @@ class StdLogger(Logger):
10748
11530
  def get_effective_level(self) -> LogLevel:
10749
11531
  return self._std.getEffectiveLevel()
10750
11532
 
10751
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> None:
11533
+ def _log(
11534
+ self,
11535
+ ctx: CaptureLoggingContext,
11536
+ msg: ta.Union[str, tuple, LoggingMsgFn],
11537
+ *args: ta.Any,
11538
+ ) -> None:
10752
11539
  if not self.is_enabled_for(ctx.must_get_info(LoggingContextInfos.Level).level):
10753
11540
  return
10754
11541
 
@@ -10893,8 +11680,23 @@ class ProcessSpawning:
10893
11680
  ##
10894
11681
 
10895
11682
 
11683
+ def _get_module_std_logger(mod_globals: ta.Mapping[str, ta.Any]) -> logging.Logger:
11684
+ return logging.getLogger(mod_globals.get('__name__'))
11685
+
11686
+
10896
11687
  def get_module_logger(mod_globals: ta.Mapping[str, ta.Any]) -> Logger:
10897
- return StdLogger(logging.getLogger(mod_globals.get('__name__'))) # noqa
11688
+ return StdLogger(_get_module_std_logger(mod_globals))
11689
+
11690
+
11691
+ def get_module_async_logger(mod_globals: ta.Mapping[str, ta.Any]) -> AsyncLogger:
11692
+ return LoggerToAsyncLogger(get_module_logger(mod_globals))
11693
+
11694
+
11695
+ def get_module_loggers(mod_globals: ta.Mapping[str, ta.Any]) -> ta.Tuple[Logger, AsyncLogger]:
11696
+ return (
11697
+ log := get_module_logger(mod_globals),
11698
+ LoggerToAsyncLogger(log),
11699
+ )
10898
11700
 
10899
11701
 
10900
11702
  ########################################