omlish 0.0.0.dev240__py3-none-any.whl → 0.0.0.dev241__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.
- omlish/__about__.py +2 -2
- omlish/asyncs/asyncio/all.py +6 -1
- omlish/asyncs/asyncio/asyncio.py +53 -16
- omlish/docker/all.py +1 -1
- omlish/docker/ns1.py +98 -0
- omlish/docker/{portrelay.py → ports.py} +22 -2
- omlish/docker/timebomb.py +6 -2
- omlish/http/coro/simple.py +11 -0
- omlish/lite/inject.py +21 -12
- omlish/marshal/base.py +1 -0
- omlish/metadata.py +11 -9
- omlish/os/mangle.py +4 -0
- omlish/sockets/bind.py +11 -11
- omlish/sockets/ports.py +62 -0
- omlish/sockets/server/handlers.py +1 -1
- omlish/sockets/server/ssl.py +39 -0
- omlish/sql/abc.py +86 -0
- omlish/sql/api/base.py +3 -3
- omlish/sql/dbapi.py +0 -6
- omlish/testing/pytest/skip.py +4 -0
- omlish/text/mangle.py +38 -0
- {omlish-0.0.0.dev240.dist-info → omlish-0.0.0.dev241.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev240.dist-info → omlish-0.0.0.dev241.dist-info}/RECORD +27 -23
- {omlish-0.0.0.dev240.dist-info → omlish-0.0.0.dev241.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev240.dist-info → omlish-0.0.0.dev241.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev240.dist-info → omlish-0.0.0.dev241.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev240.dist-info → omlish-0.0.0.dev241.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/asyncs/asyncio/all.py
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
# ruff: noqa: I001
|
2
2
|
from .asyncio import ( # noqa
|
3
|
+
asyncio_ensure_task as ensure_task,
|
4
|
+
|
3
5
|
asyncio_once as once,
|
4
|
-
|
6
|
+
|
5
7
|
drain_asyncio_tasks as drain_tasks,
|
6
8
|
draining_asyncio_tasks as draining_tasks,
|
9
|
+
|
10
|
+
asyncio_wait_concurrent as wait_concurrent,
|
11
|
+
asyncio_wait_maybe_concurrent as wait_maybe_concurrent,
|
7
12
|
)
|
omlish/asyncs/asyncio/asyncio.py
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
# @omlish-lite
|
3
|
+
"""
|
4
|
+
TODO:
|
5
|
+
- split module
|
6
|
+
"""
|
3
7
|
import asyncio
|
4
8
|
import contextlib
|
5
9
|
import functools
|
@@ -11,19 +15,37 @@ T = ta.TypeVar('T')
|
|
11
15
|
CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
|
12
16
|
|
13
17
|
|
18
|
+
##
|
19
|
+
|
20
|
+
|
21
|
+
def asyncio_ensure_task(obj: ta.Awaitable) -> asyncio.Task:
|
22
|
+
if isinstance(obj, asyncio.Task):
|
23
|
+
return obj
|
24
|
+
elif isinstance(obj, ta.Coroutine):
|
25
|
+
return asyncio.create_task(obj)
|
26
|
+
else:
|
27
|
+
raise TypeError(obj)
|
28
|
+
|
29
|
+
|
30
|
+
##
|
31
|
+
|
32
|
+
|
14
33
|
def asyncio_once(fn: CallableT) -> CallableT:
|
15
|
-
|
34
|
+
task = None
|
16
35
|
|
17
36
|
@functools.wraps(fn)
|
18
37
|
async def inner(*args, **kwargs):
|
19
|
-
nonlocal
|
20
|
-
if not
|
21
|
-
|
22
|
-
return await
|
38
|
+
nonlocal task
|
39
|
+
if not task:
|
40
|
+
task = asyncio.create_task(fn(*args, **kwargs))
|
41
|
+
return await task
|
23
42
|
|
24
43
|
return ta.cast(CallableT, inner)
|
25
44
|
|
26
45
|
|
46
|
+
##
|
47
|
+
|
48
|
+
|
27
49
|
def drain_asyncio_tasks(loop=None):
|
28
50
|
if loop is None:
|
29
51
|
loop = asyncio.get_running_loop()
|
@@ -42,8 +64,11 @@ def draining_asyncio_tasks() -> ta.Iterator[None]:
|
|
42
64
|
drain_asyncio_tasks(loop) # noqa
|
43
65
|
|
44
66
|
|
67
|
+
##
|
68
|
+
|
69
|
+
|
45
70
|
async def asyncio_wait_concurrent(
|
46
|
-
|
71
|
+
awaitables: ta.Iterable[ta.Awaitable[T]],
|
47
72
|
concurrency: ta.Union[int, asyncio.Semaphore],
|
48
73
|
*,
|
49
74
|
return_when: ta.Any = asyncio.FIRST_EXCEPTION,
|
@@ -55,18 +80,30 @@ async def asyncio_wait_concurrent(
|
|
55
80
|
else:
|
56
81
|
raise TypeError(concurrency)
|
57
82
|
|
58
|
-
async def limited_task(
|
83
|
+
async def limited_task(a):
|
59
84
|
async with semaphore:
|
60
|
-
return await
|
85
|
+
return await a
|
86
|
+
|
87
|
+
futs = [asyncio.create_task(limited_task(a)) for a in awaitables]
|
88
|
+
done, pending = await asyncio.wait(futs, return_when=return_when)
|
61
89
|
|
62
|
-
|
63
|
-
|
90
|
+
for fut in pending:
|
91
|
+
fut.cancel()
|
64
92
|
|
65
|
-
for
|
66
|
-
|
93
|
+
for fut in done:
|
94
|
+
if fut.exception():
|
95
|
+
raise fut.exception() # type: ignore
|
67
96
|
|
68
|
-
for
|
69
|
-
if task.exception():
|
70
|
-
raise task.exception() # type: ignore
|
97
|
+
return [fut.result() for fut in done]
|
71
98
|
|
72
|
-
|
99
|
+
|
100
|
+
async def asyncio_wait_maybe_concurrent(
|
101
|
+
awaitables: ta.Iterable[ta.Awaitable[T]],
|
102
|
+
concurrency: ta.Union[int, asyncio.Semaphore, None],
|
103
|
+
) -> ta.List[T]:
|
104
|
+
# Note: Only supports return_when=asyncio.FIRST_EXCEPTION
|
105
|
+
if concurrency is None:
|
106
|
+
return [await a for a in awaitables]
|
107
|
+
|
108
|
+
else:
|
109
|
+
return await asyncio_wait_concurrent(awaitables, concurrency)
|
omlish/docker/all.py
CHANGED
omlish/docker/ns1.py
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import dataclasses as dc
|
4
|
+
import re
|
5
|
+
import typing as ta
|
6
|
+
|
7
|
+
from ..lite.check import check
|
8
|
+
from ..subprocesses.run import SubprocessRun
|
9
|
+
from ..subprocesses.run import SubprocessRunnable
|
10
|
+
from ..subprocesses.run import SubprocessRunOutput
|
11
|
+
|
12
|
+
|
13
|
+
##
|
14
|
+
|
15
|
+
|
16
|
+
DEFAULT_DOCKER_NS1_RUN_IMAGE: str = 'debian'
|
17
|
+
|
18
|
+
|
19
|
+
def build_docker_ns1_run_args(
|
20
|
+
*cmd: str,
|
21
|
+
image: ta.Optional[str] = None,
|
22
|
+
nsenter: str = 'nsenter',
|
23
|
+
) -> ta.List[str]:
|
24
|
+
"""
|
25
|
+
- https://gist.github.com/BretFisher/5e1a0c7bcca4c735e716abf62afad389
|
26
|
+
- https://github.com/justincormack/nsenter1/blob/8d3ba504b2c14d73c70cf34f1ec6943c093f1b02/nsenter1.c
|
27
|
+
|
28
|
+
alt:
|
29
|
+
- nc -U ~/Library/Containers/com.docker.docker/Data/debug-shell.sock
|
30
|
+
"""
|
31
|
+
|
32
|
+
return [
|
33
|
+
'--privileged',
|
34
|
+
'--pid=host',
|
35
|
+
(image if image is not None else DEFAULT_DOCKER_NS1_RUN_IMAGE),
|
36
|
+
|
37
|
+
nsenter,
|
38
|
+
'-t', '1',
|
39
|
+
|
40
|
+
'-m', # mount
|
41
|
+
'-u', # uts
|
42
|
+
'-i', # ipc
|
43
|
+
'-n', # net
|
44
|
+
'-p', # pid
|
45
|
+
'-C', # cgroup
|
46
|
+
# '-U', # user
|
47
|
+
'-T', # time
|
48
|
+
|
49
|
+
*cmd,
|
50
|
+
]
|
51
|
+
|
52
|
+
|
53
|
+
def build_docker_ns1_run_cmd(
|
54
|
+
*cmd: str,
|
55
|
+
exe: str = 'docker',
|
56
|
+
run_args: ta.Optional[ta.Sequence[str]] = None,
|
57
|
+
**kwargs: ta.Any,
|
58
|
+
) -> ta.List[str]:
|
59
|
+
if run_args is not None:
|
60
|
+
check.not_isinstance(run_args, str)
|
61
|
+
|
62
|
+
return [
|
63
|
+
exe,
|
64
|
+
'run',
|
65
|
+
'--rm',
|
66
|
+
*(run_args if run_args is not None else []),
|
67
|
+
'-i',
|
68
|
+
*build_docker_ns1_run_args(*cmd, **kwargs),
|
69
|
+
]
|
70
|
+
|
71
|
+
|
72
|
+
##
|
73
|
+
|
74
|
+
|
75
|
+
@dc.dataclass(frozen=True)
|
76
|
+
class DockerNs1ListUsedTcpPortsCommand(SubprocessRunnable[ta.List[int]]):
|
77
|
+
kwargs: ta.Optional[ta.Mapping[str, str]] = None
|
78
|
+
|
79
|
+
def make_run(self) -> SubprocessRun:
|
80
|
+
return SubprocessRun.of(
|
81
|
+
*build_docker_ns1_run_cmd(
|
82
|
+
'netstat', '-tan',
|
83
|
+
),
|
84
|
+
stdout='pipe',
|
85
|
+
stderr='devnull',
|
86
|
+
check=True,
|
87
|
+
)
|
88
|
+
|
89
|
+
_NETSTAT_LINE_PAT: ta.ClassVar[re.Pattern] = re.compile(r'\d{1,3}(.(\d{1,3})){3}:(?P<port>\d+)')
|
90
|
+
|
91
|
+
def handle_run_output(self, output: SubprocessRunOutput) -> ta.List[int]:
|
92
|
+
lines = [s for l in check.not_none(output.stdout).decode().splitlines() if (s := l.strip())]
|
93
|
+
return [
|
94
|
+
int(m.groupdict()['port'])
|
95
|
+
for l in lines
|
96
|
+
if len(ps := l.split(maxsplit=4)) > 3
|
97
|
+
if (m := self._NETSTAT_LINE_PAT.fullmatch(ps[3])) is not None
|
98
|
+
]
|
@@ -1,13 +1,33 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
# @omlish-lite
|
3
|
+
"""
|
4
|
+
TODO:
|
5
|
+
- docstring
|
6
|
+
- timebomb
|
7
|
+
- auto-discover available ports
|
8
|
+
"""
|
3
9
|
import dataclasses as dc
|
4
10
|
import os
|
5
11
|
import typing as ta
|
6
12
|
|
7
13
|
|
14
|
+
##
|
15
|
+
|
16
|
+
|
8
17
|
@dc.dataclass(frozen=True)
|
9
18
|
class DockerPortRelay:
|
10
|
-
|
19
|
+
"""
|
20
|
+
Uses roughly the following command to forward connections from inside docker-for-mac's vm to the mac host:
|
21
|
+
|
22
|
+
docker run --rm -i -p 5001:5000 alpine/socat -d -d TCP-LISTEN:5000,fork,reuseaddr TCP:host.docker.internal:5021
|
23
|
+
|
24
|
+
This allows requests made by the docker daemon running inside the vm to `host.docker.internal:5001` to be forwarded
|
25
|
+
to the mac host on port 5021. The reason for this is to be able to use a docker registry running locally directly on
|
26
|
+
the host mac - specifically to be able to do so with ssl certificate checking disabled (which docker will only do on
|
27
|
+
localhost, which on a mac in the vm isn't actually the mac host - hence the necessity of the relay).
|
28
|
+
"""
|
29
|
+
|
30
|
+
docker_port: int # port
|
11
31
|
host_port: int
|
12
32
|
|
13
33
|
name: ta.Optional[str] = None
|
@@ -30,7 +50,7 @@ class DockerPortRelay:
|
|
30
50
|
|
31
51
|
def run_args(self) -> ta.List[str]:
|
32
52
|
if (name := self.name) is None:
|
33
|
-
name = f'docker_port_relay-{os.getpid()}'
|
53
|
+
name = f'docker_port_relay-{os.getpid()}-{self.docker_port}-{self.intermediate_port}-{self.host_port}'
|
34
54
|
|
35
55
|
return [
|
36
56
|
'--name', name,
|
omlish/docker/timebomb.py
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
+
# @omlish-lite
|
1
2
|
import shlex
|
2
3
|
|
3
4
|
|
4
|
-
|
5
|
+
##
|
5
6
|
|
6
7
|
|
7
|
-
|
8
|
+
_DEFAULT_DOCKER_TIMEBOMB_NAME = 'omlish-timebomb'
|
9
|
+
|
10
|
+
|
11
|
+
def docker_timebomb_payload(delay_s: float, name: str = _DEFAULT_DOCKER_TIMEBOMB_NAME) -> str:
|
8
12
|
return (
|
9
13
|
'('
|
10
14
|
f'echo {shlex.quote(name)} && '
|
omlish/http/coro/simple.py
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
# @omlish-lite
|
3
|
+
"""
|
4
|
+
TODO:
|
5
|
+
- logging
|
6
|
+
"""
|
3
7
|
import concurrent.futures as cf
|
4
8
|
import contextlib
|
5
9
|
import functools
|
@@ -15,6 +19,7 @@ from ...sockets.server.handlers import SocketServerHandler
|
|
15
19
|
from ...sockets.server.handlers import SocketWrappingSocketServerHandler
|
16
20
|
from ...sockets.server.handlers import StandardSocketServerHandler
|
17
21
|
from ...sockets.server.server import SocketServer
|
22
|
+
from ...sockets.server.ssl import SslErrorHandlingSocketServerHandler
|
18
23
|
from ...sockets.server.threading import ThreadingSocketServerHandler
|
19
24
|
from ..handlers import HttpHandler
|
20
25
|
from ..parsing import HttpRequestParser
|
@@ -35,6 +40,7 @@ def make_simple_http_server(
|
|
35
40
|
*,
|
36
41
|
server_version: HttpProtocolVersion = HttpProtocolVersions.HTTP_1_1,
|
37
42
|
ssl_context: ta.Optional['ssl.SSLContext'] = None,
|
43
|
+
ignore_ssl_errors: bool = False,
|
38
44
|
executor: ta.Optional[cf.Executor] = None,
|
39
45
|
use_threads: bool = False,
|
40
46
|
) -> ta.Iterator[SocketServer]:
|
@@ -72,6 +78,11 @@ def make_simple_http_server(
|
|
72
78
|
)),
|
73
79
|
)
|
74
80
|
|
81
|
+
if ignore_ssl_errors:
|
82
|
+
server_handler = SslErrorHandlingSocketServerHandler(
|
83
|
+
server_handler,
|
84
|
+
)
|
85
|
+
|
75
86
|
#
|
76
87
|
|
77
88
|
server_handler = StandardSocketServerHandler(
|
omlish/lite/inject.py
CHANGED
@@ -510,14 +510,6 @@ _INJECTION_INSPECTION_CACHE: ta.MutableMapping[ta.Any, _InjectionInspection] = w
|
|
510
510
|
|
511
511
|
def _do_injection_inspect(obj: ta.Any) -> _InjectionInspection:
|
512
512
|
tgt = obj
|
513
|
-
if isinstance(tgt, type) and tgt.__init__ is not object.__init__: # type: ignore[misc]
|
514
|
-
# Python 3.8's inspect.signature can't handle subclasses overriding __new__, always generating *args/**kwargs.
|
515
|
-
# - https://bugs.python.org/issue40897
|
516
|
-
# - https://github.com/python/cpython/commit/df7c62980d15acd3125dfbd81546dad359f7add7
|
517
|
-
tgt = tgt.__init__ # type: ignore[misc]
|
518
|
-
has_generic_base = True
|
519
|
-
else:
|
520
|
-
has_generic_base = False
|
521
513
|
|
522
514
|
# inspect.signature(eval_str=True) was added in 3.10 and we have to support 3.8, so we have to get_type_hints to
|
523
515
|
# eval str annotations *in addition to* getting the signature for parameter information.
|
@@ -525,23 +517,40 @@ def _do_injection_inspect(obj: ta.Any) -> _InjectionInspection:
|
|
525
517
|
has_partial = False
|
526
518
|
while True:
|
527
519
|
if isinstance(uw, functools.partial):
|
528
|
-
has_partial = True
|
529
520
|
uw = uw.func
|
521
|
+
has_partial = True
|
530
522
|
else:
|
531
523
|
if (uw2 := inspect.unwrap(uw)) is uw:
|
532
524
|
break
|
533
525
|
uw = uw2
|
534
526
|
|
535
|
-
|
527
|
+
has_args_offset = False
|
528
|
+
|
529
|
+
if isinstance(tgt, type) and tgt.__new__ is not object.__new__:
|
530
|
+
# Python 3.8's inspect.signature can't handle subclasses overriding __new__, always generating *args/**kwargs.
|
531
|
+
# - https://bugs.python.org/issue40897
|
532
|
+
# - https://github.com/python/cpython/commit/df7c62980d15acd3125dfbd81546dad359f7add7
|
533
|
+
tgt = tgt.__init__ # type: ignore[misc]
|
534
|
+
has_args_offset = True
|
535
|
+
|
536
|
+
if tgt in (object.__init__, object.__new__):
|
537
|
+
# inspect strips self for types but not the underlying methods.
|
538
|
+
def dummy(self):
|
539
|
+
pass
|
540
|
+
tgt = dummy
|
541
|
+
has_args_offset = True
|
542
|
+
|
543
|
+
if has_partial and has_args_offset:
|
544
|
+
# TODO: unwrap partials masking parameters like modern python
|
536
545
|
raise InjectorError(
|
537
|
-
'Injector inspection does not currently support both
|
546
|
+
'Injector inspection does not currently support both an args offset and a functools.partial: '
|
538
547
|
f'{obj}',
|
539
548
|
)
|
540
549
|
|
541
550
|
return _InjectionInspection(
|
542
551
|
inspect.signature(tgt),
|
543
552
|
ta.get_type_hints(uw),
|
544
|
-
1 if
|
553
|
+
1 if has_args_offset else 0,
|
545
554
|
)
|
546
555
|
|
547
556
|
|
omlish/marshal/base.py
CHANGED
omlish/metadata.py
CHANGED
@@ -8,6 +8,7 @@ TODO:
|
|
8
8
|
- merge mro?
|
9
9
|
- are these better left up to callers? too usecase-specific to favor either way?
|
10
10
|
"""
|
11
|
+
import threading
|
11
12
|
import types
|
12
13
|
import typing as ta
|
13
14
|
|
@@ -60,6 +61,8 @@ def _unwrap_object_metadata_target(obj: ta.Any) -> ta.Any:
|
|
60
61
|
##
|
61
62
|
|
62
63
|
|
64
|
+
_OBJECT_METADATA_LOCK = threading.RLock()
|
65
|
+
|
63
66
|
_OBJECT_METADATA_ATTR = '__' + __name__.replace('.', '_') + '__metadata__'
|
64
67
|
|
65
68
|
|
@@ -71,15 +74,14 @@ def append_object_metadata(obj: T, *mds: ObjectMetadata) -> T:
|
|
71
74
|
dct = tgt.__dict__
|
72
75
|
|
73
76
|
if isinstance(dct, types.MappingProxyType):
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
raise RuntimeError
|
77
|
+
try:
|
78
|
+
lst = dct[_OBJECT_METADATA_ATTR]
|
79
|
+
except KeyError:
|
80
|
+
with _OBJECT_METADATA_LOCK:
|
81
|
+
try:
|
82
|
+
lst = dct[_OBJECT_METADATA_ATTR]
|
83
|
+
except KeyError:
|
84
|
+
setattr(tgt, _OBJECT_METADATA_ATTR, lst := [])
|
83
85
|
|
84
86
|
else:
|
85
87
|
lst = dct.setdefault(_OBJECT_METADATA_ATTR, [])
|
omlish/os/mangle.py
CHANGED
omlish/sockets/bind.py
CHANGED
@@ -175,11 +175,11 @@ class SocketBinder(abc.ABC, ta.Generic[SocketBinderConfigT]):
|
|
175
175
|
if hasattr(self, '_socket'):
|
176
176
|
raise self.AlreadyBoundError
|
177
177
|
|
178
|
-
|
179
|
-
self._socket =
|
178
|
+
sock = socket_.socket(self.address_family, socket_.SOCK_STREAM)
|
179
|
+
self._socket = sock
|
180
180
|
|
181
181
|
if self._config.allow_reuse_address and hasattr(socket_, 'SO_REUSEADDR'):
|
182
|
-
|
182
|
+
sock.setsockopt(socket_.SOL_SOCKET, socket_.SO_REUSEADDR, 1)
|
183
183
|
|
184
184
|
# Since Linux 6.12.9, SO_REUSEPORT is not allowed on other address families than AF_INET/AF_INET6.
|
185
185
|
if (
|
@@ -187,13 +187,13 @@ class SocketBinder(abc.ABC, ta.Generic[SocketBinderConfigT]):
|
|
187
187
|
self.address_family in (socket_.AF_INET, socket_.AF_INET6)
|
188
188
|
):
|
189
189
|
try:
|
190
|
-
|
190
|
+
sock.setsockopt(socket_.SOL_SOCKET, socket_.SO_REUSEPORT, 1)
|
191
191
|
except OSError as err:
|
192
192
|
if err.errno not in (errno.ENOPROTOOPT, errno.EINVAL):
|
193
193
|
raise
|
194
194
|
|
195
|
-
if self._config.set_inheritable and hasattr(
|
196
|
-
|
195
|
+
if self._config.set_inheritable and hasattr(sock, 'set_inheritable'):
|
196
|
+
sock.set_inheritable(True)
|
197
197
|
|
198
198
|
def _pre_bind(self) -> None:
|
199
199
|
pass
|
@@ -224,7 +224,7 @@ class SocketBinder(abc.ABC, ta.Generic[SocketBinderConfigT]):
|
|
224
224
|
self.socket.listen(self._config.listen_backlog)
|
225
225
|
|
226
226
|
@abc.abstractmethod
|
227
|
-
def accept(self,
|
227
|
+
def accept(self, sock: ta.Optional[socket_.socket] = None) -> SocketAndAddress:
|
228
228
|
raise NotImplementedError
|
229
229
|
|
230
230
|
|
@@ -270,11 +270,11 @@ class TcpSocketBinder(SocketBinder):
|
|
270
270
|
|
271
271
|
#
|
272
272
|
|
273
|
-
def accept(self,
|
274
|
-
if
|
275
|
-
|
273
|
+
def accept(self, sock: ta.Optional[socket_.socket] = None) -> SocketAndAddress:
|
274
|
+
if sock is None:
|
275
|
+
sock = self.socket
|
276
276
|
|
277
|
-
conn, client_address =
|
277
|
+
conn, client_address = sock.accept()
|
278
278
|
return SocketAndAddress(conn, client_address)
|
279
279
|
|
280
280
|
|
omlish/sockets/ports.py
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import contextlib
|
4
|
+
import socket
|
5
|
+
import typing as ta
|
6
|
+
|
7
|
+
from ..lite.check import check
|
8
|
+
from ..lite.timeouts import Timeout
|
9
|
+
from ..lite.timeouts import TimeoutLike
|
10
|
+
|
11
|
+
|
12
|
+
##
|
13
|
+
|
14
|
+
|
15
|
+
DEFAULT_AVAILABLE_PORT_HOST: str = '127.0.0.1'
|
16
|
+
|
17
|
+
|
18
|
+
@contextlib.contextmanager
|
19
|
+
def get_available_port_context(host: ta.Optional[str] = None) -> ta.Iterator[int]:
|
20
|
+
if host is None:
|
21
|
+
host = DEFAULT_AVAILABLE_PORT_HOST
|
22
|
+
|
23
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
24
|
+
sock.bind((host, 0))
|
25
|
+
sock.listen(1)
|
26
|
+
port = sock.getsockname()[1]
|
27
|
+
yield port
|
28
|
+
|
29
|
+
|
30
|
+
def get_available_port(host: ta.Optional[str] = None) -> int:
|
31
|
+
with get_available_port_context(host) as port:
|
32
|
+
pass
|
33
|
+
return port
|
34
|
+
|
35
|
+
|
36
|
+
def get_available_ports(
|
37
|
+
n: int,
|
38
|
+
*,
|
39
|
+
host: ta.Optional[str] = None,
|
40
|
+
exclude: ta.Optional[ta.Iterable[int]] = None,
|
41
|
+
timeout: ta.Optional[TimeoutLike] = None,
|
42
|
+
) -> ta.List[int]:
|
43
|
+
exclude = set(exclude or [])
|
44
|
+
|
45
|
+
seen: ta.Set[int] = set()
|
46
|
+
ret: ta.List[int] = []
|
47
|
+
|
48
|
+
timeout = Timeout.of(timeout)
|
49
|
+
|
50
|
+
with contextlib.ExitStack() as es:
|
51
|
+
while len(ret) < n:
|
52
|
+
timeout()
|
53
|
+
|
54
|
+
cur = es.enter_context(get_available_port_context(host))
|
55
|
+
|
56
|
+
check.not_in(cur, seen)
|
57
|
+
seen.add(cur)
|
58
|
+
|
59
|
+
if cur not in exclude:
|
60
|
+
ret.append(cur)
|
61
|
+
|
62
|
+
return ret
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import dataclasses as dc
|
4
|
+
import logging
|
5
|
+
import typing as ta
|
6
|
+
|
7
|
+
from ..addresses import SocketAndAddress
|
8
|
+
from ..io import close_socket_immediately
|
9
|
+
from .handlers import SocketServerHandler
|
10
|
+
from .handlers import SocketServerHandler_
|
11
|
+
|
12
|
+
|
13
|
+
##
|
14
|
+
|
15
|
+
|
16
|
+
@dc.dataclass(frozen=True)
|
17
|
+
class SslErrorHandlingSocketServerHandler(SocketServerHandler_):
|
18
|
+
handler: SocketServerHandler
|
19
|
+
|
20
|
+
log: ta.Optional[logging.Logger] = None
|
21
|
+
|
22
|
+
#
|
23
|
+
|
24
|
+
_error_cls: ta.ClassVar[ta.Optional[ta.Type[BaseException]]] = None
|
25
|
+
|
26
|
+
@classmethod
|
27
|
+
def _get_error_cls(cls) -> ta.Type[BaseException]:
|
28
|
+
if (error_cls := cls._error_cls) is None:
|
29
|
+
import ssl
|
30
|
+
error_cls = cls._error_cls = ssl.SSLError
|
31
|
+
return error_cls
|
32
|
+
|
33
|
+
def __call__(self, conn: SocketAndAddress) -> None:
|
34
|
+
try:
|
35
|
+
self.handler(conn)
|
36
|
+
except self._get_error_cls(): # noqa
|
37
|
+
if (log := self.log) is not None:
|
38
|
+
log.exception('SSL Error in connection %r', conn)
|
39
|
+
close_socket_immediately(conn.socket)
|
omlish/sql/abc.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
https://peps.python.org/pep-0249/
|
3
3
|
"""
|
4
|
+
import enum
|
4
5
|
import typing as ta
|
5
6
|
|
6
7
|
|
@@ -83,3 +84,88 @@ class DbapiCursor(ta.Protocol):
|
|
83
84
|
def setinputsizes(self, sizes: ta.Sequence[DbapiTypeCode | int | None]) -> object: ...
|
84
85
|
|
85
86
|
def setoutputsize(self, size: int, column: int = ...) -> object: ...
|
87
|
+
|
88
|
+
|
89
|
+
class DbapiThreadSafety(enum.IntEnum):
|
90
|
+
NONE = 0
|
91
|
+
MODULE = 1
|
92
|
+
CONNECTION = 2
|
93
|
+
CURSOR = 3
|
94
|
+
|
95
|
+
|
96
|
+
class DbapiModule(ta.Protocol):
|
97
|
+
def connect(self, *args: ta.Any, **kwargs: ta.Any) -> DbapiConnection: ...
|
98
|
+
|
99
|
+
#
|
100
|
+
|
101
|
+
@property
|
102
|
+
def apilevel(self) -> str: ...
|
103
|
+
|
104
|
+
@property
|
105
|
+
def threadsafety(self) -> int: ...
|
106
|
+
|
107
|
+
@property
|
108
|
+
def paramstyle(self) -> str: ...
|
109
|
+
|
110
|
+
#
|
111
|
+
|
112
|
+
@property
|
113
|
+
def Warning(self) -> type[Exception]: ... # noqa
|
114
|
+
|
115
|
+
@property
|
116
|
+
def Error(self) -> type[Exception]: ... # noqa
|
117
|
+
|
118
|
+
@property
|
119
|
+
def InterfaceError(self) -> type[Exception]: ... # noqa
|
120
|
+
|
121
|
+
@property
|
122
|
+
def DatabaseError(self) -> type[Exception]: ... # noqa
|
123
|
+
|
124
|
+
@property
|
125
|
+
def DataError(self) -> type[Exception]: ... # noqa
|
126
|
+
|
127
|
+
@property
|
128
|
+
def OperationalError(self) -> type[Exception]: ... # noqa
|
129
|
+
|
130
|
+
@property
|
131
|
+
def IntegrityError(self) -> type[Exception]: ... # noqa
|
132
|
+
|
133
|
+
@property
|
134
|
+
def InternalError(self) -> type[Exception]: ... # noqa
|
135
|
+
|
136
|
+
@property
|
137
|
+
def ProgrammingError(self) -> type[Exception]: ... # noqa
|
138
|
+
|
139
|
+
@property
|
140
|
+
def NotSupportedError(self) -> type[Exception]: ... # noqa
|
141
|
+
|
142
|
+
#
|
143
|
+
|
144
|
+
def Date(self, year: ta.Any, month: ta.Any, day: ta.Any) -> ta.Any: ... # noqa
|
145
|
+
|
146
|
+
def Time(self, hour: ta.Any, minute: ta.Any, second: ta.Any) -> ta.Any: ... # noqa
|
147
|
+
|
148
|
+
def Timestamp(self, year, month, day, hour, minute, second) -> ta.Any: ... # noqa
|
149
|
+
|
150
|
+
def DateFromTicks(self, ticks: ta.Any) -> ta.Any: ... # noqa
|
151
|
+
|
152
|
+
def TimeFromTicks(self, ticks: ta.Any) -> ta.Any: ... # noqa
|
153
|
+
|
154
|
+
def TimestampFromTicks(self, ticks: ta.Any) -> ta.Any: ... # noqa
|
155
|
+
|
156
|
+
def Binary(self, string: ta.Any) -> ta.Any: ... # noqa
|
157
|
+
|
158
|
+
@property
|
159
|
+
def STRING(self) -> type: ... # noqa
|
160
|
+
|
161
|
+
@property
|
162
|
+
def BINARY(self) -> type: ... # noqa
|
163
|
+
|
164
|
+
@property
|
165
|
+
def NUMBER(self) -> type: ... # noqa
|
166
|
+
|
167
|
+
@property
|
168
|
+
def DATETIME(self) -> type: ... # noqa
|
169
|
+
|
170
|
+
@property
|
171
|
+
def ROWID(self) -> type: ... # noqa
|
omlish/sql/api/base.py
CHANGED
@@ -20,7 +20,7 @@ class Closer(lang.Abstract):
|
|
20
20
|
pass
|
21
21
|
|
22
22
|
|
23
|
-
class
|
23
|
+
class ContextCloser(Closer):
|
24
24
|
def __enter__(self) -> ta.Self:
|
25
25
|
return self
|
26
26
|
|
@@ -31,7 +31,7 @@ class SelfCloser(Closer):
|
|
31
31
|
##
|
32
32
|
|
33
33
|
|
34
|
-
class Querier(
|
34
|
+
class Querier(ContextCloser, lang.Abstract):
|
35
35
|
@property
|
36
36
|
@abc.abstractmethod
|
37
37
|
def adapter(self) -> 'Adapter':
|
@@ -45,7 +45,7 @@ class Querier(SelfCloser, lang.Abstract):
|
|
45
45
|
##
|
46
46
|
|
47
47
|
|
48
|
-
class Rows(
|
48
|
+
class Rows(ContextCloser, lang.Abstract):
|
49
49
|
@property
|
50
50
|
@abc.abstractmethod
|
51
51
|
def columns(self) -> Columns:
|
omlish/sql/dbapi.py
CHANGED
@@ -5,12 +5,6 @@ https://peps.python.org/pep-0249/
|
|
5
5
|
|
6
6
|
apilevel = '2.0'
|
7
7
|
|
8
|
-
threadsafety:
|
9
|
-
0 - Threads may not share the module.
|
10
|
-
1 - Threads may share the module, but not connections.
|
11
|
-
2 - Threads may share the module and connections.
|
12
|
-
3 - Threads may share the module, connections and cursors.
|
13
|
-
|
14
8
|
paramstyle:
|
15
9
|
qmark - Question mark style, e.g. ...WHERE name=?
|
16
10
|
numeric - Numeric, positional style, e.g. ...WHERE name=:1
|
omlish/testing/pytest/skip.py
CHANGED
@@ -20,6 +20,10 @@ def if_python_version_less_than(num: ta.Sequence[int]):
|
|
20
20
|
return pytest.mark.skipif(sys.version_info < tuple(num), reason=f'python version {tuple(sys.version_info)} < {tuple(num)}') # noqa
|
21
21
|
|
22
22
|
|
23
|
+
def if_not_platform(*platforms: str):
|
24
|
+
return pytest.mark.skipif(sys.platform not in platforms, reason=f'requires platform in {platforms}')
|
25
|
+
|
26
|
+
|
23
27
|
def if_not_single():
|
24
28
|
# FIXME
|
25
29
|
# [resolve_collection_argument(a) for a in session.config.args]
|
omlish/text/mangle.py
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import dataclasses as dc
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from ..lite.cached import cached_nullary
|
7
|
+
from ..lite.check import check
|
8
|
+
|
9
|
+
|
10
|
+
@dc.dataclass(frozen=True)
|
11
|
+
class StringMangler:
|
12
|
+
escape: str
|
13
|
+
escaped: ta.Sequence[str]
|
14
|
+
|
15
|
+
@classmethod
|
16
|
+
def of(cls, escape: str, escaped: ta.Iterable[str]) -> 'StringMangler':
|
17
|
+
check.arg(len(escape) == 1)
|
18
|
+
return StringMangler(escape, sorted(set(escaped) - {escape}))
|
19
|
+
|
20
|
+
def __post_init__(self) -> None:
|
21
|
+
check.non_empty_str(self.escape)
|
22
|
+
check.arg(len(self.escape) == 1)
|
23
|
+
check.not_in(self.escape, self.escaped)
|
24
|
+
check.arg(len(set(self.escaped)) == len(self.escaped))
|
25
|
+
|
26
|
+
@cached_nullary
|
27
|
+
def replacements(self) -> ta.Sequence[ta.Tuple[str, str]]:
|
28
|
+
return [(l, self.escape + str(i)) for i, l in enumerate([self.escape, *self.escaped])]
|
29
|
+
|
30
|
+
def mangle(self, s: str) -> str:
|
31
|
+
for l, r in self.replacements():
|
32
|
+
s = s.replace(l, r)
|
33
|
+
return s
|
34
|
+
|
35
|
+
def unmangle(self, s: str) -> str:
|
36
|
+
for l, r in reversed(self.replacements()):
|
37
|
+
s = s.replace(r, l)
|
38
|
+
return s
|
@@ -1,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=vQTAIvR8OblSq-uP2GUfnbei0RnmAnM5j0T1-OToh9E,8253
|
2
|
-
omlish/__about__.py,sha256=
|
2
|
+
omlish/__about__.py,sha256=5BBsZPnECZ_Jaf1rDpLsCugHIUD8HNUgPw4LZNyfn68,3380
|
3
3
|
omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
|
4
4
|
omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
|
5
5
|
omlish/cached.py,sha256=UI-XTFBwA6YXWJJJeBn-WkwBkfzDjLBBaZf4nIJA9y0,510
|
@@ -8,7 +8,7 @@ omlish/datetimes.py,sha256=HajeM1kBvwlTa-uR1TTZHmZ3zTPnnUr1uGGQhiO1XQ0,2152
|
|
8
8
|
omlish/defs.py,sha256=9uUjJuVIbCBL3g14fyzAp-9gH935MFofvlfOGwcBIaM,4913
|
9
9
|
omlish/dynamic.py,sha256=kIZokHHid8a0pIAPXMNiXrVJvJJyBnY49WP1a2m-HUQ,6525
|
10
10
|
omlish/libc.py,sha256=8K4c66YV1ziJerl5poAAYCmsV-VSsHkT3EHhPW04ufg,15639
|
11
|
-
omlish/metadata.py,sha256=
|
11
|
+
omlish/metadata.py,sha256=q8UG-fpcXhEF7BZnhVikIE_IHyud9-8YT8iv646zU2s,3589
|
12
12
|
omlish/outcome.py,sha256=ABIE0zjjTyTNtn-ZqQ_9_mUzLiBQ3sDAyqc9JVD8N2k,7852
|
13
13
|
omlish/runmodule.py,sha256=PWvuAaJ9wQQn6bx9ftEL3_d04DyotNn8dR_twm2pgw0,700
|
14
14
|
omlish/shlex.py,sha256=bsW2XUD8GiMTUTDefJejZ5AyqT1pTgWMPD0BMoF02jE,248
|
@@ -98,8 +98,8 @@ omlish/asyncs/flavors.py,sha256=1mNxGNRVmjUHzA13K5ht8vdJv4CLEmzYTQ6BZXr1520,4866
|
|
98
98
|
omlish/asyncs/trio.py,sha256=fmZ5b_lKdVV8NQ3euCUutWgnkqTFzSnOjvJSA_jvmrE,367
|
99
99
|
omlish/asyncs/trio_asyncio.py,sha256=oqdOHy0slj9PjVxaDf3gJkq9AAgg7wYZbB469jOftVw,1327
|
100
100
|
omlish/asyncs/asyncio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
101
|
-
omlish/asyncs/asyncio/all.py,sha256=
|
102
|
-
omlish/asyncs/asyncio/asyncio.py,sha256=
|
101
|
+
omlish/asyncs/asyncio/all.py,sha256=EksCHjRQKobiGrxuDW72IaH53WJMs7rdj_ZDBI3iKcg,315
|
102
|
+
omlish/asyncs/asyncio/asyncio.py,sha256=mDjYNm1cylUhQ8slWXwdPoXasuWfafjzu78GHt2Mdig,2437
|
103
103
|
omlish/asyncs/asyncio/channels.py,sha256=ZbmsEmdK1fV96liHdcVpRqA2dAMkXJt4Q3rFAg3YOIw,1074
|
104
104
|
omlish/asyncs/asyncio/streams.py,sha256=Uc9PCWSfBqrK2kdVtfjjQU1eaYTWYmZm8QISDj2xiuw,1004
|
105
105
|
omlish/asyncs/asyncio/subprocesses.py,sha256=f30-wi-3n9R5dftm4CMrzp23EEa4GX283bORixm1_UU,6931
|
@@ -238,15 +238,16 @@ omlish/dispatch/dispatch.py,sha256=p3-RqBf9RKZaNub1FMGHZkETewF43mU_rv4fYD_ERqU,4
|
|
238
238
|
omlish/dispatch/functions.py,sha256=S8ElsLi6DKxTdtFGigWaF0vAquwy2sK-3f4iRLaYq70,1522
|
239
239
|
omlish/dispatch/methods.py,sha256=Sg134xzG41-__czfnWdzDlXdsxVt7ELOq90N2E6NSzI,5501
|
240
240
|
omlish/docker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
241
|
-
omlish/docker/all.py,sha256=
|
241
|
+
omlish/docker/all.py,sha256=xXRgJgLGPwAtr7bDMJ_Dp9jTfOwfGvohNhc6LsoELJc,514
|
242
242
|
omlish/docker/cli.py,sha256=gtb9kitVfGnd4cr587NsVVk8D5Ok5y5SAsqD_SwGrSA,2565
|
243
243
|
omlish/docker/compose.py,sha256=4drmnGQzbkOFJ9B6XSg9rnXkJeZz1ETmdcMe1PE790U,1237
|
244
244
|
omlish/docker/consts.py,sha256=wvwfUtEFrEWZKfREWqSMrx8xjjl8P5MNUSF6qzzgJHY,70
|
245
245
|
omlish/docker/detect.py,sha256=Qrdbosm2wJkxKDuy8gaGmbQoxk4Wnp1HJjAEz58NA8Y,614
|
246
246
|
omlish/docker/hub.py,sha256=7LIuJGdA-N1Y1dmo50ynKM1KUTcnQM_5XbtPbdT_QLU,3940
|
247
247
|
omlish/docker/manifests.py,sha256=LR4FpOGNUT3bZQ-gTjB6r_-1C3YiG30QvevZjrsVUQM,7068
|
248
|
-
omlish/docker/
|
249
|
-
omlish/docker/
|
248
|
+
omlish/docker/ns1.py,sha256=75L_9zjXK5qZbvi20Kd0cus-bm3n4oKFYcmEtk1c5-o,2470
|
249
|
+
omlish/docker/ports.py,sha256=ov4Lq5JweAThD3qwnjaJbONcHAkRhEx96-NU7ftMcK0,2083
|
250
|
+
omlish/docker/timebomb.py,sha256=EnFt8pJeXkowF_F5NXnN0ogDmxUmVymV4h1CYPwyJr4,356
|
250
251
|
omlish/formats/__init__.py,sha256=T0AG1gFnqQ5JiHN0UPQjQ-7g5tnxMIG-mgOvMYExYAM,21
|
251
252
|
omlish/formats/cbor.py,sha256=o_Hbe4kthO9CeXK-FySrw0dHVlrdyTo2Y8PpGRDfZ3c,514
|
252
253
|
omlish/formats/cloudpickle.py,sha256=16si4yUp_pAdDWGECAWf6HLA2PwSANGGgDoMLGZUem8,579
|
@@ -331,7 +332,7 @@ omlish/http/wsgi.py,sha256=czZsVUX-l2YTlMrUjKN49wRoP4rVpS0qpeBn4O5BoMY,948
|
|
331
332
|
omlish/http/coro/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
332
333
|
omlish/http/coro/fdio.py,sha256=bd9K4EYVWbXV3e3npDPXI9DuDAruJiyDmrgFpgNcjzY,4035
|
333
334
|
omlish/http/coro/server.py,sha256=30FTcJG8kuFeThf0HJYpTzMZN-giLTBP7wr5Wl3b9X0,18285
|
334
|
-
omlish/http/coro/simple.py,sha256=
|
335
|
+
omlish/http/coro/simple.py,sha256=_ZKFlfLda9Gatd3bNBNGJpITNQl4tuTAbL3P-Mr5j5w,3152
|
335
336
|
omlish/http/coro/sockets.py,sha256=rtpZZ-XCOfC5tXr4Fmo1HSn-8f5nxfIOlJaPUkQeDyU,1654
|
336
337
|
omlish/inject/__init__.py,sha256=n0RC9UDGsBQQ39cST39-XJqJPq2M0tnnh9yJubW9azo,1891
|
337
338
|
omlish/inject/binder.py,sha256=DAbc8TZi5w8Mna0TUtq0mT4jeDVA7i7SlBtOFrh2swc,4185
|
@@ -436,7 +437,7 @@ omlish/lite/configs.py,sha256=Ev_19sbII67pTWzInYjYqa9VyTiZBvyjhZqyG8TtufE,908
|
|
436
437
|
omlish/lite/contextmanagers.py,sha256=ciaMl0D3QDHToM7M28-kwZ-Q48LtwgCxiud3nekgutA,2863
|
437
438
|
omlish/lite/dataclasses.py,sha256=t1G5-xOuvE6o6w9RyqHzLT9wHD0HkqBh5P8HUZWxGzs,1912
|
438
439
|
omlish/lite/imports.py,sha256=o9WWrNrWg0hKeMvaj91giaovED_9VFanN2MyEHBGekY,1346
|
439
|
-
omlish/lite/inject.py,sha256
|
440
|
+
omlish/lite/inject.py,sha256=-tTsOqqef-Ix5Tgl2DP_JAsNWJQDFUptERl3lk14Uzs,29007
|
440
441
|
omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
|
441
442
|
omlish/lite/logs.py,sha256=CWFG0NKGhqNeEgryF5atN2gkPYbUdTINEw_s1phbINM,51
|
442
443
|
omlish/lite/marshal.py,sha256=UMwSLEM-QvkvnSHmcChJVkIXkGp9WAyMYyZMB-NZefw,18463
|
@@ -472,7 +473,7 @@ omlish/manifests/load.py,sha256=9mdsS3egmSX9pymO-m-y2Fhs4p6ruOdbsYaKT1-1Hwg,6655
|
|
472
473
|
omlish/manifests/static.py,sha256=7YwOVh_Ek9_aTrWsWNO8kWS10_j4K7yv3TpXZSHsvDY,501
|
473
474
|
omlish/manifests/types.py,sha256=IOt9dOe0r8okCHSL82ryi3sn4VZ6AT80g_QQR6oZtCE,306
|
474
475
|
omlish/marshal/__init__.py,sha256=00D3S6qwUld1TUWd67hVHuNcrj3c_FAFSkCVXgGWT-s,2607
|
475
|
-
omlish/marshal/base.py,sha256=
|
476
|
+
omlish/marshal/base.py,sha256=s1wQRPG2Y6kH0qQXoL3d60ldYaVTqLuFs0NdbYXwAGg,6842
|
476
477
|
omlish/marshal/exceptions.py,sha256=jwQWn4LcPnadT2KRI_1JJCOSkwWh0yHnYK9BmSkNN4U,302
|
477
478
|
omlish/marshal/factories.py,sha256=Q926jSVjaQLEmStnHLhm_c_vqEysN1LnDCwAsFLIzXw,2970
|
478
479
|
omlish/marshal/global_.py,sha256=K76wB1-pdg4VWgiqR7wyxRNYr-voJApexYW2nV-R4DM,1127
|
@@ -527,7 +528,7 @@ omlish/os/files.py,sha256=WJ_42vsZIZukQURN3TTccp-n74ZNhbux_ps3TLbHj18,1106
|
|
527
528
|
omlish/os/forkhooks.py,sha256=yjodOvs90ClXskv5oBIJbHn0Y7dzajLmZmOpRMKbyxM,5656
|
528
529
|
omlish/os/journald.py,sha256=2nI8Res1poXkbLc31--MPUlzYMESnCcPUkIxDOCjZW0,3903
|
529
530
|
omlish/os/linux.py,sha256=whJ6scwMKSFBdXiVhJW0BCpJV4jOGMr-a_a3Bhwz6Ls,18938
|
530
|
-
omlish/os/mangle.py,sha256=
|
531
|
+
omlish/os/mangle.py,sha256=M0v-SDt4TMnL68I45GekQrUaXkTIILXIlPdqRxKBTKM,524
|
531
532
|
omlish/os/paths.py,sha256=hqPiyg_eYaRoIVPdAeX4oeLEV4Kpln_XsH0tHvbOf8Q,844
|
532
533
|
omlish/os/signals.py,sha256=FtzkovLb58N3vNdfxflUeXWFCqqKzseCjk5kBdWT-ds,267
|
533
534
|
omlish/os/sizes.py,sha256=ohkALLvqSqBX4iR-7DMKJ4pfOCRdZXV8htH4QywUNM0,152
|
@@ -560,12 +561,14 @@ omlish/secrets/subprocesses.py,sha256=ffjfbgPbEE_Pwb_87vG4yYR2CGZy3I31mHNCo_0JtH
|
|
560
561
|
omlish/secrets/tempssl.py,sha256=tlwRrbHHvgKJtNAC31I5sDKryya4fagqN6kGt-tV4Qg,1874
|
561
562
|
omlish/sockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
562
563
|
omlish/sockets/addresses.py,sha256=vbVeQBkzI513H4vRv-JS89QtRbr9U8v5zqkm3oODl_s,1869
|
563
|
-
omlish/sockets/bind.py,sha256=
|
564
|
+
omlish/sockets/bind.py,sha256=J1SfFFFnVf3H5nqESDX2NGEY8DmjyIMUXZciZM33zQY,8003
|
564
565
|
omlish/sockets/handlers.py,sha256=Gj6xZoo4vommge8XvkehYw3B7O4aql2P4qzZIIa0p24,462
|
565
566
|
omlish/sockets/io.py,sha256=lfhTkB7NnAIx9kuQhAkwgsEUXY78Mp1_WtYrIQNS_k8,1408
|
567
|
+
omlish/sockets/ports.py,sha256=Wm4mRFFz5MdD8KbdaEfT1c4PbJnsuK_iyJlZJE_-8jo,1402
|
566
568
|
omlish/sockets/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
567
|
-
omlish/sockets/server/handlers.py,sha256=
|
569
|
+
omlish/sockets/server/handlers.py,sha256=PPsb1X5oU9dN8jfztaMGsRiqWTyEANT-1aSLbS6bUVg,3867
|
568
570
|
omlish/sockets/server/server.py,sha256=mZmHPkCRPitous56_7FJdAsDLZag2wDqjj-LaYM8_Fg,4943
|
571
|
+
omlish/sockets/server/ssl.py,sha256=VE0GpdA-gYsN2m9_uvfDwWmXtIbRQqJomVdpGJO8o2M,1061
|
569
572
|
omlish/sockets/server/threading.py,sha256=YmW3Ym_p5j_F4SIH9BgRHIObywjq1HS39j9CGWIcMAY,2856
|
570
573
|
omlish/specs/__init__.py,sha256=zZwF8yXTEkSstYtORkDhVLK-_hWU8WOJCuBpognb_NY,118
|
571
574
|
omlish/specs/irc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -629,8 +632,8 @@ omlish/specs/openapi/__init__.py,sha256=zilQhafjvteRDF_TUIRgF293dBC6g-TJChmUb6T9
|
|
629
632
|
omlish/specs/openapi/marshal.py,sha256=Z-E2Knm04C81N8AA8cibCVSl2ImhSpHZVc7yAhmPx88,2135
|
630
633
|
omlish/specs/openapi/openapi.py,sha256=y4h04jeB7ORJSVrcy7apaBdpwLjIyscv1Ub5SderH2c,12682
|
631
634
|
omlish/sql/__init__.py,sha256=TpZLsEJKJzvJ0eMzuV8hwOJJbkxBCV1RZPUMLAVB6io,173
|
632
|
-
omlish/sql/abc.py,sha256=
|
633
|
-
omlish/sql/dbapi.py,sha256=
|
635
|
+
omlish/sql/abc.py,sha256=yrUSO2OSY6rtMyA0_MlrEFEyIKXLJymSPpmJwEjEJuc,4038
|
636
|
+
omlish/sql/dbapi.py,sha256=o6umqE7zVFI_ax0TnCKAikhAIPXChYOwVULb_OdcL1c,2974
|
634
637
|
omlish/sql/dbs.py,sha256=65e388987upJpsFX8bNL7uhiYv2sCsmk9Y04V0MXdsI,1873
|
635
638
|
omlish/sql/params.py,sha256=Z4VPet6GhNqD1T_MXSWSHkdy3cpUEhST-OplC4B_fYI,4433
|
636
639
|
omlish/sql/qualifiedname.py,sha256=rlW3gVmyucJbqwcxj_7BfK4X2HoXrMroZT2H45zPgJQ,2264
|
@@ -641,7 +644,7 @@ omlish/sql/alchemy/exprs.py,sha256=gO4Fj4xEY-PuDgV-N8hBMy55glZz7O-4H7v1LWabfZY,3
|
|
641
644
|
omlish/sql/alchemy/secrets.py,sha256=WEeaec1ejQcE3Yaa7p5BSP9AMGEzy1lwr7QMSRL0VBw,180
|
642
645
|
omlish/sql/alchemy/sqlean.py,sha256=RbkuOuFIfM4fowwKk8-sQ6Dxk-tTUwxS94nY5Kxt52s,403
|
643
646
|
omlish/sql/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
644
|
-
omlish/sql/api/base.py,sha256=
|
647
|
+
omlish/sql/api/base.py,sha256=fvCg-bGVPEZOwrob_Kn4bpk_rYdiEKutyPXamCbLrWw,1515
|
645
648
|
omlish/sql/api/columns.py,sha256=UBol4bfwZ1nhcjv2gE1JhUMzRFeqtiCDo2T9CUGYb64,1943
|
646
649
|
omlish/sql/api/dbapi.py,sha256=H3bFoWI-ox81APX3xBbjylNWyMUXh78ICMQjRrlDNxw,2426
|
647
650
|
omlish/sql/api/errors.py,sha256=YtC2gz5DqRTT3uCJniUOufVH1GEnFIc5ElkYLK3BHwM,230
|
@@ -691,7 +694,7 @@ omlish/testing/__init__.py,sha256=M_BQrcCHkoL-ZvE-UpQ8XxXNYRRawhjUz4rCJnAqM2A,15
|
|
691
694
|
omlish/testing/testing.py,sha256=TT2wwSzPZ_KhIvKxpM1qc1yHKD-LHDNgGrcr_h8vs7c,2895
|
692
695
|
omlish/testing/pytest/__init__.py,sha256=rOpQYgp7jYjEIMjInzl-a_uIMqmOtVwGzDgJyCDpvxg,206
|
693
696
|
omlish/testing/pytest/helpers.py,sha256=TJpD60mBtLi9FtxX4TThfuXvg5FIRPSiZk1aeRwe-D4,197
|
694
|
-
omlish/testing/pytest/skip.py,sha256=
|
697
|
+
omlish/testing/pytest/skip.py,sha256=Mk7iIfXxp6JWl8vJRP9BK9LoirSOLTJ3mLeLL6Ipi9M,984
|
695
698
|
omlish/testing/pytest/inject/__init__.py,sha256=pdRKv1HcDmJ_yArKJbYITPXXZthRSGgBJWqITr0Er38,117
|
696
699
|
omlish/testing/pytest/inject/harness.py,sha256=_Qf7lLcYc_dpauYOE68u_a65jPCFWmQUYv9m_OOdNqs,5724
|
697
700
|
omlish/testing/pytest/plugins/__init__.py,sha256=ys1zXrYrNm7Uo6YOIVJ6Bd3dQo6kv387k7MbTYlqZSI,467
|
@@ -720,12 +723,13 @@ omlish/text/asdl.py,sha256=AS3irh-sag5pqyH3beJif78PjCbOaFso1NeKq-HXuTs,16867
|
|
720
723
|
omlish/text/delimit.py,sha256=ubPXcXQmtbOVrUsNh5gH1mDq5H-n1y2R4cPL5_DQf68,4928
|
721
724
|
omlish/text/glyphsplit.py,sha256=kqqjglRdxGo0czYZxOz9Vi8aBmVsCOq8h6lPwRA5xe0,3803
|
722
725
|
omlish/text/indent.py,sha256=YjtJEBYWuk8--b9JU_T6q4yxV85_TR7VEVr5ViRCFwk,1336
|
726
|
+
omlish/text/mangle.py,sha256=kfzFLfvepH-chl1P89_mdc5vC4FSqyPA2aVtgzuB8IY,1133
|
723
727
|
omlish/text/minja.py,sha256=jZC-fp3Xuhx48ppqsf2Sf1pHbC0t8XBB7UpUUoOk2Qw,5751
|
724
728
|
omlish/text/parts.py,sha256=JkNZpyR2tv2CNcTaWJJhpQ9E4F0yPR8P_YfDbZfMtwQ,6182
|
725
729
|
omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
|
726
|
-
omlish-0.0.0.
|
727
|
-
omlish-0.0.0.
|
728
|
-
omlish-0.0.0.
|
729
|
-
omlish-0.0.0.
|
730
|
-
omlish-0.0.0.
|
731
|
-
omlish-0.0.0.
|
730
|
+
omlish-0.0.0.dev241.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
731
|
+
omlish-0.0.0.dev241.dist-info/METADATA,sha256=3ZHYGZG8a_knh-CYGzvB3tX7S65p-epBNTzdZbNja4o,4176
|
732
|
+
omlish-0.0.0.dev241.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
733
|
+
omlish-0.0.0.dev241.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
734
|
+
omlish-0.0.0.dev241.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
735
|
+
omlish-0.0.0.dev241.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|