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 CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev243'
2
- __revision__ = '97467f9facc16b72c3dac69aba18c543dce228b1'
1
+ __version__ = '0.0.0.dev244'
2
+ __revision__ = 'e7768b1afd9f43a5f1534c2840691f48c0a89efb'
3
3
 
4
4
 
5
5
  #
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) -> None:
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()
@@ -53,8 +53,10 @@ class Launcher:
53
53
  self,
54
54
  *,
55
55
  pidfile_manager: ta.ContextManager | None,
56
- launched_callback: ta.Callable[[], None] | None = None,
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 launched_callback is not None:
70
- launched_callback()
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 launched_callback is not None:
77
- launched_callback()
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
- launched_callback=launched_event.set if launched_event is not None else None,
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
 
@@ -2,7 +2,10 @@ import os
2
2
  import sys
3
3
 
4
4
 
5
- def reparent_process() -> None:
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()
@@ -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: Service | Service.Config
87
+ class ServiceDaemon(lang.Final, ta.Generic[ServiceT, ServiceConfigT]):
88
+ service: ServiceT | ServiceConfigT
87
89
 
88
90
  @cached.function
89
- def service_(self) -> Service:
90
- if isinstance(self.service, Service):
91
- return self.service
92
- elif isinstance(self.service, Service.Config):
93
- return Service.from_config(self.service)
94
- else:
95
- raise TypeError(self.service)
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
- if isinstance(self.daemon, Daemon):
104
- return self.daemon
105
- elif isinstance(self.daemon, Daemon.Config):
106
- return Daemon(Target.of(self.service_()), self.daemon)
107
- else:
108
- raise TypeError(self.daemon)
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)
@@ -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: str | None = None
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
- non_daemon: bool = False
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
- if (start_method := self._spawning.start_method) is None:
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 == 'fork':
86
- ctx = mp.get_context(check.non_empty_str(start_method))
114
+ if start_method == MultiprocessingSpawning.StartMethod.FORK:
115
+ ctx = mp.get_context(check.non_empty_str('fork'))
87
116
 
88
- elif start_method == 'spawn':
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=spawn.fn,
102
- daemon=not self._spawning.non_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
- non_daemon: bool = False
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.non_daemon,
199
+ daemon=not self._spawning.linger,
160
200
  )
161
201
  self._thread.start()
162
202
 
@@ -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], ...]] = (Exception,),
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
- try:
29
- conn = socket.create_connection(address, timeout=timeout.or_(None))
30
-
31
- except exception as e: # noqa
32
- if on_fail is not None:
33
- on_fail(e)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omlish
3
- Version: 0.0.0.dev243
3
+ Version: 0.0.0.dev244
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=vQTAIvR8OblSq-uP2GUfnbei0RnmAnM5j0T1-OToh9E,8253
2
- omlish/__about__.py,sha256=3c_F9MxYda9rfa68tINgteoJ57COTQpld-0xkq1JRwY,3380
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=ykdbCPbpxKrdZVZc892SnedTdftTAYt_YdDpYchKcUE,3410
184
- omlish/daemons/launching.py,sha256=mhtkuAO16STcznUl3rrX9pacfrKbPQRCP2AllKL4B70,3664
185
- omlish/daemons/reparent.py,sha256=UaG2X6VJHJPOlUwHPNRH3aWGgF0Fg771jjO9IRPLlyY,280
186
- omlish/daemons/services.py,sha256=UAzzdP4jG0-piVzz6CsSTPIjTGt4VFXtbzP7KczMCho,2354
187
- omlish/daemons/spawning.py,sha256=cx00xeqSrfhlFbjCtKqaBHvMuHwB9hdjuKNHzAAo_dw,4030
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=_ZKFlfLda9Gatd3bNBNGJpITNQl4tuTAbL3P-Mr5j5w,3152
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=FSHzLR66RESMwHCV-Bu_mZWNNskRGfEwvM9q1arapV0,1049
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.dev243.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
733
- omlish-0.0.0.dev243.dist-info/METADATA,sha256=dvum371m78Sb6B5v3K-C8hyOFzA8rEjIQUyy8GZH2G0,4176
734
- omlish-0.0.0.dev243.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
735
- omlish-0.0.0.dev243.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
736
- omlish-0.0.0.dev243.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
737
- omlish-0.0.0.dev243.dist-info/RECORD,,
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,,