omlish 0.0.0.dev243__py3-none-any.whl → 0.0.0.dev244__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/daemons/daemon.py +4 -2
- omlish/daemons/launching.py +10 -6
- omlish/daemons/reparent.py +14 -1
- omlish/daemons/services.py +43 -15
- omlish/daemons/spawning.py +56 -16
- omlish/http/coro/simple.py +2 -0
- omlish/sockets/wait.py +32 -10
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev244.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev244.dist-info}/RECORD +14 -14
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev244.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev244.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev244.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev244.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/daemons/daemon.py
CHANGED
@@ -48,7 +48,9 @@ class Daemon:
|
|
48
48
|
|
49
49
|
#
|
50
50
|
|
51
|
+
# TODO: None, defaults, figure out from spawn method
|
51
52
|
reparent_process: bool = False
|
53
|
+
|
52
54
|
launched_timeout_s: float = 5.
|
53
55
|
|
54
56
|
#
|
@@ -126,7 +128,7 @@ class Daemon:
|
|
126
128
|
|
127
129
|
#
|
128
130
|
|
129
|
-
def launch_no_wait(self) ->
|
131
|
+
def launch_no_wait(self) -> bool:
|
130
132
|
launcher = Launcher(
|
131
133
|
target=self._target,
|
132
134
|
spawning=check.not_none(self._config.spawning),
|
@@ -136,7 +138,7 @@ class Daemon:
|
|
136
138
|
launched_timeout_s=self._config.launched_timeout_s,
|
137
139
|
)
|
138
140
|
|
139
|
-
launcher.launch()
|
141
|
+
return launcher.launch()
|
140
142
|
|
141
143
|
def launch(self, timeout: lang.TimeoutLike = lang.Timeout.Default) -> None:
|
142
144
|
self.launch_no_wait()
|
omlish/daemons/launching.py
CHANGED
@@ -53,8 +53,10 @@ class Launcher:
|
|
53
53
|
self,
|
54
54
|
*,
|
55
55
|
pidfile_manager: ta.ContextManager | None,
|
56
|
-
|
56
|
+
callback: ta.Callable[[], None] | None = None,
|
57
57
|
) -> None:
|
58
|
+
callback_called = False
|
59
|
+
|
58
60
|
try:
|
59
61
|
if self._reparent_process:
|
60
62
|
log.info('Reparenting')
|
@@ -66,15 +68,16 @@ class Launcher:
|
|
66
68
|
pidfile = check.isinstance(es.enter_context(pidfile_manager), Pidfile)
|
67
69
|
pidfile.write()
|
68
70
|
|
69
|
-
if
|
70
|
-
|
71
|
+
if callback is not None:
|
72
|
+
callback_called = True
|
73
|
+
callback()
|
71
74
|
|
72
75
|
runner = target_runner_for(self._target)
|
73
76
|
runner.run()
|
74
77
|
|
75
78
|
finally:
|
76
|
-
if
|
77
|
-
|
79
|
+
if callback is not None and not callback_called:
|
80
|
+
callback()
|
78
81
|
|
79
82
|
def launch(self) -> bool:
|
80
83
|
with contextlib.ExitStack() as es:
|
@@ -110,8 +113,9 @@ class Launcher:
|
|
110
113
|
functools.partial(
|
111
114
|
self._inner_launch,
|
112
115
|
pidfile_manager=pidfile_manager,
|
113
|
-
|
116
|
+
callback=launched_event.set if launched_event is not None else None,
|
114
117
|
),
|
118
|
+
target=self._target,
|
115
119
|
inherit_fds=inherit_fds,
|
116
120
|
))
|
117
121
|
|
omlish/daemons/reparent.py
CHANGED
@@ -2,7 +2,10 @@ import os
|
|
2
2
|
import sys
|
3
3
|
|
4
4
|
|
5
|
-
def reparent_process(
|
5
|
+
def reparent_process(
|
6
|
+
*,
|
7
|
+
no_close_stdio: bool = False,
|
8
|
+
) -> None:
|
6
9
|
if (pid := os.fork()): # noqa
|
7
10
|
sys.exit(0)
|
8
11
|
raise RuntimeError('Unreachable') # noqa
|
@@ -12,5 +15,15 @@ def reparent_process() -> None:
|
|
12
15
|
if (pid := os.fork()): # noqa
|
13
16
|
sys.exit(0)
|
14
17
|
|
18
|
+
if not no_close_stdio:
|
19
|
+
rn_fd = os.open('/dev/null', os.O_RDONLY)
|
20
|
+
os.dup2(rn_fd, 0)
|
21
|
+
os.close(rn_fd)
|
22
|
+
|
23
|
+
wn_fd = os.open('/dev/null', os.O_WRONLY)
|
24
|
+
os.dup2(wn_fd, 1)
|
25
|
+
os.dup2(wn_fd, 2)
|
26
|
+
os.close(wn_fd)
|
27
|
+
|
15
28
|
sys.stdout.flush()
|
16
29
|
sys.stderr.flush()
|
omlish/daemons/services.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import abc
|
2
|
+
import threading
|
2
3
|
import typing as ta
|
3
4
|
|
4
5
|
from .. import cached
|
@@ -12,6 +13,7 @@ from .targets import TargetRunner
|
|
12
13
|
from .targets import target_runner_for
|
13
14
|
|
14
15
|
|
16
|
+
ServiceT = ta.TypeVar('ServiceT', bound='Service')
|
15
17
|
ServiceConfigT = ta.TypeVar('ServiceConfigT', bound='Service.Config')
|
16
18
|
|
17
19
|
|
@@ -82,27 +84,53 @@ def _(target: ServiceConfigTarget) -> ServiceConfigTargetRunner:
|
|
82
84
|
|
83
85
|
|
84
86
|
@dc.dataclass(frozen=True)
|
85
|
-
class ServiceDaemon(lang.Final):
|
86
|
-
service:
|
87
|
+
class ServiceDaemon(lang.Final, ta.Generic[ServiceT, ServiceConfigT]):
|
88
|
+
service: ServiceT | ServiceConfigT
|
87
89
|
|
88
90
|
@cached.function
|
89
|
-
def
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
91
|
+
def service_config(self) -> ServiceConfigT:
|
92
|
+
with self._lock:
|
93
|
+
if isinstance(self.service, Service):
|
94
|
+
return self.service.config
|
95
|
+
elif isinstance(self.service, Service.Config):
|
96
|
+
return self.service
|
97
|
+
else:
|
98
|
+
raise TypeError(self.service)
|
99
|
+
|
100
|
+
@cached.function
|
101
|
+
def service_(self) -> ServiceT:
|
102
|
+
with self._lock:
|
103
|
+
if isinstance(self.service, Service):
|
104
|
+
return self.service # type: ignore[return-value]
|
105
|
+
elif isinstance(self.service, Service.Config):
|
106
|
+
return Service.from_config(self.service) # type: ignore[return-value]
|
107
|
+
else:
|
108
|
+
raise TypeError(self.service)
|
96
109
|
|
97
110
|
#
|
98
111
|
|
99
112
|
daemon: Daemon | Daemon.Config = Daemon.Config()
|
100
113
|
|
114
|
+
@cached.function
|
115
|
+
def daemon_config(self) -> Daemon.Config:
|
116
|
+
with self._lock:
|
117
|
+
if isinstance(self.daemon, Daemon):
|
118
|
+
return self.daemon.config
|
119
|
+
elif isinstance(self.daemon, Daemon.Config):
|
120
|
+
return self.daemon
|
121
|
+
else:
|
122
|
+
raise TypeError(self.daemon)
|
123
|
+
|
101
124
|
@cached.function
|
102
125
|
def daemon_(self) -> Daemon:
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
126
|
+
with self._lock:
|
127
|
+
if isinstance(self.daemon, Daemon):
|
128
|
+
return self.daemon
|
129
|
+
elif isinstance(self.daemon, Daemon.Config):
|
130
|
+
return Daemon(Target.of(self.service_()), self.daemon)
|
131
|
+
else:
|
132
|
+
raise TypeError(self.daemon)
|
133
|
+
|
134
|
+
#
|
135
|
+
|
136
|
+
_lock: threading.RLock = dc.field(default_factory=lambda: threading.RLock(), init=False)
|
omlish/daemons/spawning.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import abc
|
2
|
+
import enum
|
2
3
|
import functools
|
3
4
|
import os
|
4
5
|
import sys
|
@@ -9,6 +10,7 @@ from .. import check
|
|
9
10
|
from .. import dataclasses as dc
|
10
11
|
from .. import lang
|
11
12
|
from ..diag import pydevd
|
13
|
+
from .targets import Target
|
12
14
|
|
13
15
|
|
14
16
|
if ta.TYPE_CHECKING:
|
@@ -37,6 +39,8 @@ class Spawn(dc.Frozen, final=True):
|
|
37
39
|
|
38
40
|
_: dc.KW_ONLY
|
39
41
|
|
42
|
+
target: Target | None = None
|
43
|
+
|
40
44
|
inherit_fds: ta.Collection[int] | None = None
|
41
45
|
|
42
46
|
|
@@ -59,10 +63,29 @@ def spawner_for(spawning: Spawning) -> Spawner:
|
|
59
63
|
|
60
64
|
|
61
65
|
class MultiprocessingSpawning(Spawning, kw_only=True):
|
66
|
+
class StartMethod(enum.Enum):
|
67
|
+
SPAWN = enum.auto()
|
68
|
+
FORK = enum.auto()
|
69
|
+
# TODO: FORK_SERVER
|
70
|
+
|
62
71
|
# Defaults to 'fork' if under pydevd, else 'spawn'
|
63
|
-
start_method:
|
72
|
+
start_method: StartMethod | None = None
|
73
|
+
|
74
|
+
#
|
75
|
+
|
76
|
+
# Note: Per multiprocessing docs, `no_linger=True` processes (corresponding to `Process(daemon=True)`) cannot spawn
|
77
|
+
# subprocesses, and thus will fail if `Daemon.Config.reparent_process` is set.
|
78
|
+
no_linger: bool = False
|
64
79
|
|
65
|
-
|
80
|
+
#
|
81
|
+
|
82
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
83
|
+
class EntrypointArgs:
|
84
|
+
spawning: 'MultiprocessingSpawning'
|
85
|
+
spawn: Spawn
|
86
|
+
start_method: 'MultiprocessingSpawning.StartMethod'
|
87
|
+
|
88
|
+
entrypoint: ta.Callable[[EntrypointArgs], None] | None = None
|
66
89
|
|
67
90
|
|
68
91
|
class MultiprocessingSpawner(Spawner):
|
@@ -72,20 +95,26 @@ class MultiprocessingSpawner(Spawner):
|
|
72
95
|
self._spawning = spawning
|
73
96
|
self._process: ta.Optional['mp.process.BaseProcess'] = None # noqa
|
74
97
|
|
98
|
+
@lang.cached_function
|
99
|
+
def _determine_start_method(self) -> 'MultiprocessingSpawning.StartMethod':
|
100
|
+
if (start_method := self._spawning.start_method) is not None:
|
101
|
+
return start_method
|
102
|
+
|
103
|
+
# Unfortunately, pydevd forces the use of the 'fork' start_method, which cannot be mixed with 'spawn':
|
104
|
+
# https://github.com/python/cpython/blob/a7427f2db937adb4c787754deb4c337f1894fe86/Lib/multiprocessing/spawn.py#L102 # noqa
|
105
|
+
if pydevd.is_running():
|
106
|
+
return MultiprocessingSpawning.StartMethod.FORK
|
107
|
+
|
108
|
+
return MultiprocessingSpawning.StartMethod.SPAWN
|
109
|
+
|
75
110
|
def _process_cls(self, spawn: Spawn) -> type['mp.process.BaseProcess']:
|
76
|
-
|
77
|
-
# Unfortunately, pydevd forces the use of the 'fork' start_method, which cannot be mixed with 'spawn':
|
78
|
-
# https://github.com/python/cpython/blob/a7427f2db937adb4c787754deb4c337f1894fe86/Lib/multiprocessing/spawn.py#L102 # noqa
|
79
|
-
if pydevd.is_running():
|
80
|
-
start_method = 'fork'
|
81
|
-
else:
|
82
|
-
start_method = 'spawn'
|
111
|
+
start_method = self._determine_start_method()
|
83
112
|
|
84
113
|
ctx: 'mp.context.BaseContext' # noqa
|
85
|
-
if start_method ==
|
86
|
-
ctx = mp.get_context(check.non_empty_str(
|
114
|
+
if start_method == MultiprocessingSpawning.StartMethod.FORK:
|
115
|
+
ctx = mp.get_context(check.non_empty_str('fork'))
|
87
116
|
|
88
|
-
elif start_method ==
|
117
|
+
elif start_method == MultiprocessingSpawning.StartMethod.SPAWN:
|
89
118
|
ctx = omp_spawn.ExtrasSpawnContext(omp_spawn.SpawnExtras(
|
90
119
|
pass_fds=frozenset(spawn.inherit_fds) if spawn.inherit_fds is not None else None,
|
91
120
|
))
|
@@ -97,9 +126,20 @@ class MultiprocessingSpawner(Spawner):
|
|
97
126
|
|
98
127
|
def spawn(self, spawn: Spawn) -> None:
|
99
128
|
check.none(self._process)
|
129
|
+
|
130
|
+
target: ta.Callable[[], None]
|
131
|
+
if (ep := self._spawning.entrypoint) is not None:
|
132
|
+
target = functools.partial(ep, MultiprocessingSpawning.EntrypointArgs(
|
133
|
+
spawning=self._spawning,
|
134
|
+
spawn=spawn,
|
135
|
+
start_method=self._determine_start_method(),
|
136
|
+
))
|
137
|
+
else:
|
138
|
+
target = spawn.fn
|
139
|
+
|
100
140
|
self._process = self._process_cls(spawn)(
|
101
|
-
target=
|
102
|
-
daemon=
|
141
|
+
target=target,
|
142
|
+
daemon=self._spawning.no_linger,
|
103
143
|
)
|
104
144
|
self._process.start()
|
105
145
|
|
@@ -142,7 +182,7 @@ def _(spawning: ForkSpawning) -> ForkSpawner:
|
|
142
182
|
|
143
183
|
|
144
184
|
class ThreadSpawning(Spawning, kw_only=True):
|
145
|
-
|
185
|
+
linger: bool = False
|
146
186
|
|
147
187
|
|
148
188
|
class ThreadSpawner(InProcessSpawner):
|
@@ -156,7 +196,7 @@ class ThreadSpawner(InProcessSpawner):
|
|
156
196
|
check.none(self._thread)
|
157
197
|
self._thread = threading.Thread(
|
158
198
|
target=spawn.fn,
|
159
|
-
daemon=not self._spawning.
|
199
|
+
daemon=not self._spawning.linger,
|
160
200
|
)
|
161
201
|
self._thread.start()
|
162
202
|
|
omlish/http/coro/simple.py
CHANGED
@@ -43,6 +43,7 @@ def make_simple_http_server(
|
|
43
43
|
ignore_ssl_errors: bool = False,
|
44
44
|
executor: ta.Optional[cf.Executor] = None,
|
45
45
|
use_threads: bool = False,
|
46
|
+
**kwargs: ta.Any,
|
46
47
|
) -> ta.Iterator[SocketServer]:
|
47
48
|
check.arg(not (executor is not None and use_threads))
|
48
49
|
|
@@ -107,6 +108,7 @@ def make_simple_http_server(
|
|
107
108
|
server = es.enter_context(SocketServer(
|
108
109
|
SocketBinder.of(bind),
|
109
110
|
server_handler,
|
111
|
+
**kwargs,
|
110
112
|
))
|
111
113
|
|
112
114
|
yield server
|
omlish/sockets/wait.py
CHANGED
@@ -8,13 +8,38 @@ from ..lite.timeouts import Timeout
|
|
8
8
|
from ..lite.timeouts import TimeoutLike
|
9
9
|
|
10
10
|
|
11
|
+
##
|
12
|
+
|
13
|
+
|
14
|
+
def socket_can_connect(
|
15
|
+
address: ta.Any,
|
16
|
+
*,
|
17
|
+
timeout: ta.Optional[TimeoutLike] = None,
|
18
|
+
on_fail: ta.Optional[ta.Callable[[BaseException], None]] = None,
|
19
|
+
exception: ta.Union[ta.Type[BaseException], ta.Tuple[ta.Type[BaseException], ...]] = (ConnectionRefusedError,),
|
20
|
+
) -> bool:
|
21
|
+
timeout = Timeout.of(timeout)
|
22
|
+
|
23
|
+
try:
|
24
|
+
conn = socket.create_connection(address, timeout=timeout.or_(None))
|
25
|
+
|
26
|
+
except exception as e: # noqa
|
27
|
+
if on_fail is not None:
|
28
|
+
on_fail(e)
|
29
|
+
return False
|
30
|
+
|
31
|
+
else:
|
32
|
+
conn.close()
|
33
|
+
return True
|
34
|
+
|
35
|
+
|
11
36
|
def socket_wait_until_can_connect(
|
12
37
|
address: ta.Any,
|
13
38
|
*,
|
14
39
|
timeout: ta.Optional[TimeoutLike] = None,
|
15
40
|
on_fail: ta.Optional[ta.Callable[[BaseException], None]] = None,
|
16
41
|
sleep_s: float = .1,
|
17
|
-
exception: ta.Union[ta.Type[BaseException], ta.Tuple[ta.Type[BaseException], ...]] = (
|
42
|
+
exception: ta.Union[ta.Type[BaseException], ta.Tuple[ta.Type[BaseException], ...]] = (ConnectionRefusedError,),
|
18
43
|
cancel_event: ta.Optional[threading.Event] = None,
|
19
44
|
) -> None:
|
20
45
|
timeout = Timeout.of(timeout)
|
@@ -25,15 +50,12 @@ def socket_wait_until_can_connect(
|
|
25
50
|
while not cancel_event.is_set():
|
26
51
|
timeout()
|
27
52
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
else:
|
36
|
-
conn.close()
|
53
|
+
if socket_can_connect(
|
54
|
+
address,
|
55
|
+
timeout=timeout,
|
56
|
+
on_fail=on_fail,
|
57
|
+
exception=exception,
|
58
|
+
):
|
37
59
|
break
|
38
60
|
|
39
61
|
cancel_event.wait(min(sleep_s, timeout.remaining()))
|
@@ -1,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=vQTAIvR8OblSq-uP2GUfnbei0RnmAnM5j0T1-OToh9E,8253
|
2
|
-
omlish/__about__.py,sha256=
|
2
|
+
omlish/__about__.py,sha256=I6B5uZQiJsqsRd0Vxt8abjKC5YXkiwOJFtBfeWlVYAM,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
|
@@ -180,11 +180,11 @@ omlish/configs/processing/names.py,sha256=weHmaTclzgM9lUn3aBtw-kwZ3mc2N-CZlFg3Kd
|
|
180
180
|
omlish/configs/processing/rewriting.py,sha256=v7PfHtuTn5v_5Y6Au7oMN2Z0nxAMy1iYyO5CXnTvZhs,4226
|
181
181
|
omlish/configs/processing/strings.py,sha256=qFS2oh6z02IaM_q4lTKLdufzkJqAJ6J-Qjrz5S-QJoM,826
|
182
182
|
omlish/daemons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
183
|
-
omlish/daemons/daemon.py,sha256=
|
184
|
-
omlish/daemons/launching.py,sha256=
|
185
|
-
omlish/daemons/reparent.py,sha256=
|
186
|
-
omlish/daemons/services.py,sha256=
|
187
|
-
omlish/daemons/spawning.py,sha256=
|
183
|
+
omlish/daemons/daemon.py,sha256=3Wkvu8M_EaCKSpKI5UN5OayRXV0oVdF62tBss9_hlr0,3479
|
184
|
+
omlish/daemons/launching.py,sha256=sNOYW939IGI4ZlLQ0bKxzXj6EyeOiwV7Upqhd5XfoHc,3747
|
185
|
+
omlish/daemons/reparent.py,sha256=7uJ9oPGt9Ud7uA8bDl_SHcuqjcsmXa3kkjp9jf29wOw,585
|
186
|
+
omlish/daemons/services.py,sha256=jIlGWhiWoqQlm_OFeffkSs9jjr-icDF1-I-SNMEgg9Y,3406
|
187
|
+
omlish/daemons/spawning.py,sha256=psR73zOYjMKTqNpx1bMib8uU9wAZz62tw5TaWHrTdyY,5337
|
188
188
|
omlish/daemons/targets.py,sha256=00KmtlknMhQ5PyyVAhWl3rpeTMPym0GxvHHq6mYPZ7c,3051
|
189
189
|
omlish/daemons/waiting.py,sha256=RfgD1L33QQVbD2431dkKZGE4w6DUcGvYeRXXi8puAP4,1676
|
190
190
|
omlish/dataclasses/__init__.py,sha256=b7EZCIfHnEHCHWwgD3YXxkdsU-uYd9iD4hM36RgpI1g,1598
|
@@ -333,7 +333,7 @@ omlish/http/wsgi.py,sha256=czZsVUX-l2YTlMrUjKN49wRoP4rVpS0qpeBn4O5BoMY,948
|
|
333
333
|
omlish/http/coro/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
334
334
|
omlish/http/coro/fdio.py,sha256=bd9K4EYVWbXV3e3npDPXI9DuDAruJiyDmrgFpgNcjzY,4035
|
335
335
|
omlish/http/coro/server.py,sha256=30FTcJG8kuFeThf0HJYpTzMZN-giLTBP7wr5Wl3b9X0,18285
|
336
|
-
omlish/http/coro/simple.py,sha256=
|
336
|
+
omlish/http/coro/simple.py,sha256=inWA_ss6Nz5Rqmy4dL9_SGah4anYoDecDTRQqVIGYeY,3200
|
337
337
|
omlish/http/coro/sockets.py,sha256=rtpZZ-XCOfC5tXr4Fmo1HSn-8f5nxfIOlJaPUkQeDyU,1654
|
338
338
|
omlish/inject/__init__.py,sha256=n0RC9UDGsBQQ39cST39-XJqJPq2M0tnnh9yJubW9azo,1891
|
339
339
|
omlish/inject/binder.py,sha256=DAbc8TZi5w8Mna0TUtq0mT4jeDVA7i7SlBtOFrh2swc,4185
|
@@ -566,7 +566,7 @@ omlish/sockets/bind.py,sha256=J1SfFFFnVf3H5nqESDX2NGEY8DmjyIMUXZciZM33zQY,8003
|
|
566
566
|
omlish/sockets/handlers.py,sha256=Gj6xZoo4vommge8XvkehYw3B7O4aql2P4qzZIIa0p24,462
|
567
567
|
omlish/sockets/io.py,sha256=lfhTkB7NnAIx9kuQhAkwgsEUXY78Mp1_WtYrIQNS_k8,1408
|
568
568
|
omlish/sockets/ports.py,sha256=Wm4mRFFz5MdD8KbdaEfT1c4PbJnsuK_iyJlZJE_-8jo,1402
|
569
|
-
omlish/sockets/wait.py,sha256=
|
569
|
+
omlish/sockets/wait.py,sha256=aznyOzGa9oNBc31xnyk1S7TylO8hGx7vFyOVsdY3zFE,1585
|
570
570
|
omlish/sockets/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
571
571
|
omlish/sockets/server/handlers.py,sha256=PPsb1X5oU9dN8jfztaMGsRiqWTyEANT-1aSLbS6bUVg,3867
|
572
572
|
omlish/sockets/server/server.py,sha256=FkaishIxJuU4it9tTI7wzlGqJYzFGXzDrd_HgV0jAmU,6253
|
@@ -729,9 +729,9 @@ omlish/text/mangle.py,sha256=kfzFLfvepH-chl1P89_mdc5vC4FSqyPA2aVtgzuB8IY,1133
|
|
729
729
|
omlish/text/minja.py,sha256=jZC-fp3Xuhx48ppqsf2Sf1pHbC0t8XBB7UpUUoOk2Qw,5751
|
730
730
|
omlish/text/parts.py,sha256=JkNZpyR2tv2CNcTaWJJhpQ9E4F0yPR8P_YfDbZfMtwQ,6182
|
731
731
|
omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
|
732
|
-
omlish-0.0.0.
|
733
|
-
omlish-0.0.0.
|
734
|
-
omlish-0.0.0.
|
735
|
-
omlish-0.0.0.
|
736
|
-
omlish-0.0.0.
|
737
|
-
omlish-0.0.0.
|
732
|
+
omlish-0.0.0.dev244.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
733
|
+
omlish-0.0.0.dev244.dist-info/METADATA,sha256=7T41OvUEW2aOLkXNGyFicYfwqbooZHctPFTfjF0XpnQ,4176
|
734
|
+
omlish-0.0.0.dev244.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
735
|
+
omlish-0.0.0.dev244.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
736
|
+
omlish-0.0.0.dev244.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
737
|
+
omlish-0.0.0.dev244.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|