omlish 0.0.0.dev243__py3-none-any.whl → 0.0.0.dev245__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/cached.py +4 -3
- 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/lang/__init__.py +40 -16
- omlish/lang/attrs.py +132 -0
- omlish/lang/cached/__init__.py +0 -0
- omlish/lang/{cached.py → cached/function.py} +124 -83
- omlish/lang/cached/property.py +118 -0
- omlish/lang/classes/__init__.py +0 -41
- omlish/sockets/wait.py +32 -10
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev245.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev245.dist-info}/RECORD +21 -18
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev245.dist-info}/WHEEL +1 -1
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev245.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev245.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev243.dist-info → omlish-0.0.0.dev245.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/cached.py
CHANGED
@@ -9,11 +9,12 @@ builtins and thus not distinguish it from a normal property.
|
|
9
9
|
def p(self) -> str: ...
|
10
10
|
|
11
11
|
"""
|
12
|
-
from .lang.cached import
|
13
|
-
from .lang.cached import
|
12
|
+
from .lang.cached.function import cached_function as _cached_function
|
13
|
+
from .lang.cached.property import cached_property as _cached_property
|
14
|
+
|
14
15
|
|
15
16
|
function = _cached_function
|
16
17
|
|
17
18
|
property = property # noqa
|
18
19
|
|
19
|
-
globals()['property'] =
|
20
|
+
globals()['property'] = _cached_property # noqa
|
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/lang/__init__.py
CHANGED
@@ -1,39 +1,63 @@
|
|
1
|
-
from .
|
1
|
+
from .attrs import ( # noqa
|
2
|
+
AttrOps,
|
3
|
+
STD_ATTR_OPS,
|
4
|
+
StdAttrOps,
|
5
|
+
TRANSIENT_ATTR_OPS,
|
6
|
+
TransientAttrOps,
|
7
|
+
TransientDict,
|
8
|
+
transient_delattr,
|
9
|
+
transient_getattr,
|
10
|
+
transient_setattr,
|
11
|
+
)
|
12
|
+
|
13
|
+
from .cached.function import ( # noqa
|
2
14
|
cached_function,
|
3
|
-
cached_property,
|
4
15
|
static_init,
|
5
16
|
)
|
6
17
|
|
7
|
-
from .
|
18
|
+
from .cached.property import ( # noqa
|
19
|
+
cached_property,
|
20
|
+
)
|
21
|
+
|
22
|
+
from .classes.abstract import ( # noqa
|
8
23
|
Abstract,
|
9
24
|
AbstractTypeError,
|
25
|
+
get_abstract_methods,
|
26
|
+
is_abstract,
|
27
|
+
is_abstract_class,
|
28
|
+
is_abstract_method,
|
29
|
+
make_abstract,
|
30
|
+
unabstract_class,
|
31
|
+
)
|
32
|
+
|
33
|
+
from .classes.restrict import ( # noqa
|
10
34
|
AnySensitive,
|
11
|
-
Callable,
|
12
|
-
Descriptor,
|
13
35
|
Final,
|
14
36
|
FinalTypeError,
|
15
|
-
LazySingleton,
|
16
|
-
Marker,
|
17
|
-
Namespace,
|
18
37
|
NoBool,
|
19
38
|
NotInstantiable,
|
20
39
|
NotPicklable,
|
21
40
|
PackageSealed,
|
22
|
-
Picklable,
|
23
41
|
SENSITIVE_ATTR,
|
24
42
|
Sealed,
|
25
43
|
SealedError,
|
26
44
|
Sensitive,
|
45
|
+
no_bool,
|
46
|
+
)
|
47
|
+
|
48
|
+
from .classes.simple import ( # noqa
|
49
|
+
LazySingleton,
|
50
|
+
Marker,
|
51
|
+
Namespace,
|
27
52
|
SimpleMetaDict,
|
28
53
|
Singleton,
|
54
|
+
)
|
55
|
+
|
56
|
+
from .classes.virtual import ( # noqa
|
57
|
+
Callable,
|
58
|
+
Descriptor,
|
59
|
+
Picklable,
|
29
60
|
Virtual,
|
30
|
-
get_abstract_methods,
|
31
|
-
is_abstract,
|
32
|
-
is_abstract_class,
|
33
|
-
is_abstract_method,
|
34
|
-
make_abstract,
|
35
|
-
no_bool,
|
36
|
-
unabstract_class,
|
37
61
|
virtual_check,
|
38
62
|
)
|
39
63
|
|
omlish/lang/attrs.py
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
import abc
|
2
|
+
import collections.abc
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
|
6
|
+
##
|
7
|
+
|
8
|
+
|
9
|
+
class AttrOps(abc.ABC):
|
10
|
+
class NOT_SET: # noqa
|
11
|
+
def __new__(cls, *args, **kwargs): # noqa
|
12
|
+
raise TypeError
|
13
|
+
|
14
|
+
@abc.abstractmethod
|
15
|
+
def getattr(self, obj: ta.Any, name: str, default: ta.Any = NOT_SET) -> ta.Any:
|
16
|
+
raise NotImplementedError
|
17
|
+
|
18
|
+
@abc.abstractmethod
|
19
|
+
def setattr(self, obj: ta.Any, name: str, value: ta.Any) -> None:
|
20
|
+
raise NotImplementedError
|
21
|
+
|
22
|
+
@abc.abstractmethod
|
23
|
+
def delattr(self, obj: ta.Any, name: str) -> None:
|
24
|
+
raise NotImplementedError
|
25
|
+
|
26
|
+
|
27
|
+
##
|
28
|
+
|
29
|
+
|
30
|
+
class StdAttrOps(AttrOps):
|
31
|
+
def getattr(self, obj: ta.Any, name: str, default: ta.Any = AttrOps.NOT_SET) -> ta.Any:
|
32
|
+
if default is AttrOps.NOT_SET:
|
33
|
+
return getattr(obj, name)
|
34
|
+
else:
|
35
|
+
return getattr(obj, name, default)
|
36
|
+
|
37
|
+
def setattr(self, obj: ta.Any, name: str, value: ta.Any) -> None:
|
38
|
+
setattr(obj, name, value)
|
39
|
+
|
40
|
+
def delattr(self, obj: ta.Any, name: str) -> None:
|
41
|
+
delattr(obj, name)
|
42
|
+
|
43
|
+
|
44
|
+
STD_ATTR_OPS = StdAttrOps()
|
45
|
+
|
46
|
+
|
47
|
+
##
|
48
|
+
|
49
|
+
|
50
|
+
class TransientDict(collections.abc.MutableMapping):
|
51
|
+
def __init__(self) -> None:
|
52
|
+
super().__init__()
|
53
|
+
|
54
|
+
self._dct: dict = {}
|
55
|
+
|
56
|
+
def __reduce__(self):
|
57
|
+
return (TransientDict, ())
|
58
|
+
|
59
|
+
def __getitem__(self, item):
|
60
|
+
return self._dct[item]
|
61
|
+
|
62
|
+
def __setitem__(self, key, value):
|
63
|
+
self._dct[key] = value
|
64
|
+
|
65
|
+
def __delitem__(self, key):
|
66
|
+
del self._dct[key]
|
67
|
+
|
68
|
+
def __len__(self):
|
69
|
+
return len(self._dct)
|
70
|
+
|
71
|
+
def __iter__(self):
|
72
|
+
return iter(self._dct)
|
73
|
+
|
74
|
+
def clear(self):
|
75
|
+
self._dct.clear()
|
76
|
+
|
77
|
+
def items(self):
|
78
|
+
return self._dct.items()
|
79
|
+
|
80
|
+
def keys(self):
|
81
|
+
return self._dct.keys()
|
82
|
+
|
83
|
+
def values(self):
|
84
|
+
return self._dct.values()
|
85
|
+
|
86
|
+
def __contains__(self, key, /):
|
87
|
+
return super().__contains__(key)
|
88
|
+
|
89
|
+
def __eq__(self, other, /):
|
90
|
+
raise TypeError(self)
|
91
|
+
|
92
|
+
|
93
|
+
#
|
94
|
+
|
95
|
+
|
96
|
+
_TRANSIENT_DICT_ATTR = '__transient_dict__'
|
97
|
+
|
98
|
+
|
99
|
+
def _get_object_transient_dict(obj: ta.Any) -> TransientDict:
|
100
|
+
try:
|
101
|
+
return obj.__dict__[_TRANSIENT_DICT_ATTR]
|
102
|
+
except KeyError:
|
103
|
+
return obj.__dict__.setdefault(_TRANSIENT_DICT_ATTR, TransientDict())
|
104
|
+
|
105
|
+
|
106
|
+
class TransientAttrOps(AttrOps):
|
107
|
+
def getattr(self, obj: ta.Any, name: str, default: ta.Any = AttrOps.NOT_SET) -> ta.Any:
|
108
|
+
td = _get_object_transient_dict(obj)
|
109
|
+
try:
|
110
|
+
return td[name]
|
111
|
+
except KeyError:
|
112
|
+
if default is not AttrOps.NOT_SET:
|
113
|
+
return default
|
114
|
+
raise AttributeError(name) from None
|
115
|
+
|
116
|
+
def setattr(self, obj: ta.Any, name: str, value: ta.Any) -> None:
|
117
|
+
td = _get_object_transient_dict(obj)
|
118
|
+
td[name] = value
|
119
|
+
|
120
|
+
def delattr(self, obj: ta.Any, name: str) -> None:
|
121
|
+
td = _get_object_transient_dict(obj)
|
122
|
+
try:
|
123
|
+
del td[name]
|
124
|
+
except KeyError:
|
125
|
+
raise AttributeError(name) from None
|
126
|
+
|
127
|
+
|
128
|
+
TRANSIENT_ATTR_OPS = TransientAttrOps()
|
129
|
+
|
130
|
+
transient_getattr = TRANSIENT_ATTR_OPS.getattr
|
131
|
+
transient_setattr = TRANSIENT_ATTR_OPS.setattr
|
132
|
+
transient_delattr = TRANSIENT_ATTR_OPS.delattr
|
File without changes
|
@@ -2,37 +2,42 @@
|
|
2
2
|
TODO:
|
3
3
|
- integrate / expose with collections.cache
|
4
4
|
- weakrefs (selectable by arg)
|
5
|
-
-
|
5
|
+
- more rigorous descriptor pickling
|
6
|
+
- must support free functions (which have no instance nor owner)
|
7
|
+
- 'staticmethod' or effective equiv - which must resolve to the shared instance
|
8
|
+
- and must be transient?
|
6
9
|
"""
|
7
10
|
import dataclasses as dc
|
8
11
|
import functools
|
9
12
|
import inspect
|
10
13
|
import typing as ta
|
11
14
|
|
12
|
-
from .
|
13
|
-
from
|
14
|
-
from
|
15
|
-
from
|
15
|
+
from ..classes.abstract import Abstract
|
16
|
+
from ..contextmanagers import DefaultLockable
|
17
|
+
from ..contextmanagers import default_lock
|
18
|
+
from ..descriptors import unwrap_func
|
19
|
+
from ..descriptors import unwrap_func_with_partials
|
16
20
|
|
17
21
|
|
18
22
|
P = ta.ParamSpec('P')
|
19
23
|
T = ta.TypeVar('T')
|
20
24
|
CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
|
21
25
|
|
22
|
-
|
26
|
+
|
27
|
+
##
|
23
28
|
|
24
29
|
|
25
|
-
def
|
30
|
+
def _nullary_cache_key_maker():
|
26
31
|
return ()
|
27
32
|
|
28
33
|
|
29
|
-
def
|
34
|
+
def _simple_cache_key_maker(*args, **kwargs):
|
30
35
|
return (args, tuple(sorted(kwargs.items())))
|
31
36
|
|
32
37
|
|
33
|
-
def
|
38
|
+
def _make_cache_key_maker(fn, *, simple=False, bound=False):
|
34
39
|
if simple:
|
35
|
-
return
|
40
|
+
return _simple_cache_key_maker
|
36
41
|
|
37
42
|
fn, partials = unwrap_func_with_partials(fn)
|
38
43
|
|
@@ -42,7 +47,7 @@ def _make_cache_keyer(fn, *, simple=False, bound=False):
|
|
42
47
|
sig = inspect.signature(fn)
|
43
48
|
sig_params = list(sig.parameters.values())[1 if bound else 0:]
|
44
49
|
if not sig_params:
|
45
|
-
return
|
50
|
+
return _nullary_cache_key_maker
|
46
51
|
|
47
52
|
ns = {}
|
48
53
|
src_params = []
|
@@ -92,19 +97,23 @@ def _make_cache_keyer(fn, *, simple=False, bound=False):
|
|
92
97
|
return kfn
|
93
98
|
|
94
99
|
|
95
|
-
|
100
|
+
##
|
101
|
+
|
102
|
+
|
103
|
+
class _CachedFunction(ta.Generic[T], Abstract):
|
96
104
|
@dc.dataclass(frozen=True)
|
97
105
|
class Opts:
|
98
106
|
map_maker: ta.Callable[[], ta.MutableMapping] = dict
|
99
107
|
simple_key: bool = False
|
100
108
|
lock: DefaultLockable = None
|
109
|
+
transient: bool = False
|
101
110
|
|
102
111
|
def __init__(
|
103
112
|
self,
|
104
113
|
fn: ta.Callable[P, T],
|
105
114
|
*,
|
106
115
|
opts: Opts = Opts(),
|
107
|
-
|
116
|
+
key_maker: ta.Callable[..., tuple] | None = None,
|
108
117
|
values: ta.MutableMapping | None = None,
|
109
118
|
value_fn: ta.Callable[P, T] | None = None,
|
110
119
|
) -> None:
|
@@ -112,7 +121,7 @@ class _CachedFunction(ta.Generic[T]):
|
|
112
121
|
|
113
122
|
self._fn = (fn,)
|
114
123
|
self._opts = opts
|
115
|
-
self.
|
124
|
+
self._key_maker = key_maker if key_maker is not None else _make_cache_key_maker(fn, simple=opts.simple_key)
|
116
125
|
|
117
126
|
self._lock = default_lock(opts.lock, False)() if opts.lock is not None else None
|
118
127
|
self._values = values if values is not None else opts.map_maker()
|
@@ -134,7 +143,7 @@ class _CachedFunction(ta.Generic[T]):
|
|
134
143
|
raise TypeError
|
135
144
|
|
136
145
|
def __call__(self, *args, **kwargs) -> T:
|
137
|
-
k = self.
|
146
|
+
k = self._key_maker(*args, **kwargs)
|
138
147
|
|
139
148
|
try:
|
140
149
|
return self._values[k]
|
@@ -157,11 +166,42 @@ class _CachedFunction(ta.Generic[T]):
|
|
157
166
|
return value
|
158
167
|
|
159
168
|
|
160
|
-
|
169
|
+
#
|
170
|
+
|
171
|
+
|
172
|
+
class _FreeCachedFunction(_CachedFunction[T]):
|
173
|
+
@classmethod
|
174
|
+
def _unpickle(
|
175
|
+
cls,
|
176
|
+
fn,
|
177
|
+
opts,
|
178
|
+
values,
|
179
|
+
):
|
180
|
+
return cls(
|
181
|
+
fn,
|
182
|
+
opts=opts,
|
183
|
+
values=values,
|
184
|
+
)
|
185
|
+
|
186
|
+
def __reduce__(self):
|
187
|
+
return (
|
188
|
+
_FreeCachedFunction._unpickle,
|
189
|
+
(
|
190
|
+
self._fn,
|
191
|
+
self._opts,
|
192
|
+
self._values if not self._opts.transient else None,
|
193
|
+
),
|
194
|
+
)
|
195
|
+
|
196
|
+
|
197
|
+
#
|
198
|
+
|
199
|
+
|
200
|
+
class _DescriptorCachedFunction(_CachedFunction[T]):
|
161
201
|
def __init__(
|
162
202
|
self,
|
163
203
|
fn: ta.Callable[P, T],
|
164
|
-
scope: ta.Any,
|
204
|
+
scope: ta.Any, # classmethod | None
|
165
205
|
*,
|
166
206
|
instance: ta.Any = None,
|
167
207
|
owner: ta.Any = None,
|
@@ -174,9 +214,61 @@ class _CachedFunctionDescriptor(_CachedFunction[T]):
|
|
174
214
|
self._instance = instance
|
175
215
|
self._owner = owner
|
176
216
|
self._name = name if name is not None else unwrap_func(fn).__name__
|
177
|
-
self.
|
217
|
+
self._bound_key_maker = None
|
178
218
|
|
179
|
-
|
219
|
+
@classmethod
|
220
|
+
def _unpickle(
|
221
|
+
cls,
|
222
|
+
scope,
|
223
|
+
instance,
|
224
|
+
owner,
|
225
|
+
name,
|
226
|
+
values,
|
227
|
+
):
|
228
|
+
if scope is not None:
|
229
|
+
raise NotImplementedError
|
230
|
+
|
231
|
+
if instance is None:
|
232
|
+
raise RuntimeError
|
233
|
+
obj = type(instance)
|
234
|
+
|
235
|
+
desc: _DescriptorCachedFunction = object.__getattribute__(obj, name)
|
236
|
+
if not isinstance(desc, cls):
|
237
|
+
raise TypeError(desc)
|
238
|
+
if (desc._instance is not None or desc._owner is not None):
|
239
|
+
raise RuntimeError
|
240
|
+
|
241
|
+
return desc._bind(
|
242
|
+
instance,
|
243
|
+
owner,
|
244
|
+
values=values,
|
245
|
+
)
|
246
|
+
|
247
|
+
def __reduce__(self):
|
248
|
+
if self._scope is not None:
|
249
|
+
raise NotImplementedError
|
250
|
+
|
251
|
+
if not (self._instance is not None or self._owner is not None):
|
252
|
+
raise RuntimeError
|
253
|
+
|
254
|
+
return (
|
255
|
+
_DescriptorCachedFunction._unpickle,
|
256
|
+
(
|
257
|
+
self._scope,
|
258
|
+
self._instance,
|
259
|
+
self._owner,
|
260
|
+
self._name,
|
261
|
+
self._values if not self._opts.transient else None,
|
262
|
+
),
|
263
|
+
)
|
264
|
+
|
265
|
+
def _bind(
|
266
|
+
self,
|
267
|
+
instance,
|
268
|
+
owner=None,
|
269
|
+
*,
|
270
|
+
values: ta.MutableMapping | None = None,
|
271
|
+
):
|
180
272
|
scope = self._scope
|
181
273
|
if owner is self._owner and (instance is self._instance or scope is classmethod):
|
182
274
|
return self
|
@@ -184,8 +276,8 @@ class _CachedFunctionDescriptor(_CachedFunction[T]):
|
|
184
276
|
fn, = self._fn
|
185
277
|
name = self._name
|
186
278
|
bound_fn = fn.__get__(instance, owner)
|
187
|
-
if self.
|
188
|
-
self.
|
279
|
+
if self._bound_key_maker is None:
|
280
|
+
self._bound_key_maker = _make_cache_key_maker(fn, simple=self._opts.simple_key, bound=True)
|
189
281
|
|
190
282
|
bound = self.__class__(
|
191
283
|
fn,
|
@@ -194,8 +286,9 @@ class _CachedFunctionDescriptor(_CachedFunction[T]):
|
|
194
286
|
instance=instance,
|
195
287
|
owner=owner,
|
196
288
|
name=name,
|
197
|
-
|
198
|
-
# values=None if scope is classmethod else self._values,
|
289
|
+
key_maker=self._bound_key_maker,
|
290
|
+
# values=None if scope is classmethod else self._values, # FIXME: ?
|
291
|
+
values=values,
|
199
292
|
value_fn=bound_fn,
|
200
293
|
)
|
201
294
|
|
@@ -206,76 +299,24 @@ class _CachedFunctionDescriptor(_CachedFunction[T]):
|
|
206
299
|
|
207
300
|
return bound
|
208
301
|
|
302
|
+
def __get__(self, instance, owner=None):
|
303
|
+
return self._bind(instance, owner)
|
304
|
+
|
305
|
+
|
306
|
+
#
|
307
|
+
|
209
308
|
|
210
309
|
def cached_function(fn=None, **kwargs): # noqa
|
211
310
|
if fn is None:
|
212
311
|
return functools.partial(cached_function, **kwargs)
|
213
312
|
opts = _CachedFunction.Opts(**kwargs)
|
214
313
|
if isinstance(fn, staticmethod):
|
215
|
-
return
|
314
|
+
return _FreeCachedFunction(fn, opts=opts, value_fn=unwrap_func(fn))
|
216
315
|
scope = classmethod if isinstance(fn, classmethod) else None
|
217
|
-
return
|
316
|
+
return _DescriptorCachedFunction(fn, scope, opts=opts)
|
218
317
|
|
219
318
|
|
220
319
|
def static_init(fn: CallableT) -> CallableT:
|
221
320
|
fn = cached_function(fn)
|
222
321
|
fn()
|
223
322
|
return fn
|
224
|
-
|
225
|
-
|
226
|
-
##
|
227
|
-
|
228
|
-
|
229
|
-
class _CachedProperty(property):
|
230
|
-
def __init__(
|
231
|
-
self,
|
232
|
-
fn,
|
233
|
-
*,
|
234
|
-
name=None,
|
235
|
-
ignore_if=lambda _: False,
|
236
|
-
clear_on_init=False,
|
237
|
-
):
|
238
|
-
if isinstance(fn, property):
|
239
|
-
fn = fn.fget
|
240
|
-
super().__init__(fn)
|
241
|
-
self._fn = fn
|
242
|
-
self._ignore_if = ignore_if
|
243
|
-
self._name = name
|
244
|
-
self._clear_on_init = clear_on_init
|
245
|
-
|
246
|
-
def __set_name__(self, owner, name):
|
247
|
-
if self._name is None:
|
248
|
-
self._name = name
|
249
|
-
|
250
|
-
def __get__(self, instance, owner=None):
|
251
|
-
if instance is None:
|
252
|
-
return self
|
253
|
-
if self._name is None:
|
254
|
-
raise TypeError(self)
|
255
|
-
|
256
|
-
try:
|
257
|
-
return instance.__dict__[self._name]
|
258
|
-
except KeyError:
|
259
|
-
pass
|
260
|
-
|
261
|
-
value = self._fn.__get__(instance, owner)()
|
262
|
-
if value is _IGNORE:
|
263
|
-
return None
|
264
|
-
instance.__dict__[self._name] = value
|
265
|
-
return value
|
266
|
-
|
267
|
-
def __set__(self, instance, value):
|
268
|
-
if self._ignore_if(value):
|
269
|
-
return
|
270
|
-
if instance.__dict__[self._name] == value:
|
271
|
-
return
|
272
|
-
raise TypeError(self._name)
|
273
|
-
|
274
|
-
def __delete__(self, instance):
|
275
|
-
raise TypeError
|
276
|
-
|
277
|
-
|
278
|
-
def cached_property(fn=None, **kwargs): # noqa
|
279
|
-
if fn is None:
|
280
|
-
return functools.partial(cached_property, **kwargs)
|
281
|
-
return _CachedProperty(fn, **kwargs)
|
@@ -0,0 +1,118 @@
|
|
1
|
+
import abc
|
2
|
+
import functools
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
from ..attrs import transient_getattr
|
6
|
+
from ..attrs import transient_setattr
|
7
|
+
from ..classes.abstract import Abstract
|
8
|
+
|
9
|
+
|
10
|
+
_IGNORE = object()
|
11
|
+
|
12
|
+
|
13
|
+
##
|
14
|
+
|
15
|
+
|
16
|
+
class _CachedProperty(property, Abstract):
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
fn,
|
20
|
+
*,
|
21
|
+
name=None,
|
22
|
+
ignore_if=lambda _: False,
|
23
|
+
):
|
24
|
+
if isinstance(fn, property):
|
25
|
+
fn = fn.fget
|
26
|
+
|
27
|
+
super().__init__(fn)
|
28
|
+
|
29
|
+
self._fn = fn
|
30
|
+
self._ignore_if = ignore_if
|
31
|
+
|
32
|
+
self._name = name
|
33
|
+
|
34
|
+
def __set_name__(self, owner, name):
|
35
|
+
if self._name is None:
|
36
|
+
self._name = name
|
37
|
+
|
38
|
+
@abc.abstractmethod
|
39
|
+
def _instance_get(self, instance: ta.Any) -> tuple[ta.Any, bool]:
|
40
|
+
raise NotImplementedError
|
41
|
+
|
42
|
+
@abc.abstractmethod
|
43
|
+
def _instance_set(self, instance: ta.Any, value: ta.Any) -> None:
|
44
|
+
raise NotImplementedError
|
45
|
+
|
46
|
+
def __get__(self, instance, owner=None):
|
47
|
+
if instance is None:
|
48
|
+
return self
|
49
|
+
if self._name is None:
|
50
|
+
raise TypeError(self)
|
51
|
+
|
52
|
+
value, ok = self._instance_get(instance)
|
53
|
+
if ok:
|
54
|
+
return value
|
55
|
+
|
56
|
+
value = self._fn.__get__(instance, owner)()
|
57
|
+
if value is _IGNORE:
|
58
|
+
return None
|
59
|
+
|
60
|
+
self._instance_set(instance, value)
|
61
|
+
return value
|
62
|
+
|
63
|
+
def __set__(self, instance, value):
|
64
|
+
if self._ignore_if(value):
|
65
|
+
return
|
66
|
+
|
67
|
+
ev, ok = self._instance_get(instance)
|
68
|
+
if ok and ev == value:
|
69
|
+
return
|
70
|
+
|
71
|
+
raise TypeError(self._name)
|
72
|
+
|
73
|
+
def __delete__(self, instance):
|
74
|
+
raise TypeError
|
75
|
+
|
76
|
+
|
77
|
+
#
|
78
|
+
|
79
|
+
|
80
|
+
class _DictCachedProperty(_CachedProperty):
|
81
|
+
def _instance_get(self, instance: ta.Any) -> tuple[ta.Any, bool]:
|
82
|
+
try:
|
83
|
+
value = instance.__dict__[self._name]
|
84
|
+
except KeyError:
|
85
|
+
return None, False
|
86
|
+
else:
|
87
|
+
return value, True
|
88
|
+
|
89
|
+
def _instance_set(self, instance: ta.Any, value: ta.Any) -> None:
|
90
|
+
instance.__dict__[self._name] = value
|
91
|
+
|
92
|
+
|
93
|
+
#
|
94
|
+
|
95
|
+
|
96
|
+
class _TransientCachedProperty(_CachedProperty):
|
97
|
+
def _instance_get(self, instance: ta.Any) -> tuple[ta.Any, bool]:
|
98
|
+
try:
|
99
|
+
value = transient_getattr(instance, self._name)
|
100
|
+
except AttributeError:
|
101
|
+
return None, False
|
102
|
+
else:
|
103
|
+
return value, True
|
104
|
+
|
105
|
+
def _instance_set(self, instance: ta.Any, value: ta.Any) -> None:
|
106
|
+
transient_setattr(instance, self._name, value)
|
107
|
+
|
108
|
+
|
109
|
+
#
|
110
|
+
|
111
|
+
|
112
|
+
def cached_property(fn=None, *, transient=False, **kwargs): # noqa
|
113
|
+
if fn is None:
|
114
|
+
return functools.partial(cached_property, transient=transient, **kwargs)
|
115
|
+
if transient:
|
116
|
+
return _TransientCachedProperty(fn, **kwargs)
|
117
|
+
else:
|
118
|
+
return _DictCachedProperty(fn, **kwargs)
|
omlish/lang/classes/__init__.py
CHANGED
@@ -1,41 +0,0 @@
|
|
1
|
-
from .abstract import ( # noqa
|
2
|
-
Abstract,
|
3
|
-
AbstractTypeError,
|
4
|
-
get_abstract_methods,
|
5
|
-
is_abstract,
|
6
|
-
is_abstract_class,
|
7
|
-
is_abstract_method,
|
8
|
-
make_abstract,
|
9
|
-
unabstract_class,
|
10
|
-
)
|
11
|
-
|
12
|
-
from .restrict import ( # noqa
|
13
|
-
AnySensitive,
|
14
|
-
Final,
|
15
|
-
FinalTypeError,
|
16
|
-
NoBool,
|
17
|
-
NotInstantiable,
|
18
|
-
NotPicklable,
|
19
|
-
PackageSealed,
|
20
|
-
SENSITIVE_ATTR,
|
21
|
-
Sealed,
|
22
|
-
SealedError,
|
23
|
-
Sensitive,
|
24
|
-
no_bool,
|
25
|
-
)
|
26
|
-
|
27
|
-
from .simple import ( # noqa
|
28
|
-
LazySingleton,
|
29
|
-
Marker,
|
30
|
-
Namespace,
|
31
|
-
SimpleMetaDict,
|
32
|
-
Singleton,
|
33
|
-
)
|
34
|
-
|
35
|
-
from .virtual import ( # noqa
|
36
|
-
Callable,
|
37
|
-
Descriptor,
|
38
|
-
Picklable,
|
39
|
-
Virtual,
|
40
|
-
virtual_check,
|
41
|
-
)
|
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,8 +1,8 @@
|
|
1
1
|
omlish/.manifests.json,sha256=vQTAIvR8OblSq-uP2GUfnbei0RnmAnM5j0T1-OToh9E,8253
|
2
|
-
omlish/__about__.py,sha256=
|
2
|
+
omlish/__about__.py,sha256=igneh_Z18EcRdtPlDRmK5tHmRK3NpxQPFoUopKrrNtw,3380
|
3
3
|
omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
|
4
4
|
omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
|
5
|
-
omlish/cached.py,sha256=
|
5
|
+
omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
|
6
6
|
omlish/check.py,sha256=THqm6jD1a0skAO5EC8SOVg58yq96Vk5wcuruBkCYxyU,2016
|
7
7
|
omlish/datetimes.py,sha256=HajeM1kBvwlTa-uR1TTZHmZ3zTPnnUr1uGGQhiO1XQ0,2152
|
8
8
|
omlish/defs.py,sha256=9uUjJuVIbCBL3g14fyzAp-9gH935MFofvlfOGwcBIaM,4913
|
@@ -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
|
@@ -399,8 +399,8 @@ omlish/iterators/iterators.py,sha256=ghI4dO6WPyyFOLTIIMaHQ_IOy2xXaFpGPqveZ5YGIBU
|
|
399
399
|
omlish/iterators/recipes.py,sha256=53mkexitMhkwXQZbL6DrhpT0WePQ_56uXd5Jaw3DfzI,467
|
400
400
|
omlish/iterators/tools.py,sha256=Pi4ybXytUXVZ3xwK89xpPImQfYYId9p1vIFQvVqVLqA,2551
|
401
401
|
omlish/iterators/unique.py,sha256=0jAX3kwzVfRNhe0Tmh7kVP_Q2WBIn8POo_O-rgFV0rQ,1390
|
402
|
-
omlish/lang/__init__.py,sha256=
|
403
|
-
omlish/lang/
|
402
|
+
omlish/lang/__init__.py,sha256=0LNdzzVTKiDEmjCzT9wUPbimcAu21KYe-EwD4xDgtRM,4592
|
403
|
+
omlish/lang/attrs.py,sha256=vGYdGnS7ZAbSz0ZSZkkZKHCK1tKoNjZY0JdzFraHiUw,3105
|
404
404
|
omlish/lang/clsdct.py,sha256=sJYadm-fwzti-gsi98knR5qQUxriBmOqQE_qz3RopNk,1743
|
405
405
|
omlish/lang/cmp.py,sha256=5vbzWWbqdzDmNKAGL19z6ZfUKe5Ci49e-Oegf9f4BsE,1346
|
406
406
|
omlish/lang/contextmanagers.py,sha256=UPH6daYwSP9cH5AfSVsJyEHk1UURMGhVPM5ZRhp_Hvw,7576
|
@@ -418,7 +418,10 @@ omlish/lang/resources.py,sha256=N64KeVE-rYMxqBBRp91qzgVqpOVR2uX7k1WlS_bo5hM,2681
|
|
418
418
|
omlish/lang/strings.py,sha256=egdv8PxLNG40-5V93agP5j2rBUDIsahCx048zV7uEbU,4690
|
419
419
|
omlish/lang/sys.py,sha256=UoZz_PJYVKLQAKqYxxn-LHz1okK_38I__maZgnXMcxU,406
|
420
420
|
omlish/lang/typing.py,sha256=Zdad9Zv0sa-hIaUXPrzPidT7sDVpRcussAI7D-j-I1c,3296
|
421
|
-
omlish/lang/
|
421
|
+
omlish/lang/cached/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
422
|
+
omlish/lang/cached/function.py,sha256=k3jRNuDtFh9Mk97oonC7nq5XRros2qmRJVWviMFrx74,8728
|
423
|
+
omlish/lang/cached/property.py,sha256=kzbao_35PlszdK_9oJBWrMmFFlVK_Xhx7YczHhTJ6cc,2764
|
424
|
+
omlish/lang/classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
422
425
|
omlish/lang/classes/abstract.py,sha256=bIcuAetV_aChhpSURypmjjcqP07xi20uVYPKh1kvQNU,3710
|
423
426
|
omlish/lang/classes/restrict.py,sha256=QvM-GqXvO8vpCONkcr0QpgVPfzZmgDo7o8d9kUwOzlo,3941
|
424
427
|
omlish/lang/classes/simple.py,sha256=XQ8b86WvQA0qtSYqlbMOJS7tHgE8sv9onda33uQmbkM,3294
|
@@ -566,7 +569,7 @@ omlish/sockets/bind.py,sha256=J1SfFFFnVf3H5nqESDX2NGEY8DmjyIMUXZciZM33zQY,8003
|
|
566
569
|
omlish/sockets/handlers.py,sha256=Gj6xZoo4vommge8XvkehYw3B7O4aql2P4qzZIIa0p24,462
|
567
570
|
omlish/sockets/io.py,sha256=lfhTkB7NnAIx9kuQhAkwgsEUXY78Mp1_WtYrIQNS_k8,1408
|
568
571
|
omlish/sockets/ports.py,sha256=Wm4mRFFz5MdD8KbdaEfT1c4PbJnsuK_iyJlZJE_-8jo,1402
|
569
|
-
omlish/sockets/wait.py,sha256=
|
572
|
+
omlish/sockets/wait.py,sha256=aznyOzGa9oNBc31xnyk1S7TylO8hGx7vFyOVsdY3zFE,1585
|
570
573
|
omlish/sockets/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
571
574
|
omlish/sockets/server/handlers.py,sha256=PPsb1X5oU9dN8jfztaMGsRiqWTyEANT-1aSLbS6bUVg,3867
|
572
575
|
omlish/sockets/server/server.py,sha256=FkaishIxJuU4it9tTI7wzlGqJYzFGXzDrd_HgV0jAmU,6253
|
@@ -729,9 +732,9 @@ omlish/text/mangle.py,sha256=kfzFLfvepH-chl1P89_mdc5vC4FSqyPA2aVtgzuB8IY,1133
|
|
729
732
|
omlish/text/minja.py,sha256=jZC-fp3Xuhx48ppqsf2Sf1pHbC0t8XBB7UpUUoOk2Qw,5751
|
730
733
|
omlish/text/parts.py,sha256=JkNZpyR2tv2CNcTaWJJhpQ9E4F0yPR8P_YfDbZfMtwQ,6182
|
731
734
|
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.
|
735
|
+
omlish-0.0.0.dev245.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
736
|
+
omlish-0.0.0.dev245.dist-info/METADATA,sha256=JjIb3VY1vdwMkndkHrbTmD72X00OMiCJVwBpn07rc88,4176
|
737
|
+
omlish-0.0.0.dev245.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
|
738
|
+
omlish-0.0.0.dev245.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
739
|
+
omlish-0.0.0.dev245.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
740
|
+
omlish-0.0.0.dev245.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|