omlish 0.0.0.dev226__py3-none-any.whl → 0.0.0.dev228__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. omlish/__about__.py +3 -3
  2. omlish/diag/lslocks.py +4 -4
  3. omlish/diag/lsof.py +3 -4
  4. omlish/diag/ps.py +9 -0
  5. omlish/lite/timeouts.py +1 -1
  6. omlish/marshal/__init__.py +39 -24
  7. omlish/marshal/composite/__init__.py +0 -0
  8. omlish/marshal/{iterables.py → composite/iterables.py} +10 -10
  9. omlish/marshal/{literals.py → composite/literals.py} +9 -9
  10. omlish/marshal/{mappings.py → composite/mappings.py} +10 -10
  11. omlish/marshal/{maybes.py → composite/maybes.py} +11 -11
  12. omlish/marshal/{newtypes.py → composite/newtypes.py} +8 -8
  13. omlish/marshal/{optionals.py → composite/optionals.py} +9 -9
  14. omlish/marshal/objects/__init__.py +7 -0
  15. omlish/marshal/{dataclasses.py → objects/dataclasses.py} +24 -24
  16. omlish/marshal/{helpers.py → objects/helpers.py} +6 -3
  17. omlish/marshal/objects/marshal.py +108 -0
  18. omlish/marshal/objects/metadata.py +124 -0
  19. omlish/marshal/{namedtuples.py → objects/namedtuples.py} +16 -16
  20. omlish/marshal/objects/unmarshal.py +141 -0
  21. omlish/marshal/polymorphism/__init__.py +7 -0
  22. omlish/marshal/polymorphism/marshal.py +66 -0
  23. omlish/marshal/polymorphism/metadata.py +140 -0
  24. omlish/marshal/{unions.py → polymorphism/unions.py} +18 -18
  25. omlish/marshal/polymorphism/unmarshal.py +73 -0
  26. omlish/marshal/singular/__init__.py +0 -0
  27. omlish/marshal/{any.py → singular/any.py} +8 -8
  28. omlish/marshal/{base64.py → singular/base64.py} +9 -9
  29. omlish/marshal/{datetimes.py → singular/datetimes.py} +9 -9
  30. omlish/marshal/{enums.py → singular/enums.py} +9 -9
  31. omlish/marshal/{numbers.py → singular/numbers.py} +8 -8
  32. omlish/marshal/{primitives.py → singular/primitives.py} +8 -8
  33. omlish/marshal/{uuids.py → singular/uuids.py} +8 -8
  34. omlish/marshal/standard.py +32 -32
  35. omlish/os/death.py +70 -4
  36. omlish/os/fcntl.py +11 -12
  37. omlish/os/files.py +18 -3
  38. omlish/os/forkhooks.py +215 -0
  39. omlish/os/pidfiles/manager.py +4 -1
  40. omlish/os/pidfiles/pidfile.py +31 -11
  41. omlish/os/pidfiles/pinning.py +250 -0
  42. omlish/sockets/bind.py +4 -4
  43. {omlish-0.0.0.dev226.dist-info → omlish-0.0.0.dev228.dist-info}/METADATA +3 -3
  44. {omlish-0.0.0.dev226.dist-info → omlish-0.0.0.dev228.dist-info}/RECORD +48 -38
  45. omlish/marshal/objects.py +0 -317
  46. omlish/marshal/polymorphism.py +0 -267
  47. {omlish-0.0.0.dev226.dist-info → omlish-0.0.0.dev228.dist-info}/LICENSE +0 -0
  48. {omlish-0.0.0.dev226.dist-info → omlish-0.0.0.dev228.dist-info}/WHEEL +0 -0
  49. {omlish-0.0.0.dev226.dist-info → omlish-0.0.0.dev228.dist-info}/entry_points.txt +0 -0
  50. {omlish-0.0.0.dev226.dist-info → omlish-0.0.0.dev228.dist-info}/top_level.txt +0 -0
@@ -109,7 +109,7 @@ class Pidfile:
109
109
  del self._f
110
110
  return True
111
111
 
112
- def try_lock(self) -> bool:
112
+ def try_acquire_lock(self) -> bool:
113
113
  try:
114
114
  fcntl.flock(self._f, fcntl.LOCK_EX | fcntl.LOCK_NB)
115
115
  return True
@@ -117,14 +117,29 @@ class Pidfile:
117
117
  except OSError:
118
118
  return False
119
119
 
120
- def ensure_locked(self) -> None:
121
- if not self.try_lock():
122
- raise RuntimeError('Could not get lock')
120
+ #
121
+
122
+ class Error(Exception):
123
+ pass
124
+
125
+ class LockedError(Error):
126
+ pass
127
+
128
+ def acquire_lock(self) -> None:
129
+ if not self.try_acquire_lock():
130
+ raise self.LockedError
131
+
132
+ class NotLockedError(Error):
133
+ pass
134
+
135
+ def ensure_cannot_lock(self) -> None:
136
+ if self.try_acquire_lock():
137
+ raise self.NotLockedError
123
138
 
