omlish 0.0.0.dev227__py3-none-any.whl → 0.0.0.dev229__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 +3 -3
- omlish/diag/lsof.py +4 -5
- omlish/diag/ps.py +9 -0
- omlish/lite/timeouts.py +1 -1
- omlish/marshal/__init__.py +39 -24
- omlish/marshal/composite/__init__.py +0 -0
- omlish/marshal/{iterables.py → composite/iterables.py} +10 -10
- omlish/marshal/{literals.py → composite/literals.py} +9 -9
- omlish/marshal/{mappings.py → composite/mappings.py} +10 -10
- omlish/marshal/{maybes.py → composite/maybes.py} +11 -11
- omlish/marshal/{newtypes.py → composite/newtypes.py} +8 -8
- omlish/marshal/{optionals.py → composite/optionals.py} +9 -9
- omlish/marshal/objects/__init__.py +7 -0
- omlish/marshal/{dataclasses.py → objects/dataclasses.py} +24 -24
- omlish/marshal/{helpers.py → objects/helpers.py} +6 -3
- omlish/marshal/objects/marshal.py +108 -0
- omlish/marshal/objects/metadata.py +124 -0
- omlish/marshal/{namedtuples.py → objects/namedtuples.py} +16 -16
- omlish/marshal/objects/unmarshal.py +141 -0
- omlish/marshal/polymorphism/__init__.py +7 -0
- omlish/marshal/polymorphism/marshal.py +66 -0
- omlish/marshal/polymorphism/metadata.py +140 -0
- omlish/marshal/{unions.py → polymorphism/unions.py} +18 -18
- omlish/marshal/polymorphism/unmarshal.py +73 -0
- omlish/marshal/singular/__init__.py +0 -0
- omlish/marshal/{any.py → singular/any.py} +8 -8
- omlish/marshal/{base64.py → singular/base64.py} +9 -9
- omlish/marshal/{datetimes.py → singular/datetimes.py} +9 -9
- omlish/marshal/{enums.py → singular/enums.py} +9 -9
- omlish/marshal/{numbers.py → singular/numbers.py} +8 -8
- omlish/marshal/{primitives.py → singular/primitives.py} +8 -8
- omlish/marshal/{uuids.py → singular/uuids.py} +8 -8
- omlish/marshal/standard.py +32 -32
- omlish/os/death.py +72 -4
- omlish/os/fcntl.py +11 -12
- omlish/os/files.py +18 -3
- omlish/os/forkhooks.py +215 -0
- omlish/os/pidfiles/pinning.py +4 -0
- omlish/sockets/bind.py +4 -4
- {omlish-0.0.0.dev227.dist-info → omlish-0.0.0.dev229.dist-info}/METADATA +3 -3
- {omlish-0.0.0.dev227.dist-info → omlish-0.0.0.dev229.dist-info}/RECORD +45 -36
- omlish/marshal/objects.py +0 -317
- omlish/marshal/polymorphism.py +0 -267
- {omlish-0.0.0.dev227.dist-info → omlish-0.0.0.dev229.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev227.dist-info → omlish-0.0.0.dev229.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev227.dist-info → omlish-0.0.0.dev229.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev227.dist-info → omlish-0.0.0.dev229.dist-info}/top_level.txt +0 -0
omlish/marshal/standard.py
CHANGED
@@ -1,42 +1,42 @@
|
|
1
1
|
from ..funcs import match as mfs
|
2
|
-
from .any import ANY_MARSHALER_FACTORY
|
3
|
-
from .any import ANY_UNMARSHALER_FACTORY
|
4
2
|
from .base import MarshalerFactory
|
5
3
|
from .base import RecursiveMarshalerFactory
|
6
4
|
from .base import RecursiveUnmarshalerFactory
|
7
5
|
from .base import TypeCacheMarshalerFactory
|
8
6
|
from .base import TypeCacheUnmarshalerFactory
|
9
7
|
from .base import UnmarshalerFactory
|
10
|
-
from .
|
11
|
-
from .
|
12
|
-
from .
|
13
|
-
from .
|
14
|
-
from .
|
15
|
-
from .
|
16
|
-
from .
|
17
|
-
from .
|
18
|
-
from .
|
19
|
-
from .
|
20
|
-
from .
|
21
|
-
from .
|
22
|
-
from .
|
23
|
-
from .
|
24
|
-
from .
|
25
|
-
from .
|
26
|
-
from .
|
27
|
-
from .
|
28
|
-
from .
|
29
|
-
from .
|
30
|
-
from .
|
31
|
-
from .
|
32
|
-
from .
|
33
|
-
from .
|
34
|
-
from .
|
35
|
-
from .
|
36
|
-
from .
|
37
|
-
from .
|
38
|
-
from .
|
39
|
-
from .
|
8
|
+
from .composite.iterables import IterableMarshalerFactory
|
9
|
+
from .composite.iterables import IterableUnmarshalerFactory
|
10
|
+
from .composite.literals import LiteralMarshalerFactory
|
11
|
+
from .composite.literals import LiteralUnmarshalerFactory
|
12
|
+
from .composite.mappings import MappingMarshalerFactory
|
13
|
+
from .composite.mappings import MappingUnmarshalerFactory
|
14
|
+
from .composite.maybes import MaybeMarshalerFactory
|
15
|
+
from .composite.maybes import MaybeUnmarshalerFactory
|
16
|
+
from .composite.newtypes import NewtypeMarshalerFactory
|
17
|
+
from .composite.newtypes import NewtypeUnmarshalerFactory
|
18
|
+
from .composite.optionals import OptionalMarshalerFactory
|
19
|
+
from .composite.optionals import OptionalUnmarshalerFactory
|
20
|
+
from .objects.dataclasses import DataclassMarshalerFactory
|
21
|
+
from .objects.dataclasses import DataclassUnmarshalerFactory
|
22
|
+
from .objects.namedtuples import NamedtupleMarshalerFactory
|
23
|
+
from .objects.namedtuples import NamedtupleUnmarshalerFactory
|
24
|
+
from .polymorphism.unions import PrimitiveUnionMarshalerFactory
|
25
|
+
from .polymorphism.unions import PrimitiveUnionUnmarshalerFactory
|
26
|
+
from .singular.any import ANY_MARSHALER_FACTORY
|
27
|
+
from .singular.any import ANY_UNMARSHALER_FACTORY
|
28
|
+
from .singular.base64 import BASE64_MARSHALER_FACTORY
|
29
|
+
from .singular.base64 import BASE64_UNMARSHALER_FACTORY
|
30
|
+
from .singular.datetimes import DATETIME_MARSHALER_FACTORY
|
31
|
+
from .singular.datetimes import DATETIME_UNMARSHALER_FACTORY
|
32
|
+
from .singular.enums import EnumMarshalerFactory
|
33
|
+
from .singular.enums import EnumUnmarshalerFactory
|
34
|
+
from .singular.numbers import NUMBERS_MARSHALER_FACTORY
|
35
|
+
from .singular.numbers import NUMBERS_UNMARSHALER_FACTORY
|
36
|
+
from .singular.primitives import PRIMITIVE_MARSHALER_FACTORY
|
37
|
+
from .singular.primitives import PRIMITIVE_UNMARSHALER_FACTORY
|
38
|
+
from .singular.uuids import UUID_MARSHALER_FACTORY
|
39
|
+
from .singular.uuids import UUID_UNMARSHALER_FACTORY
|
40
40
|
|
41
41
|
|
42
42
|
##
|
omlish/os/death.py
CHANGED
@@ -4,8 +4,11 @@ import signal
|
|
4
4
|
import sys
|
5
5
|
import time
|
6
6
|
import typing as ta
|
7
|
+
import weakref
|
7
8
|
|
8
9
|
from .. import check
|
10
|
+
from .forkhooks import ForkHook
|
11
|
+
from .forkhooks import get_fork_depth
|
9
12
|
|
10
13
|
|
11
14
|
##
|
@@ -82,12 +85,24 @@ class BaseDeathpact(Deathpact, abc.ABC):
|
|
82
85
|
|
83
86
|
|
84
87
|
class PipeDeathpact(BaseDeathpact):
|
88
|
+
"""
|
89
|
+
NOTE: Closes write side in children lazily on poll - does not proactively close write sides on fork. This means
|
90
|
+
parents which fork children into codepaths unaware of live PipeDeathpacts will leave write sides open in those
|
91
|
+
children, potentially leading to zombies (if those children outlast the parent). Use ForkAwarePipeDeathpact to
|
92
|
+
handle such cases.
|
93
|
+
"""
|
94
|
+
|
95
|
+
_COOKIE: ta.ClassVar[bytes] = os.urandom(16)
|
96
|
+
|
85
97
|
def __init__(self, **kwargs: ta.Any) -> None:
|
86
98
|
super().__init__(**kwargs)
|
87
99
|
|
88
100
|
self._rfd: int | None = None
|
89
101
|
self._wfd: int | None = None
|
90
102
|
|
103
|
+
self._cookie: bytes | None = self._COOKIE
|
104
|
+
self._fork_depth: int | None = get_fork_depth()
|
105
|
+
|
91
106
|
def __repr__(self) -> str:
|
92
107
|
return f'{self.__class__.__name__}(rfd={self._rfd}, wfd={self._wfd})'
|
93
108
|
|
@@ -95,26 +110,54 @@ class PipeDeathpact(BaseDeathpact):
|
|
95
110
|
def pass_fd(self) -> int:
|
96
111
|
return check.not_none(self._rfd)
|
97
112
|
|
113
|
+
def is_parent(self) -> bool:
|
114
|
+
return (self._COOKIE, get_fork_depth()) == (self._cookie, self._fork_depth)
|
115
|
+
|
116
|
+
#
|
117
|
+
|
98
118
|
def __enter__(self) -> ta.Self:
|
99
119
|
check.none(self._rfd)
|
100
120
|
check.none(self._wfd)
|
101
121
|
|
102
122
|
self._rfd, self._wfd = os.pipe()
|
103
123
|
|
104
|
-
os.set_inheritable(self._rfd, True)
|
105
124
|
os.set_blocking(self._rfd, False)
|
106
125
|
|
107
126
|
return self
|
108
127
|
|
109
|
-
def
|
110
|
-
if self.
|
111
|
-
|
128
|
+
def _close_wfd_if_not_parent(self) -> None:
|
129
|
+
if self._wfd is not None:
|
130
|
+
if not self.is_parent():
|
131
|
+
os.close(check.not_none(self._wfd))
|
112
132
|
self._wfd = None
|
113
133
|
|
134
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
135
|
+
if self._rfd is not None:
|
114
136
|
os.close(self._rfd)
|
115
137
|
self._rfd = None
|
116
138
|
|
139
|
+
self._close_wfd_if_not_parent()
|
140
|
+
|
141
|
+
#
|
142
|
+
|
143
|
+
def __getstate__(self):
|
144
|
+
return {
|
145
|
+
**self.__dict__,
|
146
|
+
**dict(
|
147
|
+
_wfd=None,
|
148
|
+
_cookie=None,
|
149
|
+
_fork_depth=None,
|
150
|
+
),
|
151
|
+
}
|
152
|
+
|
153
|
+
def __setstate__(self, state):
|
154
|
+
self.__dict__.update(state)
|
155
|
+
|
156
|
+
#
|
157
|
+
|
117
158
|
def should_die(self) -> bool:
|
159
|
+
self._close_wfd_if_not_parent()
|
160
|
+
|
118
161
|
try:
|
119
162
|
buf = os.read(check.not_none(self._rfd), 1)
|
120
163
|
except BlockingIOError:
|
@@ -125,3 +168,28 @@ class PipeDeathpact(BaseDeathpact):
|
|
125
168
|
self.die()
|
126
169
|
|
127
170
|
return True
|
171
|
+
|
172
|
+
|
173
|
+
#
|
174
|
+
|
175
|
+
|
176
|
+
class ForkAwarePipeDeathpact(PipeDeathpact):
|
177
|
+
"""
|
178
|
+
TODO:
|
179
|
+
- Despite no correct way to do threads+forks, still audit thread-safety. Is WeakSet threadsafe? Probably not..
|
180
|
+
"""
|
181
|
+
|
182
|
+
_PARENTS: ta.ClassVar[ta.MutableSet['ForkAwarePipeDeathpact']] = weakref.WeakSet()
|
183
|
+
|
184
|
+
def __init__(self, **kwargs: ta.Any) -> None:
|
185
|
+
super().__init__(**kwargs)
|
186
|
+
|
187
|
+
self._ForkHook.install()
|
188
|
+
self._PARENTS.add(self)
|
189
|
+
|
190
|
+
class _ForkHook(ForkHook):
|
191
|
+
@classmethod
|
192
|
+
def _after_fork_in_child(cls) -> None:
|
193
|
+
for pdp in ForkAwarePipeDeathpact._PARENTS:
|
194
|
+
pdp._close_wfd_if_not_parent() # noqa
|
195
|
+
ForkAwarePipeDeathpact._PARENTS.clear()
|
omlish/os/fcntl.py
CHANGED
@@ -19,7 +19,7 @@ class FcntlLockData:
|
|
19
19
|
|
20
20
|
#
|
21
21
|
|
22
|
-
|
22
|
+
_STRUCT_PACKING_BY_PLATFORM: ta.ClassVar[ta.Mapping[str, ta.Sequence[ta.Tuple[str, str]]]] = {
|
23
23
|
'linux': [
|
24
24
|
('type', 'h'),
|
25
25
|
('whence', 'h'),
|
@@ -36,24 +36,23 @@ class FcntlLockData:
|
|
36
36
|
],
|
37
37
|
}
|
38
38
|
|
39
|
-
|
39
|
+
@classmethod
|
40
|
+
def _struct_packing(cls) -> ta.Sequence[ta.Tuple[str, str]]:
|
40
41
|
try:
|
41
|
-
|
42
|
+
return cls._STRUCT_PACKING_BY_PLATFORM[sys.platform]
|
42
43
|
except KeyError:
|
43
44
|
raise OSError from None
|
44
45
|
|
45
|
-
|
46
|
-
|
46
|
+
def pack(self) -> bytes:
|
47
|
+
packing = self._struct_packing()
|
48
|
+
fmt = ''.join(f for _, f in packing)
|
49
|
+
tup = [getattr(self, a) for a, _ in packing]
|
47
50
|
return struct.pack(fmt, *tup)
|
48
51
|
|
49
52
|
@classmethod
|
50
53
|
def unpack(cls, data: bytes) -> 'FcntlLockData':
|
51
|
-
|
52
|
-
|
53
|
-
except KeyError:
|
54
|
-
raise OSError from None
|
55
|
-
|
56
|
-
fmt = ''.join(f for _, f in pack)
|
54
|
+
packing = cls._struct_packing()
|
55
|
+
fmt = ''.join(f for _, f in packing)
|
57
56
|
tup = struct.unpack(fmt, data)
|
58
|
-
kw = {a: v for (a, _), v in zip(
|
57
|
+
kw = {a: v for (a, _), v in zip(packing, tup)}
|
59
58
|
return FcntlLockData(**kw)
|
omlish/os/files.py
CHANGED
@@ -1,24 +1,39 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
# @omlish-lite
|
3
3
|
import contextlib
|
4
|
+
import errno
|
5
|
+
import fcntl
|
4
6
|
import os
|
5
7
|
import typing as ta
|
6
8
|
|
7
9
|
|
8
|
-
def
|
10
|
+
def is_fd_open(fd: int) -> bool:
|
11
|
+
try:
|
12
|
+
fcntl.fcntl(fd, fcntl.F_GETFD)
|
13
|
+
except OSError as e:
|
14
|
+
if e.errno == errno.EBADF:
|
15
|
+
return False
|
16
|
+
raise
|
17
|
+
else:
|
18
|
+
return True
|
19
|
+
|
20
|
+
|
21
|
+
def touch(path: str, mode: int = 0o666, exist_ok: bool = True) -> None:
|
9
22
|
if exist_ok:
|
10
23
|
# First try to bump modification time
|
11
24
|
# Implementation note: GNU touch uses the UTIME_NOW option of the utimensat() / futimens() functions.
|
12
25
|
try:
|
13
|
-
os.utime(
|
26
|
+
os.utime(path, None)
|
14
27
|
except OSError:
|
15
28
|
pass
|
16
29
|
else:
|
17
30
|
return
|
31
|
+
|
18
32
|
flags = os.O_CREAT | os.O_WRONLY
|
19
33
|
if not exist_ok:
|
20
34
|
flags |= os.O_EXCL
|
21
|
-
|
35
|
+
|
36
|
+
fd = os.open(path, flags, mode)
|
22
37
|
os.close(fd)
|
23
38
|
|
24
39
|
|
omlish/os/forkhooks.py
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
"""
|
4
|
+
TODO:
|
5
|
+
- ForkHook base class? all classmethods? prevents pickling
|
6
|
+
"""
|
7
|
+
import abc
|
8
|
+
import os
|
9
|
+
import threading
|
10
|
+
import typing as ta
|
11
|
+
|
12
|
+
from ..lite.check import check
|
13
|
+
|
14
|
+
|
15
|
+
##
|
16
|
+
|
17
|
+
|
18
|
+
class _ForkHookManager:
|
19
|
+
def __new__(cls, *args, **kwargs): # noqa
|
20
|
+
raise TypeError
|
21
|
+
|
22
|
+
def __init_subclass__(cls, **kwargs): # noqa
|
23
|
+
raise TypeError
|
24
|
+
|
25
|
+
#
|
26
|
+
|
27
|
+
# Intentionally not an RLock - do not nest calls.
|
28
|
+
_lock: ta.ClassVar[threading.Lock] = threading.Lock()
|
29
|
+
|
30
|
+
#
|
31
|
+
|
32
|
+
_installed: ta.ClassVar[bool] = False
|
33
|
+
|
34
|
+
@classmethod
|
35
|
+
def _install(cls) -> bool:
|
36
|
+
if cls._installed:
|
37
|
+
return False
|
38
|
+
|
39
|
+
check.state(not cls._installed)
|
40
|
+
|
41
|
+
os.register_at_fork(
|
42
|
+
before=cls._before_fork,
|
43
|
+
after_in_parent=cls._after_fork_in_parent,
|
44
|
+
after_in_child=cls._after_fork_in_child,
|
45
|
+
)
|
46
|
+
|
47
|
+
cls._installed = True
|
48
|
+
return True
|
49
|
+
|
50
|
+
@classmethod
|
51
|
+
def install(cls) -> bool:
|
52
|
+
with cls._lock:
|
53
|
+
return cls._install()
|
54
|
+
|
55
|
+
#
|
56
|
+
|
57
|
+
class Hook(ta.NamedTuple):
|
58
|
+
key: ta.Any
|
59
|
+
priority: int
|
60
|
+
|
61
|
+
# NOTE: these are called inside the global, non-reentrant manager lock
|
62
|
+
before_fork: ta.Optional[ta.Callable[[], None]] = None
|
63
|
+
after_fork_in_parent: ta.Optional[ta.Callable[[], None]] = None
|
64
|
+
after_fork_in_child: ta.Optional[ta.Callable[[], None]] = None
|
65
|
+
|
66
|
+
#
|
67
|
+
|
68
|
+
_hooks_by_key: ta.ClassVar[ta.Dict[str, Hook]] = {}
|
69
|
+
|
70
|
+
_hook_keys: ta.ClassVar[ta.FrozenSet[str]] = frozenset()
|
71
|
+
_priority_ordered_hooks: ta.ClassVar[ta.List[Hook]] = []
|
72
|
+
|
73
|
+
@classmethod
|
74
|
+
def _rebuild_hook_collections(cls) -> None:
|
75
|
+
cls._hook_keys = frozenset(cls._hooks_by_key)
|
76
|
+
|
77
|
+
# Uses on dict order preservation for insertion-order of hooks of equal priority (although that shouldn't be
|
78
|
+
# depended upon for usecase correctness.
|
79
|
+
cls._priority_ordered_hooks = sorted(cls._hooks_by_key.values(), key=lambda h: h.priority)
|
80
|
+
|
81
|
+
#
|
82
|
+
|
83
|
+
class HookAlreadyPresentError(Exception):
|
84
|
+
pass
|
85
|
+
|
86
|
+
@classmethod
|
87
|
+
def add_hook(cls, hook: Hook) -> None:
|
88
|
+
with cls._lock:
|
89
|
+
if hook.key in cls._hooks_by_key:
|
90
|
+
raise cls.HookAlreadyPresentError(hook.key)
|
91
|
+
|
92
|
+
check.isinstance(hook.priority, int)
|
93
|
+
|
94
|
+
cls._hooks_by_key[hook.key] = hook
|
95
|
+
cls._rebuild_hook_collections()
|
96
|
+
|
97
|
+
cls._install()
|
98
|
+
|
99
|
+
@classmethod
|
100
|
+
def try_add_hook(cls, hook: Hook) -> bool:
|
101
|
+
if hook.key in cls._hook_keys:
|
102
|
+
return False
|
103
|
+
|
104
|
+
try:
|
105
|
+
cls.add_hook(hook)
|
106
|
+
except cls.HookAlreadyPresentError:
|
107
|
+
return False
|
108
|
+
else:
|
109
|
+
return True
|
110
|
+
|
111
|
+
@classmethod
|
112
|
+
def contains_hook(cls, key: ta.Any) -> bool:
|
113
|
+
return key in cls._hook_keys
|
114
|
+
|
115
|
+
@classmethod
|
116
|
+
def remove_hook(cls, key: ta.Any) -> None:
|
117
|
+
with cls._lock:
|
118
|
+
del cls._hooks_by_key[key]
|
119
|
+
|
120
|
+
cls._rebuild_hook_collections()
|
121
|
+
|
122
|
+
#
|
123
|
+
|
124
|
+
@classmethod
|
125
|
+
def _before_fork(cls) -> None:
|
126
|
+
cls._lock.acquire()
|
127
|
+
|
128
|
+
for hook in cls._priority_ordered_hooks:
|
129
|
+
if (fn := hook.before_fork) is not None:
|
130
|
+
fn()
|
131
|
+
|
132
|
+
@classmethod
|
133
|
+
def _after_fork_in_parent(cls) -> None:
|
134
|
+
for hook in cls._priority_ordered_hooks:
|
135
|
+
if (fn := hook.after_fork_in_parent) is not None:
|
136
|
+
fn()
|
137
|
+
|
138
|
+
cls._lock.release()
|
139
|
+
|
140
|
+
@classmethod
|
141
|
+
def _after_fork_in_child(cls) -> None:
|
142
|
+
for hook in cls._priority_ordered_hooks:
|
143
|
+
if (fn := hook.after_fork_in_child) is not None:
|
144
|
+
fn()
|
145
|
+
|
146
|
+
cls._lock.release()
|
147
|
+
|
148
|
+
|
149
|
+
#
|
150
|
+
|
151
|
+
|
152
|
+
class ForkHook(abc.ABC): # noqa
|
153
|
+
@ta.final
|
154
|
+
def __new__(cls, *args, **kwargs): # noqa
|
155
|
+
raise TypeError
|
156
|
+
|
157
|
+
@classmethod
|
158
|
+
@ta.final
|
159
|
+
def install(cls) -> bool:
|
160
|
+
if _ForkHookManager.contains_hook(cls):
|
161
|
+
return False
|
162
|
+
|
163
|
+
return _ForkHookManager.try_add_hook(_ForkHookManager.Hook(
|
164
|
+
key=cls,
|
165
|
+
priority=cls._hook_priority,
|
166
|
+
|
167
|
+
before_fork=cls._before_fork,
|
168
|
+
after_fork_in_parent=cls._after_fork_in_parent,
|
169
|
+
after_fork_in_child=cls._after_fork_in_child,
|
170
|
+
))
|
171
|
+
|
172
|
+
def __init_subclass__(cls, install: bool = False, **kwargs: ta.Any) -> None:
|
173
|
+
super().__init_subclass__(**kwargs)
|
174
|
+
|
175
|
+
if install:
|
176
|
+
cls.install()
|
177
|
+
|
178
|
+
#
|
179
|
+
|
180
|
+
_hook_priority: ta.ClassVar[int] = 0
|
181
|
+
|
182
|
+
@classmethod # noqa
|
183
|
+
def _before_fork(cls) -> None:
|
184
|
+
pass
|
185
|
+
|
186
|
+
@classmethod # noqa
|
187
|
+
def _after_fork_in_parent(cls) -> None:
|
188
|
+
pass
|
189
|
+
|
190
|
+
@classmethod # noqa
|
191
|
+
def _after_fork_in_child(cls) -> None:
|
192
|
+
pass
|
193
|
+
|
194
|
+
|
195
|
+
##
|
196
|
+
|
197
|
+
|
198
|
+
class _ForkDepthTracker(ForkHook):
|
199
|
+
_hook_priority = -1000
|
200
|
+
|
201
|
+
_fork_depth: ta.ClassVar[int] = 0
|
202
|
+
|
203
|
+
@classmethod
|
204
|
+
def _after_fork_in_child(cls) -> None:
|
205
|
+
cls._fork_depth += 1
|
206
|
+
|
207
|
+
@classmethod
|
208
|
+
def get_fork_depth(cls) -> int:
|
209
|
+
cls.install()
|
210
|
+
|
211
|
+
return cls._fork_depth
|
212
|
+
|
213
|
+
|
214
|
+
def get_fork_depth() -> int:
|
215
|
+
return _ForkDepthTracker.get_fork_depth()
|
omlish/os/pidfiles/pinning.py
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
# @omlish-lite
|
3
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
|
+
|
4
8
|
Strategies:
|
5
9
|
- linux
|
6
10
|
- get pid of owner (lslocks or F_GETLK)
|
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
|
19
|
-
from
|
20
|
-
from
|
21
|
-
from
|
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.
|
3
|
+
Version: 0.0.0.dev229
|
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~=
|
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~=
|
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
|