124
139
  #
125
140
 
126
141
  def write(self, pid: ta.Optional[int] = None) -> None:
127
- self.ensure_locked()
142
+ self.acquire_lock()
128
143
 
129
144
  if pid is None:
130
145
  pid = os.getpid()
@@ -135,18 +150,23 @@ class Pidfile:
135
150
  self._f.flush()
136
151
 
137
152
  def clear(self) -> None:
138
- self.ensure_locked()
153
+ self.acquire_lock()
139
154
 
140
155
  self._f.seek(0)
141
156
  self._f.truncate()
142
157
 
143
- def read(self) -> int:
144
- if self.try_lock():
145
- raise RuntimeError('Got lock')
158
+ #
159
+
160
+ def read(self) -> ta.Optional[int]:
161
+ self.ensure_cannot_lock()
146
162
 
147
163
  self._f.seek(0)
148
- return int(self._f.read())
164
+ buf = self._f.read()
165
+ if not buf:
166
+ return None
167
+ return int(buf)
149
168
 
150
169
  def kill(self, sig: int = signal.SIGTERM) -> None:
151
- pid = self.read()
170
+ if (pid := self.read()) is None:
171
+ raise self.Error(f'Pidfile locked but empty')
152
172
  os.kill(pid, sig)
@@ -0,0 +1,250 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ """
4
+ Notes:
5
+ - still racy as to if it's a different actual process as initial check, just with same pid, but due to 'identity' /
6
+ semantic meaning of the named pidfile the processes are considered equivalent
7
+
8
+ Strategies:
9
+ - linux
10
+ - get pid of owner (lslocks or F_GETLK)
11
+ - open pidfd to owner pid
12
+ - re-check pid of owner
13
+ - darwin
14
+ - get pids of referrers (lsof)
15
+ - read pid from file
16
+ - ensure pid is in referrers
17
+ - optionally loop
18
+ - ? setup pid death watcher? still a race
19
+ """
20
+ import abc
21
+ import contextlib
22
+ import os.path
23
+ import shutil
24
+ import sys
25
+ import time
26
+ import typing as ta
27
+
28
+ from ...diag.lslocks import LslocksCommand
29
+ from ...diag.lsof import LsofCommand
30
+ from ...lite.timeouts import Timeout
31
+ from ...lite.timeouts import TimeoutLike
32
+ from ...subprocesses.sync import subprocesses # noqa
33
+ from .pidfile import Pidfile
34
+
35
+
36
+ ##
37
+
38
+
39
+ class PidfilePinner(abc.ABC):
40
+ def __init__(
41
+ self,
42
+ *,
43
+ sleep_s: float = .1,
44
+ ) -> None:
45
+ super().__init__()
46
+
47
+ self._sleep_s = sleep_s
48
+
49
+ @classmethod
50
+ @abc.abstractmethod
51
+ def is_available(cls) -> bool:
52
+ raise NotImplementedError
53
+
54
+ class NoOwnerError(Exception):
55
+ pass
56
+
57
+ @abc.abstractmethod
58
+ def _pin_pidfile_owner(self, pidfile: Pidfile, timeout: Timeout) -> ta.ContextManager[int]:
59
+ raise NotImplementedError
60
+
61
+ @contextlib.contextmanager
62
+ def pin_pidfile_owner(
63
+ self,
64
+ path: str,
65
+ *,
66
+ timeout: ta.Optional[TimeoutLike] = None,
67
+ ) -> ta.Iterator[int]:
68
+ timeout = Timeout.of(timeout)
69
+
70
+ if not os.path.isfile(path):
71
+ raise self.NoOwnerError
72
+
73
+ with Pidfile(path, inheritable=False) as pf:
74
+ try:
75
+ with self._pin_pidfile_owner(pf, timeout) as pid:
76
+ yield pid
77
+
78
+ except Pidfile.NotLockedError:
79
+ raise self.NoOwnerError from None
80
+
81
+ @classmethod
82
+ def default_impl(cls) -> ta.Type['PidfilePinner']:
83
+ for impl in [
84
+ LslocksPidfdPidfilePinner,
85
+ LsofPidfilePinner,
86
+ ]:
87
+ if impl.is_available():
88
+ return impl
89
+ return UnverifiedPidfilePinner
90
+
91
+
92
+ ##
93
+
94
+
95
+ class UnverifiedPidfilePinner(PidfilePinner):
96
+ @classmethod
97
+ def is_available(cls) -> bool:
98
+ return True
99
+
100
+ @contextlib.contextmanager
101
+ def _pin_pidfile_owner(self, pidfile: Pidfile, timeout: Timeout) -> ta.Iterator[int]:
102
+ while (pid := pidfile.read()) is None:
103
+ time.sleep(self._sleep_s)
104
+ timeout()
105
+
106
+ yield pid
107
+
108
+
109
+ ##
110
+
111
+
112
+ class LsofPidfilePinner(PidfilePinner):
113
+ """
114
+ Fundamentally wrong, but still better than nothing. Simply reads the file contents and ensures a valid contained pid
115
+ has the file open via `lsof`.
116
+ """
117
+
118
+ @classmethod
119
+ def is_available(cls) -> bool:
120
+ return shutil.which('lsof') is not None
121
+
122
+ def _try_read_and_verify(self, pf: Pidfile, timeout: Timeout) -> ta.Optional[int]:
123
+ if (initial_pid := pf.read()) is None:
124
+ return None
125
+
126
+ lsof_output = LsofCommand(
127
+ # pid=initial_pid,
128
+ file=os.path.abspath(pf.path),
129
+ ).run(
130
+ timeout=timeout,
131
+ )
132
+
133
+ lsof_pids: ta.Set[int] = set()
134
+ for li in lsof_output:
135
+ if li.pid is None:
136
+ continue
137
+ try:
138
+ li_pid = int(li.pid)
139
+ except ValueError:
140
+ continue
141
+ lsof_pids.add(li_pid)
142
+
143
+ if initial_pid not in lsof_pids:
144
+ return None
145
+
146
+ if (reread_pid := pf.read()) is None or reread_pid != initial_pid:
147
+ return None
148
+
149
+ return reread_pid
150
+
151
+ @contextlib.contextmanager
152
+ def _pin_pidfile_owner(self, pidfile: Pidfile, timeout: Timeout) -> ta.Iterator[int]:
153
+ while (pid := self._try_read_and_verify(pidfile, timeout)) is None:
154
+ time.sleep(self._sleep_s)
155
+ timeout()
156
+
157
+ yield pid
158
+
159
+
160
+ ##
161
+
162
+
163
+ class LslocksPidfdPidfilePinner(PidfilePinner):
164
+ """
165
+ Finds the locking pid via `lslocks`, opens a pidfd, then re-runs `lslocks` and rechecks the locking pid is the same.
166
+ """
167
+
168
+ @classmethod
169
+ def is_available(cls) -> bool:
170
+ return sys.platform == 'linux' and shutil.which('lslocks') is not None
171
+
172
+ def _read_locking_pid(self, path: str, timeout: Timeout) -> int:
173
+ lsl_output = LslocksCommand().run(timeout=timeout)
174
+
175
+ lsl_pids = {
176
+ li.pid
177
+ for li in lsl_output
178
+ if li.path == path
179
+ and li.type == 'FLOCK'
180
+ }
181
+ if not lsl_pids:
182
+ raise self.NoOwnerError
183
+ if len(lsl_pids) != 1:
184
+ raise RuntimeError(f'Multiple locks on file: {path}')
185
+
186
+ [pid] = lsl_pids
187
+ return pid
188
+
189
+ class _Result(ta.NamedTuple):
190
+ pid: int
191
+ pidfd: int
192
+
193
+ def _try_read_and_verify(
194
+ self,
195
+ pidfile: Pidfile,
196
+ timeout: Timeout,
197
+ ) -> ta.Optional[_Result]:
198
+ path = os.path.abspath(pidfile.path)
199
+ initial_pid = self._read_locking_pid(path, timeout)
200
+
201
+ try:
202
+ pidfd = os.open(f'/proc/{initial_pid}', os.O_RDONLY)
203
+ except FileNotFoundError:
204
+ raise self.NoOwnerError from None
205
+
206
+ try:
207
+ reread_pid = self._read_locking_pid(path, timeout)
208
+ if reread_pid != initial_pid:
209
+ os.close(pidfd)
210
+ return None
211
+
212
+ return self._Result(initial_pid, pidfd)
213
+
214
+ except BaseException:
215
+ os.close(pidfd)
216
+ raise
217
+
218
+ @contextlib.contextmanager
219
+ def _pin_pidfile_owner(
220
+ self,
221
+ pidfile: Pidfile,
222
+ timeout: Timeout,
223
+ ) -> ta.Iterator[int]:
224
+ while (res := self._try_read_and_verify(pidfile, timeout)) is None:
225
+ time.sleep(self._sleep_s)
226
+ timeout()
227
+
228
+ try:
229
+ yield res.pid
230
+ finally:
231
+ os.close(res.pidfd)
232
+
233
+
234
+ ##
235
+
236
+
237
+ if __name__ == '__main__':
238
+ def _main() -> None:
239
+ argparse = __import__('argparse')
240
+ parser = argparse.ArgumentParser()
241
+ parser.add_argument('file')
242
+ args = parser.parse_args()
243
+
244
+ with PidfilePinner.default_impl()().pin_pidfile_owner(
245
+ args.file,
246
+ timeout=5.,
247
+ ) as pid:
248
+ print(pid)
249
+
250
+ _main()
omlish/sockets/bind.py CHANGED
@@ -15,10 +15,10 @@ import socket as socket_
15
15
  import stat
16
16
  import typing as ta
17
17
 
18
- from omlish.lite.check import check
19
- from omlish.lite.dataclasses import dataclass_maybe_post_init
20
- from omlish.sockets.addresses import SocketAddress
21
- from omlish.sockets.addresses import SocketAndAddress
18
+ from ..lite.check import check
19
+ from ..lite.dataclasses import dataclass_maybe_post_init
20
+ from .addresses import SocketAddress
21
+ from .addresses import SocketAndAddress
22
22
 
23
23
 
24
24
  SocketBinderT = ta.TypeVar('SocketBinderT', bound='SocketBinder')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omlish
3
- Version: 0.0.0.dev226
3
+ Version: 0.0.0.dev228
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -32,7 +32,7 @@ Requires-Dist: cbor2~=5.6; extra == "all"
32
32
  Requires-Dist: cloudpickle~=3.1; extra == "all"
33
33
  Requires-Dist: httpx[http2]~=0.28; extra == "all"
34
34
  Requires-Dist: wrapt~=1.17; extra == "all"
35
- Requires-Dist: cryptography~=43.0; extra == "all"
35
+ Requires-Dist: cryptography~=44.0; extra == "all"
36
36
  Requires-Dist: sqlalchemy[asyncio]~=2.0; extra == "all"
37
37
  Requires-Dist: pg8000~=1.31; extra == "all"
38
38
  Requires-Dist: pymysql~=1.1; extra == "all"
@@ -76,7 +76,7 @@ Requires-Dist: httpx[http2]~=0.28; extra == "http"
76
76
  Provides-Extra: misc
77
77
  Requires-Dist: wrapt~=1.17; extra == "misc"
78
78
  Provides-Extra: secrets
79
- Requires-Dist: cryptography~=43.0; extra == "secrets"
79
+ Requires-Dist: cryptography~=44.0; extra == "secrets"
80
80
  Provides-Extra: sqlalchemy
81
81
  Requires-Dist: sqlalchemy[asyncio]~=2.0; extra == "sqlalchemy"
82
82
  Provides-Extra: sqldrivers
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=YGmAnUBszmosQQ_7Hh2wwtDiYdYZ4unNKYzOtALuels,7968
2
- omlish/__about__.py,sha256=QR1QmjxqAJ538XauJdEuHH505rJ9QF67p3blNgi6kwc,3380
2
+ omlish/__about__.py,sha256=7YnXltr9so9p-_7zMJK4FLVkWPEZyjsuxoimC7Jjpb4,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
@@ -203,11 +203,11 @@ omlish/dataclasses/impl/utils.py,sha256=aER2iL3UAtgS1BdLuEvTr9Tr2wC28wk1kiOeO-jI
203
203
  omlish/diag/__init__.py,sha256=4S8v0myJM4Zld6_FV6cPe_nSv0aJb6kXftEit0HkiGE,1141
204
204
  omlish/diag/asts.py,sha256=BveUUNUcaAm4Hg55f4ZxGSI313E4L8cCZ5XjHpEkKVI,3325
205
205
  omlish/diag/debug.py,sha256=ClED7kKXeVMyKrjGIxcq14kXk9kvUJfytBQwK9y7c4Q,1637
206
- omlish/diag/lslocks.py,sha256=7I_u-Vx0Dz0KN59Y6dk9nBZynHa6usvKIXVSywGCiWE,1722
207
- omlish/diag/lsof.py,sha256=gMtNmfBo1AlYmvZWBB51u4NfbfC6yJyEVwfr1HgktP8,9328
206
+ omlish/diag/lslocks.py,sha256=fWI3SZwgEkhipVfSqvzVzREJRShcDYmlYByHBT0LToc,1744
207
+ omlish/diag/lsof.py,sha256=DnowqvKYah-WCuBHS3DAcZCvlsWJdM9kYNFq97UZDDA,9127
208
208
  omlish/diag/procfs.py,sha256=KaGTAA2Gj8eEEp7MjClRe4aimwzd-HDABThFzvq2cBQ,9684
209
209
  omlish/diag/procstats.py,sha256=UkqxREqfd-38xPYZ9T1SIJISz5ARQCEhTtOZrxtm2dE,777
210
- omlish/diag/ps.py,sha256=mwraPO6Nze_2r9RiC4dyLL0aJ_lHR1_fSN9BlYNBoHw,1651
210
+ omlish/diag/ps.py,sha256=b7ai9O4mGZliNFvBu6PdQfMmct4qpcMTygEf1ISHBLQ,1666
211
211
  omlish/diag/pycharm.py,sha256=7-r_F-whXt8v-0dehxAX-MeMFPM3iZXX9IfeL0GfUtk,4643
212
212
  omlish/diag/pydevd.py,sha256=UN55ZjkWLCVyHxE2CNRRYamuvSKfzWsn0D5oczRTXO4,7536
213
213
  omlish/diag/threads.py,sha256=1-x02VCDZ407gfbtXm1pWK-ubqhqfePm9PMqkHCVoqk,3642
@@ -433,7 +433,7 @@ omlish/lite/resources.py,sha256=YNSmX1Ohck1aoWRs55a-o5ChVbFJIQhtbqE-XwF55Oc,326
433
433
  omlish/lite/runtime.py,sha256=XQo408zxTdJdppUZqOWHyeUR50VlCpNIExNGHz4U6O4,459
434
434
  omlish/lite/secrets.py,sha256=3Mz3V2jf__XU9qNHcH56sBSw95L3U2UPL24bjvobG0c,816
435
435
  omlish/lite/strings.py,sha256=QGxT1Yh4oI8ycsfeobxnjEhvDob_GiAKLeIhZwo1j24,1986
436
- omlish/lite/timeouts.py,sha256=Bd_XaEmaGTsKcORfPt-AJECmf8Bt9XwjBfjkMacpEdM,4954
436
+ omlish/lite/timeouts.py,sha256=wZ6PP1D3B8jpAI7-cQm7wkGTh2sE366XIn6FSMH6DZ4,4959
437
437
  omlish/lite/timing.py,sha256=aVu3hEDB_jyTF_ryZI7iU-xg4q8CNwqpp9Apfru_iwY,196
438
438
  omlish/lite/types.py,sha256=fP5EMyBdEp2LmDxcHjUDtwAMdR06ISr9lKOL7smWfHM,140
439
439
  omlish/lite/typing.py,sha256=U3-JaEnkDSYxK4tsu_MzUn3RP6qALBe5FXQXpD-licE,1090
@@ -455,37 +455,45 @@ omlish/logs/utils.py,sha256=mzHrZ9ji75p5A8qR29eUr05CBAHMb8J753MSkID_VaQ,393
455
455
  omlish/manifests/__init__.py,sha256=P2B0dpT8D7l5lJwRGPA92IcQj6oeXfd90X5-q9BJrKg,51
456
456
  omlish/manifests/load.py,sha256=LrWAvBfdzDkFdLuVwfw2RwFvLjxx-rvfkpU9eBsWeIc,5626
457
457
  omlish/manifests/types.py,sha256=d8bv5tknCJqclRfxCpao_8XxHo2yofhLpVHQTB-MfNw,260
458
- omlish/marshal/__init__.py,sha256=iVA7n31L08Bdub6HKPvYOXVvDhk2CMA6rPeKDL_u1to,2298
459
- omlish/marshal/any.py,sha256=e82OyYK3Emm1P1ClnsnxP7fIWC2iNVyW0H5nK4mLmWM,779
458
+ omlish/marshal/__init__.py,sha256=o7EtBHbciL3OLz6zsMny-UGjyAfFXO1zRGIqVOPEKw0,2591
460
459
  omlish/marshal/base.py,sha256=HEzfby-PgGzIhiRpBkFrkw5-hKacRSC5W_jwLjT8aYw,6740
461
- omlish/marshal/base64.py,sha256=F-3ogJdcFCtWINRgJgWT0rErqgx6f4qahhcg8OrkqhE,1089
462
- omlish/marshal/dataclasses.py,sha256=ZpfNaIdhKwWouWkoQGuequKwrEeehbz9QSQoW3acyPo,7756
463
- omlish/marshal/datetimes.py,sha256=0ffg8cEvx9SMKIXZGD9b7MqpLfmgw0uKKdn6YTfoqok,3714
464
- omlish/marshal/enums.py,sha256=CMAbx6RI2EcQoo7SoD-5q2l-3DFKreWMiOxs6mFpl_4,1472
465
460
  omlish/marshal/exceptions.py,sha256=jwQWn4LcPnadT2KRI_1JJCOSkwWh0yHnYK9BmSkNN4U,302
466
461
  omlish/marshal/factories.py,sha256=Q926jSVjaQLEmStnHLhm_c_vqEysN1LnDCwAsFLIzXw,2970
467
462
  omlish/marshal/forbidden.py,sha256=NDe828hqCQw-AgxcEm8MiDZNxWoBwf3o7sTyvQsSsQ0,867
468
463
  omlish/marshal/global_.py,sha256=K76wB1-pdg4VWgiqR7wyxRNYr-voJApexYW2nV-R4DM,1127
469
- omlish/marshal/helpers.py,sha256=-SOgYJmrURILHpPK6Wu3cCvhj8RJrqfJxuKhh9UMs7o,1102
470
- omlish/marshal/iterables.py,sha256=H9FoCB5RlJW0SVSi3SBD6sxOXN9XRTOUkHRwCYSqRb8,2632
471
- omlish/marshal/literals.py,sha256=m92biF9-PwHGVBx3zG3s2YvklLeOQ7T5IOdgwBJr-NU,1599
472
- omlish/marshal/mappings.py,sha256=XcNOaV708ZHeuIrWiFHC6F1O6U9NyyTKUurvXwIryJo,2789
473
- omlish/marshal/maybes.py,sha256=i-gOQJ-7tdt6sOazAeyCh4N71SK3jWv-BKlkx-fZm-s,2200
474
- omlish/marshal/namedtuples.py,sha256=H0icxcE5FrqgfI9dRc8LlXJy430g_yuILTCVD0Zvfd0,2799
475
464
  omlish/marshal/naming.py,sha256=lIklR_Od4x1ghltAgOzqcKhHs-leeSv2YmFhCHO7GIs,613
476
- omlish/marshal/newtypes.py,sha256=fRpXLoCpoiaqcvl7v92I1_Qt7udn4vsPc1P3UfcBu-8,841
477
465
  omlish/marshal/nop.py,sha256=2mWve_dicFAiUQ2Y5asKkUW-XGmEE9Qi2ClIasFad0c,461
478
- omlish/marshal/numbers.py,sha256=kFRIX9l1yofiYzafV6SnYfEg0PiCsAqeRHOeT6BSxlM,1672
479
- omlish/marshal/objects.py,sha256=74tUmMymimSqgd4a6kyMh_owJe6J7YQXwCXEF-JWt1c,8419
480
- omlish/marshal/optionals.py,sha256=r0XB5rqfasvgZJNrKYd6Unq2U4nHt3JURi26j0dYHlw,1499
481
- omlish/marshal/polymorphism.py,sha256=2SxrfneA9QdhNdxieEGFnHDHpUo3ftETA9dMbCbmbWY,6511
482
- omlish/marshal/primitives.py,sha256=f_6m24Cb-FDGsZpYSas11nLt3xCCEUXugw3Hv4-aNhg,1291
483
466
  omlish/marshal/registries.py,sha256=FvC6qXHCizNB2QmU_N3orxW7iqfGYkiUXYYdTRWS6HA,2353
484
- omlish/marshal/standard.py,sha256=-iA2o674AW1uNVabcA9Dtr6z0gyFS5yLP80nBQ8R5qY,3479
485
- omlish/marshal/unions.py,sha256=tT4W-mxuPi7s_kdl25_AUltsurYPO_0mQl2CiVmNonY,4357
467
+ omlish/marshal/standard.py,sha256=ncaoD_OyCzS5H8bt3WAp5z7k1mWPFeOgAYBVaBLq4Sg,3783
486
468
  omlish/marshal/utils.py,sha256=puKJpwPpuDlMOIrKMcLTRLJyMiL6n_Xs-p59AuDEymA,543
487
- omlish/marshal/uuids.py,sha256=H4B7UX_EPNmP2tC8bubcKrPLTS4aQu98huvbXQ3Zv2g,910
488
469
  omlish/marshal/values.py,sha256=ssHiWdg_L6M17kAn8GiGdPW7UeQOm3RDikWkvwblf5I,263
470
+ omlish/marshal/composite/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
471
+ omlish/marshal/composite/iterables.py,sha256=tOMjJ-nyYBrRnAGAlT2scV3iR9QSic8La-1qFcy9yNk,2642
472
+ omlish/marshal/composite/literals.py,sha256=0fTwQVSQJe9L17grcLq5hMuoYD7vHRFgdHR5-Teq0g8,1608
473
+ omlish/marshal/composite/mappings.py,sha256=92oAqYzzTdRNhFDtmukpJd8anoi3pt2nX5XhLTlM_Dg,2799
474
+ omlish/marshal/composite/maybes.py,sha256=4rxTv769Uk4gQGAnE6urVnMscF1vhcnwqP_lm5QzSoM,2211
475
+ omlish/marshal/composite/newtypes.py,sha256=lYCacdR2BlLWMT1kc8o08UjsmJ6aYlAHkrzG8BozWCM,849
476
+ omlish/marshal/composite/optionals.py,sha256=iQgSrb9Xyb_IP81YREAZ0xKYXImLgMILGLBnYL2lFWw,1508
477
+ omlish/marshal/objects/__init__.py,sha256=F4wej8L_tedC8ETYxAnmKfdPR9TjsqIus9Z3nZofYuc,182
478
+ omlish/marshal/objects/dataclasses.py,sha256=Fo15maLrWICq4H3YlZpa0PjnJTC4eqG1IF0C-88qCKs,7780
479
+ omlish/marshal/objects/helpers.py,sha256=85GZp4h3Yo0GYGmnZpKgNxkWnSk8h2R21nfDLU2DtM0,1110
480
+ omlish/marshal/objects/marshal.py,sha256=rdZsDWKgLBZxV0ZQlwHTJv9iUg0lC57trJHBOFKvuLo,2636
481
+ omlish/marshal/objects/metadata.py,sha256=QVD7DRhXbLda_Edgpc3OD1xT9fthmeaaj6l_nTE1PMM,3307
482
+ omlish/marshal/objects/namedtuples.py,sha256=ENWyiYZhTwDRqUkkWrHeNcoHGEo5GW634jMuBUc3654,2815
483
+ omlish/marshal/objects/unmarshal.py,sha256=-83sSgL6WX6C3JfXZr64ZLYi3SsJC9kyiGAbEQbVHQI,3620
484
+ omlish/marshal/polymorphism/__init__.py,sha256=e2UTrSL0qp7w_1vkdxDWd7sXlWhep2KPV49-BB64ma8,130
485
+ omlish/marshal/polymorphism/marshal.py,sha256=Lraih0Dhkcwu6Bb6QkrYOWoMIubh46VQkXxJQWisA8Y,1827
486
+ omlish/marshal/polymorphism/metadata.py,sha256=hbXqkYKXhfJeMllzI0Ubeeqbq201khVN8uprgVhMpHM,3046
487
+ omlish/marshal/polymorphism/unions.py,sha256=YwsK9T3okGiHj88LTFNdUvuH7VkCsD-aTnZseKzhpdA,4350
488
+ omlish/marshal/polymorphism/unmarshal.py,sha256=gdWTgpl4hXBqac1gHmg_23VMb7x42CE2x-3oRpCaLwU,2047
489
+ omlish/marshal/singular/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
490
+ omlish/marshal/singular/any.py,sha256=42XmUbX_AaiHGFwS2QmA-huPCcZ1_jzVyrnBUYVm21U,787
491
+ omlish/marshal/singular/base64.py,sha256=gQIkFN60lNEuSiu3MDQFoHwMZTtzDbrBRtNpFZ2HXew,1098
492
+ omlish/marshal/singular/datetimes.py,sha256=1PDVFF6TGkGxGUno8TaFGRof0DQUookYf_X2Nl8T4V0,3723
493
+ omlish/marshal/singular/enums.py,sha256=0FSRj8fiM9LrRrFtlWrMsjG-ualfirGNSP0H-36J4sA,1481
494
+ omlish/marshal/singular/numbers.py,sha256=12a8HxjDHMTgtJTxO7VY_h5llaPyYB-QEPY09dUFOec,1680
495
+ omlish/marshal/singular/primitives.py,sha256=EZi2AIi_Onw1pekAiL4ujz4w-ASuOGWCu4MUIE_dCnk,1299
496
+ omlish/marshal/singular/uuids.py,sha256=rZoE6NE5z6x_ZRmVMjjR2iK7CR8ijfDOvhMKgmm8Mgk,918
489
497
  omlish/math/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
490
498
  omlish/math/bits.py,sha256=yip1l8agOYzT7bFyMGc0RR3XlnGCfHMpjw_SECLLh1I,3477
491
499
  omlish/math/floats.py,sha256=UimhOT7KRl8LXTzOI5cQWoX_9h6WNWe_3vcOuO7-h_8,327
@@ -495,18 +503,20 @@ omlish/multiprocessing/proxies.py,sha256=bInhGds2rv6xT9q3qRMlZuSXFAjwfspkiohXZ36
495
503
  omlish/multiprocessing/spawn.py,sha256=sTvPLIJGYnjjI6ASqhFzHF-97tCnaOXX7u7s-33SUMw,1875
496
504
  omlish/os/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
497
505
  omlish/os/atomics.py,sha256=KhWNeh4mzU3M-TF0v8uR6hUqMfZJW42MeyIK9Jl6R0k,5246
498
- omlish/os/death.py,sha256=_JV88yxwYIalamIMuqmGTQ4KYd0t-UHLjVkdoliPWL8,2943
506
+ omlish/os/death.py,sha256=kxcAWw91zvvWT5qxXbKVg-YEOJXxx658VCdkCTNPTag,4825
499
507
  omlish/os/deathsig.py,sha256=hk9Yq2kyDdI-cI7OQH7mOfpRbOKzY_TfPKEqgrjVYbA,641
500
- omlish/os/fcntl.py,sha256=hSlCqzMu5iqEsxlp9me6KtoG9FK7g3sdjZIpDba-kLQ,1458
501
- omlish/os/files.py,sha256=O1Um1iCLeIzDSKQq2zByVC_4NZ3tLMqv6vICyU21U3Q,861
508
+ omlish/os/fcntl.py,sha256=riQf9iEEEIC28lJp8ud06MU56w2XJHJ9nBFtck_hdhc,1501
509
+ omlish/os/files.py,sha256=WJ_42vsZIZukQURN3TTccp-n74ZNhbux_ps3TLbHj18,1106
510
+ omlish/os/forkhooks.py,sha256=PbcMrPF_7D0QBm73e55I_XIk4yQzsdj-L6rpe_eyAFM,5024
502
511
  omlish/os/journald.py,sha256=2nI8Res1poXkbLc31--MPUlzYMESnCcPUkIxDOCjZW0,3903
503
512
  omlish/os/linux.py,sha256=whJ6scwMKSFBdXiVhJW0BCpJV4jOGMr-a_a3Bhwz6Ls,18938
504
513
  omlish/os/paths.py,sha256=hqPiyg_eYaRoIVPdAeX4oeLEV4Kpln_XsH0tHvbOf8Q,844
505
514
  omlish/os/sizes.py,sha256=ohkALLvqSqBX4iR-7DMKJ4pfOCRdZXV8htH4QywUNM0,152
506
515
  omlish/os/temp.py,sha256=P97KiVeNB7rfGn4tlgU5ro86JUxAsiphLMlxsjQgfB0,1198
507
516
  omlish/os/pidfiles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
508
- omlish/os/pidfiles/manager.py,sha256=ssnxCvSoL3OapzcMmxSgiy9o1NogR6PTX4LLTJXuPkM,2830
509
- omlish/os/pidfiles/pidfile.py,sha256=833aPCV7cQqFqYnxwma8np0eoy0CJzVHRskn-YSQwis,3378
517
+ omlish/os/pidfiles/manager.py,sha256=XP_0ynjWDG2wjRN3sIKjmFafpf-er3AYm8E7aXs16fA,3209
518
+ omlish/os/pidfiles/pidfile.py,sha256=WmZt_c8fvztPgZQnYHhcQCKWgHqAEsaI3Ggz6Wqgkc8,3748
519
+ omlish/os/pidfiles/pinning.py,sha256=_AwYjJc1UGX7mdCOk4mItJJcsOJo3RW2ebBOm2noW5Y,6359
510
520
  omlish/reflect/__init__.py,sha256=JBWwxKwP4IEaomkK0PTju02STU1BVXT14SCrShT1Sm0,769
511
521
  omlish/reflect/inspect.py,sha256=veJ424-9oZrqyvhVpvxOi7hcKW-PDBkdYL2yjrFlk4o,495
512
522
  omlish/reflect/ops.py,sha256=RJ6jzrM4ieFsXzWyNXWV43O_WgzEaUvlHSc5N2ezW2A,2044
@@ -525,7 +535,7 @@ omlish/secrets/subprocesses.py,sha256=ffjfbgPbEE_Pwb_87vG4yYR2CGZy3I31mHNCo_0JtH
525
535
  omlish/secrets/tempssl.py,sha256=tlwRrbHHvgKJtNAC31I5sDKryya4fagqN6kGt-tV4Qg,1874
526
536
  omlish/sockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
527
537
  omlish/sockets/addresses.py,sha256=vbVeQBkzI513H4vRv-JS89QtRbr9U8v5zqkm3oODl_s,1869
528
- omlish/sockets/bind.py,sha256=TnG5nm0pnuMxRA02TG2W40RbutrPA6tkOJtbZvBjDWU,8063
538
+ omlish/sockets/bind.py,sha256=uRHZJvBxWXadd3Qt7deDEJ1rWzjC-lVodRgPA6VMwNU,8025
529
539
  omlish/sockets/handlers.py,sha256=Gj6xZoo4vommge8XvkehYw3B7O4aql2P4qzZIIa0p24,462
530
540
  omlish/sockets/io.py,sha256=lfhTkB7NnAIx9kuQhAkwgsEUXY78Mp1_WtYrIQNS_k8,1408
531
541
  omlish/sockets/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -679,9 +689,9 @@ omlish/text/indent.py,sha256=YjtJEBYWuk8--b9JU_T6q4yxV85_TR7VEVr5ViRCFwk,1336
679
689
  omlish/text/minja.py,sha256=jZC-fp3Xuhx48ppqsf2Sf1pHbC0t8XBB7UpUUoOk2Qw,5751
680
690
  omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
681
691
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
682
- omlish-0.0.0.dev226.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
683
- omlish-0.0.0.dev226.dist-info/METADATA,sha256=0HcF368bfjlzkGzprdbRX4GJagL5c9PDPRUqiBYWIlY,4176
684
- omlish-0.0.0.dev226.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
685
- omlish-0.0.0.dev226.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
686
- omlish-0.0.0.dev226.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
687
- omlish-0.0.0.dev226.dist-info/RECORD,,
692
+ omlish-0.0.0.dev228.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
693
+ omlish-0.0.0.dev228.dist-info/METADATA,sha256=-HhLcpKh_NMYQIcIENf1Dyen3kQU2vxd2nAe_weG7wk,4176
694
+ omlish-0.0.0.dev228.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
695
+ omlish-0.0.0.dev228.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
696
+ omlish-0.0.0.dev228.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
697
+ omlish-0.0.0.dev228.dist-info/RECORD,,