omlish 0.0.0.dev128__py3-none-any.whl → 0.0.0.dev130__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- omlish/__about__.py +2 -2
- omlish/lite/check.py +37 -0
- omlish/lite/fdio/__init__.py +0 -0
- omlish/lite/fdio/corohttp.py +135 -0
- omlish/lite/fdio/handlers.py +67 -0
- omlish/lite/fdio/kqueue.py +102 -0
- omlish/lite/fdio/manager.py +48 -0
- omlish/lite/fdio/pollers.py +212 -0
- omlish/lite/inject.py +127 -16
- omlish/lite/io.py +98 -0
- {omlish-0.0.0.dev128.dist-info → omlish-0.0.0.dev130.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev128.dist-info → omlish-0.0.0.dev130.dist-info}/RECORD +16 -10
- {omlish-0.0.0.dev128.dist-info → omlish-0.0.0.dev130.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev128.dist-info → omlish-0.0.0.dev130.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev128.dist-info → omlish-0.0.0.dev130.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev128.dist-info → omlish-0.0.0.dev130.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/lite/check.py
CHANGED
@@ -3,6 +3,7 @@ import typing as ta
|
|
3
3
|
|
4
4
|
|
5
5
|
T = ta.TypeVar('T')
|
6
|
+
SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
|
6
7
|
|
7
8
|
|
8
9
|
def check_isinstance(v: ta.Any, spec: ta.Union[ta.Type[T], tuple]) -> T:
|
@@ -57,6 +58,42 @@ def check_not_equal(l: T, r: T) -> T:
|
|
57
58
|
return l
|
58
59
|
|
59
60
|
|
61
|
+
def check_is(l: T, r: T) -> T:
|
62
|
+
if l is not r:
|
63
|
+
raise ValueError(l, r)
|
64
|
+
return l
|
65
|
+
|
66
|
+
|
67
|
+
def check_is_not(l: T, r: ta.Any) -> T:
|
68
|
+
if l is r:
|
69
|
+
raise ValueError(l, r)
|
70
|
+
return l
|
71
|
+
|
72
|
+
|
73
|
+
def check_in(v: T, c: ta.Container[T]) -> T:
|
74
|
+
if v not in c:
|
75
|
+
raise ValueError(v, c)
|
76
|
+
return v
|
77
|
+
|
78
|
+
|
79
|
+
def check_not_in(v: T, c: ta.Container[T]) -> T:
|
80
|
+
if v in c:
|
81
|
+
raise ValueError(v, c)
|
82
|
+
return v
|
83
|
+
|
84
|
+
|
60
85
|
def check_single(vs: ta.Iterable[T]) -> T:
|
61
86
|
[v] = vs
|
62
87
|
return v
|
88
|
+
|
89
|
+
|
90
|
+
def check_empty(v: SizedT) -> SizedT:
|
91
|
+
if len(v):
|
92
|
+
raise ValueError(v)
|
93
|
+
return v
|
94
|
+
|
95
|
+
|
96
|
+
def check_non_empty(v: SizedT) -> SizedT:
|
97
|
+
if not len(v):
|
98
|
+
raise ValueError(v)
|
99
|
+
return v
|
File without changes
|
@@ -0,0 +1,135 @@
|
|
1
|
+
import socket
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from ..check import check_isinstance
|
5
|
+
from ..check import check_none
|
6
|
+
from ..check import check_not_none
|
7
|
+
from ..check import check_state
|
8
|
+
from ..http.coroserver import CoroHttpServer
|
9
|
+
from ..http.handlers import HttpHandler
|
10
|
+
from ..io import IncrementalWriteBuffer
|
11
|
+
from ..io import ReadableListBuffer
|
12
|
+
from ..socket import SocketAddress
|
13
|
+
from .handlers import SocketFdIoHandler
|
14
|
+
|
15
|
+
|
16
|
+
class CoroHttpServerConnectionFdIoHandler(SocketFdIoHandler):
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
addr: SocketAddress,
|
20
|
+
sock: socket.socket,
|
21
|
+
handler: HttpHandler,
|
22
|
+
*,
|
23
|
+
read_size: int = 0x10000,
|
24
|
+
write_size: int = 0x10000,
|
25
|
+
) -> None:
|
26
|
+
check_state(not sock.getblocking())
|
27
|
+
|
28
|
+
super().__init__(addr, sock)
|
29
|
+
|
30
|
+
self._handler = handler
|
31
|
+
self._read_size = read_size
|
32
|
+
self._write_size = write_size
|
33
|
+
|
34
|
+
self._read_buf = ReadableListBuffer()
|
35
|
+
self._write_buf: IncrementalWriteBuffer | None = None
|
36
|
+
|
37
|
+
self._coro_srv = CoroHttpServer(
|
38
|
+
addr,
|
39
|
+
handler=self._handler,
|
40
|
+
)
|
41
|
+
self._srv_coro: ta.Optional[ta.Generator[CoroHttpServer.Io, ta.Optional[bytes], None]] = self._coro_srv.coro_handle() # noqa
|
42
|
+
|
43
|
+
self._cur_io: CoroHttpServer.Io | None = None
|
44
|
+
self._next_io()
|
45
|
+
|
46
|
+
#
|
47
|
+
|
48
|
+
def _next_io(self) -> None: # noqa
|
49
|
+
coro = check_not_none(self._srv_coro)
|
50
|
+
|
51
|
+
d: bytes | None = None
|
52
|
+
o = self._cur_io
|
53
|
+
while True:
|
54
|
+
if o is None:
|
55
|
+
try:
|
56
|
+
if d is not None:
|
57
|
+
o = coro.send(d)
|
58
|
+
d = None
|
59
|
+
else:
|
60
|
+
o = next(coro)
|
61
|
+
except StopIteration:
|
62
|
+
self.close()
|
63
|
+
o = None
|
64
|
+
break
|
65
|
+
|
66
|
+
if isinstance(o, CoroHttpServer.AnyLogIo):
|
67
|
+
print(o)
|
68
|
+
o = None
|
69
|
+
|
70
|
+
elif isinstance(o, CoroHttpServer.ReadIo):
|
71
|
+
if (d := self._read_buf.read(o.sz)) is None:
|
72
|
+
break
|
73
|
+
o = None
|
74
|
+
|
75
|
+
elif isinstance(o, CoroHttpServer.ReadLineIo):
|
76
|
+
if (d := self._read_buf.read_until(b'\n')) is None:
|
77
|
+
break
|
78
|
+
o = None
|
79
|
+
|
80
|
+
elif isinstance(o, CoroHttpServer.WriteIo):
|
81
|
+
check_none(self._write_buf)
|
82
|
+
self._write_buf = IncrementalWriteBuffer(o.data, write_size=self._write_size)
|
83
|
+
break
|
84
|
+
|
85
|
+
else:
|
86
|
+
raise TypeError(o)
|
87
|
+
|
88
|
+
self._cur_io = o
|
89
|
+
|
90
|
+
#
|
91
|
+
|
92
|
+
def readable(self) -> bool:
|
93
|
+
return True
|
94
|
+
|
95
|
+
def writable(self) -> bool:
|
96
|
+
return self._write_buf is not None
|
97
|
+
|
98
|
+
#
|
99
|
+
|
100
|
+
def on_readable(self) -> None:
|
101
|
+
try:
|
102
|
+
buf = check_not_none(self._sock).recv(self._read_size)
|
103
|
+
except BlockingIOError:
|
104
|
+
return
|
105
|
+
except ConnectionResetError:
|
106
|
+
self.close()
|
107
|
+
return
|
108
|
+
if not buf:
|
109
|
+
self.close()
|
110
|
+
return
|
111
|
+
|
112
|
+
self._read_buf.feed(buf)
|
113
|
+
|
114
|
+
if isinstance(self._cur_io, CoroHttpServer.AnyReadIo):
|
115
|
+
self._next_io()
|
116
|
+
|
117
|
+
def on_writable(self) -> None:
|
118
|
+
check_isinstance(self._cur_io, CoroHttpServer.WriteIo)
|
119
|
+
wb = check_not_none(self._write_buf)
|
120
|
+
while wb.rem > 0:
|
121
|
+
def send(d: bytes) -> int:
|
122
|
+
try:
|
123
|
+
return check_not_none(self._sock).send(d)
|
124
|
+
except ConnectionResetError:
|
125
|
+
self.close()
|
126
|
+
return 0
|
127
|
+
except BlockingIOError:
|
128
|
+
return 0
|
129
|
+
if not wb.write(send):
|
130
|
+
break
|
131
|
+
|
132
|
+
if wb.rem < 1:
|
133
|
+
self._write_buf = None
|
134
|
+
self._cur_io = None
|
135
|
+
self._next_io()
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import abc
|
3
|
+
import socket
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from ..check import check_not_none
|
7
|
+
from ..socket import SocketAddress
|
8
|
+
|
9
|
+
|
10
|
+
class FdIoHandler(abc.ABC):
|
11
|
+
@abc.abstractmethod
|
12
|
+
def fd(self) -> int:
|
13
|
+
raise NotImplementedError
|
14
|
+
|
15
|
+
#
|
16
|
+
|
17
|
+
@property
|
18
|
+
@abc.abstractmethod
|
19
|
+
def closed(self) -> bool:
|
20
|
+
raise NotImplementedError
|
21
|
+
|
22
|
+
@abc.abstractmethod
|
23
|
+
def close(self) -> None:
|
24
|
+
raise NotImplementedError
|
25
|
+
|
26
|
+
#
|
27
|
+
|
28
|
+
def readable(self) -> bool:
|
29
|
+
return False
|
30
|
+
|
31
|
+
def writable(self) -> bool:
|
32
|
+
return False
|
33
|
+
|
34
|
+
#
|
35
|
+
|
36
|
+
def on_readable(self) -> None:
|
37
|
+
raise TypeError
|
38
|
+
|
39
|
+
def on_writable(self) -> None:
|
40
|
+
raise TypeError
|
41
|
+
|
42
|
+
def on_error(self, exc: ta.Optional[BaseException] = None) -> None: # noqa
|
43
|
+
pass
|
44
|
+
|
45
|
+
|
46
|
+
class SocketFdIoHandler(FdIoHandler, abc.ABC):
|
47
|
+
def __init__(
|
48
|
+
self,
|
49
|
+
addr: SocketAddress,
|
50
|
+
sock: socket.socket,
|
51
|
+
) -> None:
|
52
|
+
super().__init__()
|
53
|
+
|
54
|
+
self._addr = addr
|
55
|
+
self._sock: ta.Optional[socket.socket] = sock
|
56
|
+
|
57
|
+
def fd(self) -> int:
|
58
|
+
return check_not_none(self._sock).fileno()
|
59
|
+
|
60
|
+
@property
|
61
|
+
def closed(self) -> bool:
|
62
|
+
return self._sock is None
|
63
|
+
|
64
|
+
def close(self) -> None:
|
65
|
+
if self._sock is not None:
|
66
|
+
self._sock.close()
|
67
|
+
self._sock = None
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import errno
|
3
|
+
import select
|
4
|
+
import sys
|
5
|
+
import typing as ta
|
6
|
+
|
7
|
+
from .pollers import FdIoPoller
|
8
|
+
|
9
|
+
|
10
|
+
KqueueFdIoPoller: ta.Optional[ta.Type[FdIoPoller]]
|
11
|
+
if sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
|
12
|
+
|
13
|
+
class _KqueueFdIoPoller(FdIoPoller):
|
14
|
+
DEFAULT_MAX_EVENTS = 1000
|
15
|
+
|
16
|
+
def __init__(
|
17
|
+
self,
|
18
|
+
*,
|
19
|
+
max_events: int = DEFAULT_MAX_EVENTS,
|
20
|
+
) -> None:
|
21
|
+
super().__init__()
|
22
|
+
|
23
|
+
self._max_events = max_events
|
24
|
+
|
25
|
+
self._kqueue: ta.Optional[ta.Any] = None
|
26
|
+
|
27
|
+
#
|
28
|
+
|
29
|
+
def _get_kqueue(self) -> 'select.kqueue':
|
30
|
+
if (kq := self._kqueue) is not None:
|
31
|
+
return kq
|
32
|
+
kq = select.kqueue()
|
33
|
+
self._kqueue = kq
|
34
|
+
return kq
|
35
|
+
|
36
|
+
def close(self) -> None:
|
37
|
+
if self._kqueue is not None:
|
38
|
+
self._kqueue.close()
|
39
|
+
self._kqueue = None
|
40
|
+
|
41
|
+
def reopen(self) -> None:
|
42
|
+
for fd in self._readable:
|
43
|
+
self._register_readable(fd)
|
44
|
+
for fd in self._writable:
|
45
|
+
self._register_writable(fd)
|
46
|
+
|
47
|
+
#
|
48
|
+
|
49
|
+
def _register_readable(self, fd: int) -> None:
|
50
|
+
self._control(fd, select.KQ_FILTER_READ, select.KQ_EV_ADD)
|
51
|
+
|
52
|
+
def _register_writable(self, fd: int) -> None:
|
53
|
+
self._control(fd, select.KQ_FILTER_WRITE, select.KQ_EV_ADD)
|
54
|
+
|
55
|
+
def _unregister_readable(self, fd: int) -> None:
|
56
|
+
self._control(fd, select.KQ_FILTER_READ, select.KQ_EV_DELETE)
|
57
|
+
|
58
|
+
def _unregister_writable(self, fd: int) -> None:
|
59
|
+
self._control(fd, select.KQ_FILTER_WRITE, select.KQ_EV_DELETE)
|
60
|
+
|
61
|
+
def _control(self, fd: int, filter: int, flags: int) -> None: # noqa
|
62
|
+
ke = select.kevent(fd, filter=filter, flags=flags)
|
63
|
+
kq = self._get_kqueue()
|
64
|
+
try:
|
65
|
+
kq.control([ke], 0)
|
66
|
+
|
67
|
+
except OSError as exc:
|
68
|
+
if exc.errno == errno.EBADF:
|
69
|
+
# log.debug('EBADF encountered in kqueue. Invalid file descriptor %s', ke.ident)
|
70
|
+
pass
|
71
|
+
elif exc.errno == errno.ENOENT:
|
72
|
+
# Can happen when trying to remove an already closed socket
|
73
|
+
pass
|
74
|
+
else:
|
75
|
+
raise
|
76
|
+
|
77
|
+
#
|
78
|
+
|
79
|
+
def poll(self, timeout: ta.Optional[float]) -> FdIoPoller.PollResult:
|
80
|
+
kq = self._get_kqueue()
|
81
|
+
try:
|
82
|
+
kes = kq.control(None, self._max_events, timeout)
|
83
|
+
|
84
|
+
except OSError as exc:
|
85
|
+
if exc.errno == errno.EINTR:
|
86
|
+
return FdIoPoller.PollResult(msg='EINTR encountered in poll', exc=exc)
|
87
|
+
else:
|
88
|
+
raise
|
89
|
+
|
90
|
+
r: ta.List[int] = []
|
91
|
+
w: ta.List[int] = []
|
92
|
+
for ke in kes:
|
93
|
+
if ke.filter == select.KQ_FILTER_READ:
|
94
|
+
r.append(ke.ident)
|
95
|
+
if ke.filter == select.KQ_FILTER_WRITE:
|
96
|
+
w.append(ke.ident)
|
97
|
+
|
98
|
+
return FdIoPoller.PollResult(r, w)
|
99
|
+
|
100
|
+
KqueueFdIoPoller = _KqueueFdIoPoller
|
101
|
+
else:
|
102
|
+
KqueueFdIoPoller = None
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from .handlers import FdIoHandler
|
5
|
+
from .pollers import FdIoPoller
|
6
|
+
|
7
|
+
|
8
|
+
class FdIoManager:
|
9
|
+
def __init__(
|
10
|
+
self,
|
11
|
+
poller: FdIoPoller,
|
12
|
+
) -> None:
|
13
|
+
super().__init__()
|
14
|
+
|
15
|
+
self._poller = poller
|
16
|
+
|
17
|
+
self._handlers: ta.Dict[int, FdIoHandler] = {} # Preserves insertion order
|
18
|
+
|
19
|
+
def register(self, h: FdIoHandler) -> None:
|
20
|
+
if (hid := id(h)) in self._handlers:
|
21
|
+
raise KeyError(h)
|
22
|
+
self._handlers[hid] = h
|
23
|
+
|
24
|
+
def unregister(self, h: FdIoHandler) -> None:
|
25
|
+
del self._handlers[id(h)]
|
26
|
+
|
27
|
+
def poll(self, *, timeout: float = 1.) -> None:
|
28
|
+
hs = list(self._handlers.values())
|
29
|
+
rd = {h.fd(): h for h in hs if h.readable()}
|
30
|
+
wd = {h.fd(): h for h in hs if h.writable()}
|
31
|
+
|
32
|
+
self._poller.update(set(rd), set(wd))
|
33
|
+
|
34
|
+
pr = self._poller.poll(timeout)
|
35
|
+
|
36
|
+
for f in pr.r:
|
37
|
+
if not (h := rd[f]).closed:
|
38
|
+
h.on_readable()
|
39
|
+
for f in pr.w:
|
40
|
+
if not (h := wd[f]).closed:
|
41
|
+
h.on_writable()
|
42
|
+
|
43
|
+
hs = list(self._handlers.values())
|
44
|
+
nh = {}
|
45
|
+
for h in hs:
|
46
|
+
if not h.closed:
|
47
|
+
nh[id(h)] = h
|
48
|
+
self._handlers = nh
|
@@ -0,0 +1,212 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import abc
|
3
|
+
import dataclasses as dc
|
4
|
+
import errno
|
5
|
+
import select
|
6
|
+
import typing as ta
|
7
|
+
|
8
|
+
|
9
|
+
##
|
10
|
+
|
11
|
+
|
12
|
+
class FdIoPoller(abc.ABC):
|
13
|
+
def __init__(self) -> None:
|
14
|
+
super().__init__()
|
15
|
+
|
16
|
+
self._readable: ta.Set[int] = set()
|
17
|
+
self._writable: ta.Set[int] = set()
|
18
|
+
|
19
|
+
#
|
20
|
+
|
21
|
+
def close(self) -> None: # noqa
|
22
|
+
pass
|
23
|
+
|
24
|
+
def reopen(self) -> None: # noqa
|
25
|
+
pass
|
26
|
+
|
27
|
+
#
|
28
|
+
|
29
|
+
@property
|
30
|
+
@ta.final
|
31
|
+
def readable(self) -> ta.AbstractSet[int]:
|
32
|
+
return self._readable
|
33
|
+
|
34
|
+
@property
|
35
|
+
@ta.final
|
36
|
+
def writable(self) -> ta.AbstractSet[int]:
|
37
|
+
return self._writable
|
38
|
+
|
39
|
+
#
|
40
|
+
|
41
|
+
@ta.final
|
42
|
+
def register_readable(self, fd: int) -> bool:
|
43
|
+
if fd in self._readable:
|
44
|
+
return False
|
45
|
+
self._readable.add(fd)
|
46
|
+
self._register_readable(fd)
|
47
|
+
return True
|
48
|
+
|
49
|
+
@ta.final
|
50
|
+
def register_writable(self, fd: int) -> bool:
|
51
|
+
if fd in self._writable:
|
52
|
+
return False
|
53
|
+
self._writable.add(fd)
|
54
|
+
self._register_writable(fd)
|
55
|
+
return True
|
56
|
+
|
57
|
+
@ta.final
|
58
|
+
def unregister_readable(self, fd: int) -> bool:
|
59
|
+
if fd not in self._readable:
|
60
|
+
return False
|
61
|
+
self._readable.discard(fd)
|
62
|
+
self._unregister_readable(fd)
|
63
|
+
return True
|
64
|
+
|
65
|
+
@ta.final
|
66
|
+
def unregister_writable(self, fd: int) -> bool:
|
67
|
+
if fd not in self._writable:
|
68
|
+
return False
|
69
|
+
self._writable.discard(fd)
|
70
|
+
self._unregister_writable(fd)
|
71
|
+
return True
|
72
|
+
|
73
|
+
#
|
74
|
+
|
75
|
+
def _register_readable(self, fd: int) -> None: # noqa
|
76
|
+
pass
|
77
|
+
|
78
|
+
def _register_writable(self, fd: int) -> None: # noqa
|
79
|
+
pass
|
80
|
+
|
81
|
+
def _unregister_readable(self, fd: int) -> None: # noqa
|
82
|
+
pass
|
83
|
+
|
84
|
+
def _unregister_writable(self, fd: int) -> None: # noqa
|
85
|
+
pass
|
86
|
+
|
87
|
+
#
|
88
|
+
|
89
|
+
def update(
|
90
|
+
self,
|
91
|
+
r: ta.AbstractSet[int],
|
92
|
+
w: ta.AbstractSet[int],
|
93
|
+
) -> None:
|
94
|
+
for f in r - self._readable:
|
95
|
+
self.register_readable(f)
|
96
|
+
for f in w - self._writable:
|
97
|
+
self.register_writable(f)
|
98
|
+
for f in self._readable - r:
|
99
|
+
self.unregister_readable(f)
|
100
|
+
for f in self._writable - w:
|
101
|
+
self.unregister_writable(f)
|
102
|
+
|
103
|
+
#
|
104
|
+
|
105
|
+
@dc.dataclass(frozen=True)
|
106
|
+
class PollResult:
|
107
|
+
r: ta.Sequence[int] = ()
|
108
|
+
w: ta.Sequence[int] = ()
|
109
|
+
|
110
|
+
inv: ta.Sequence[int] = ()
|
111
|
+
|
112
|
+
msg: ta.Optional[str] = None
|
113
|
+
exc: ta.Optional[BaseException] = None
|
114
|
+
|
115
|
+
@abc.abstractmethod
|
116
|
+
def poll(self, timeout: ta.Optional[float]) -> PollResult:
|
117
|
+
raise NotImplementedError
|
118
|
+
|
119
|
+
|
120
|
+
##
|
121
|
+
|
122
|
+
|
123
|
+
class SelectFdIoPoller(FdIoPoller):
|
124
|
+
def poll(self, timeout: ta.Optional[float]) -> FdIoPoller.PollResult:
|
125
|
+
try:
|
126
|
+
r, w, x = select.select(
|
127
|
+
self._readable,
|
128
|
+
self._writable,
|
129
|
+
[],
|
130
|
+
timeout,
|
131
|
+
)
|
132
|
+
|
133
|
+
except OSError as exc:
|
134
|
+
if exc.errno == errno.EINTR:
|
135
|
+
return FdIoPoller.PollResult(msg='EINTR encountered in poll', exc=exc)
|
136
|
+
elif exc.errno == errno.EBADF:
|
137
|
+
return FdIoPoller.PollResult(msg='EBADF encountered in poll', exc=exc)
|
138
|
+
else:
|
139
|
+
raise
|
140
|
+
|
141
|
+
return FdIoPoller.PollResult(r, w)
|
142
|
+
|
143
|
+
|
144
|
+
##
|
145
|
+
|
146
|
+
|
147
|
+
PollFdIoPoller: ta.Optional[ta.Type[FdIoPoller]]
|
148
|
+
if hasattr(select, 'poll'):
|
149
|
+
|
150
|
+
class _PollFdIoPoller(FdIoPoller):
|
151
|
+
def __init__(self) -> None:
|
152
|
+
super().__init__()
|
153
|
+
|
154
|
+
self._poller = select.poll()
|
155
|
+
|
156
|
+
#
|
157
|
+
|
158
|
+
_READ = select.POLLIN | select.POLLPRI | select.POLLHUP
|
159
|
+
_WRITE = select.POLLOUT
|
160
|
+
|
161
|
+
def _register_readable(self, fd: int) -> None:
|
162
|
+
self._update_registration(fd)
|
163
|
+
|
164
|
+
def _register_writable(self, fd: int) -> None:
|
165
|
+
self._update_registration(fd)
|
166
|
+
|
167
|
+
def _unregister_readable(self, fd: int) -> None:
|
168
|
+
self._update_registration(fd)
|
169
|
+
|
170
|
+
def _unregister_writable(self, fd: int) -> None:
|
171
|
+
self._update_registration(fd)
|
172
|
+
|
173
|
+
def _update_registration(self, fd: int) -> None:
|
174
|
+
r = fd in self._readable
|
175
|
+
w = fd in self._writable
|
176
|
+
if r or w:
|
177
|
+
self._poller.register(fd, (self._READ if r else 0) | (self._WRITE if w else 0))
|
178
|
+
else:
|
179
|
+
self._poller.unregister(fd)
|
180
|
+
|
181
|
+
#
|
182
|
+
|
183
|
+
def poll(self, timeout: ta.Optional[float]) -> FdIoPoller.PollResult:
|
184
|
+
polled: ta.List[ta.Tuple[int, int]]
|
185
|
+
try:
|
186
|
+
polled = self._poller.poll(timeout * 1000 if timeout is not None else None)
|
187
|
+
|
188
|
+
except OSError as exc:
|
189
|
+
if exc.errno == errno.EINTR:
|
190
|
+
return FdIoPoller.PollResult(msg='EINTR encountered in poll', exc=exc)
|
191
|
+
else:
|
192
|
+
raise
|
193
|
+
|
194
|
+
r: ta.List[int] = []
|
195
|
+
w: ta.List[int] = []
|
196
|
+
inv: ta.List[int] = []
|
197
|
+
for fd, mask in polled:
|
198
|
+
if mask & select.POLLNVAL:
|
199
|
+
self._poller.unregister(fd)
|
200
|
+
self._readable.discard(fd)
|
201
|
+
self._writable.discard(fd)
|
202
|
+
inv.append(fd)
|
203
|
+
continue
|
204
|
+
if mask & self._READ:
|
205
|
+
r.append(fd)
|
206
|
+
if mask & self._WRITE:
|
207
|
+
w.append(fd)
|
208
|
+
return FdIoPoller.PollResult(r, w, inv=inv)
|
209
|
+
|
210
|
+
PollFdIoPoller = _PollFdIoPoller
|
211
|
+
else:
|
212
|
+
PollFdIoPoller = None
|
omlish/lite/inject.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
import abc
|
3
|
+
import contextlib
|
3
4
|
import dataclasses as dc
|
4
5
|
import functools
|
5
6
|
import inspect
|
@@ -7,7 +8,9 @@ import types
|
|
7
8
|
import typing as ta
|
8
9
|
import weakref
|
9
10
|
|
11
|
+
from .check import check_in
|
10
12
|
from .check import check_isinstance
|
13
|
+
from .check import check_not_in
|
11
14
|
from .check import check_not_isinstance
|
12
15
|
from .check import check_not_none
|
13
16
|
from .maybes import Maybe
|
@@ -124,7 +127,7 @@ class InjectorError(Exception):
|
|
124
127
|
pass
|
125
128
|
|
126
129
|
|
127
|
-
@dc.dataclass(
|
130
|
+
@dc.dataclass()
|
128
131
|
class InjectorKeyError(InjectorError):
|
129
132
|
key: InjectorKey
|
130
133
|
|
@@ -132,16 +135,18 @@ class InjectorKeyError(InjectorError):
|
|
132
135
|
name: ta.Optional[str] = None
|
133
136
|
|
134
137
|
|
135
|
-
@dc.dataclass(frozen=True)
|
136
138
|
class UnboundInjectorKeyError(InjectorKeyError):
|
137
139
|
pass
|
138
140
|
|
139
141
|
|
140
|
-
@dc.dataclass(frozen=True)
|
141
142
|
class DuplicateInjectorKeyError(InjectorKeyError):
|
142
143
|
pass
|
143
144
|
|
144
145
|
|
146
|
+
class CyclicDependencyInjectorKeyError(InjectorKeyError):
|
147
|
+
pass
|
148
|
+
|
149
|
+
|
145
150
|
###
|
146
151
|
# keys
|
147
152
|
|
@@ -315,7 +320,11 @@ def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey,
|
|
315
320
|
|
316
321
|
for b in bs.bindings():
|
317
322
|
if b.key.array:
|
318
|
-
am.setdefault(b.key, [])
|
323
|
+
al = am.setdefault(b.key, [])
|
324
|
+
if isinstance(b.provider, ArrayInjectorProvider):
|
325
|
+
al.extend(b.provider.ps)
|
326
|
+
else:
|
327
|
+
al.append(b.provider)
|
319
328
|
else:
|
320
329
|
if b.key in pm:
|
321
330
|
raise KeyError(b.key)
|
@@ -463,6 +472,14 @@ def build_injection_kwargs_target(
|
|
463
472
|
_INJECTOR_INJECTOR_KEY: InjectorKey[Injector] = InjectorKey(Injector)
|
464
473
|
|
465
474
|
|
475
|
+
@dc.dataclass(frozen=True)
|
476
|
+
class _InjectorEager:
|
477
|
+
key: InjectorKey
|
478
|
+
|
479
|
+
|
480
|
+
_INJECTOR_EAGER_ARRAY_KEY: InjectorKey[_InjectorEager] = InjectorKey(_InjectorEager, array=True)
|
481
|
+
|
482
|
+
|
466
483
|
class _Injector(Injector):
|
467
484
|
def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
|
468
485
|
super().__init__()
|
@@ -475,22 +492,69 @@ class _Injector(Injector):
|
|
475
492
|
if _INJECTOR_INJECTOR_KEY in self._pfm:
|
476
493
|
raise DuplicateInjectorKeyError(_INJECTOR_INJECTOR_KEY)
|
477
494
|
|
495
|
+
self.__cur_req: ta.Optional[_Injector._Request] = None
|
496
|
+
|
497
|
+
if _INJECTOR_EAGER_ARRAY_KEY in self._pfm:
|
498
|
+
for e in self.provide(_INJECTOR_EAGER_ARRAY_KEY):
|
499
|
+
self.provide(e.key)
|
500
|
+
|
501
|
+
class _Request:
|
502
|
+
def __init__(self, injector: '_Injector') -> None:
|
503
|
+
super().__init__()
|
504
|
+
self._injector = injector
|
505
|
+
self._provisions: ta.Dict[InjectorKey, Maybe] = {}
|
506
|
+
self._seen_keys: ta.Set[InjectorKey] = set()
|
507
|
+
|
508
|
+
def handle_key(self, key: InjectorKey) -> Maybe[Maybe]:
|
509
|
+
try:
|
510
|
+
return Maybe.just(self._provisions[key])
|
511
|
+
except KeyError:
|
512
|
+
pass
|
513
|
+
if key in self._seen_keys:
|
514
|
+
raise CyclicDependencyInjectorKeyError(key)
|
515
|
+
self._seen_keys.add(key)
|
516
|
+
return Maybe.empty()
|
517
|
+
|
518
|
+
def handle_provision(self, key: InjectorKey, mv: Maybe) -> Maybe:
|
519
|
+
check_in(key, self._seen_keys)
|
520
|
+
check_not_in(key, self._provisions)
|
521
|
+
self._provisions[key] = mv
|
522
|
+
return mv
|
523
|
+
|
524
|
+
@contextlib.contextmanager
|
525
|
+
def _current_request(self) -> ta.Generator[_Request, None, None]:
|
526
|
+
if (cr := self.__cur_req) is not None:
|
527
|
+
yield cr
|
528
|
+
return
|
529
|
+
|
530
|
+
cr = self._Request(self)
|
531
|
+
try:
|
532
|
+
self.__cur_req = cr
|
533
|
+
yield cr
|
534
|
+
finally:
|
535
|
+
self.__cur_req = None
|
536
|
+
|
478
537
|
def try_provide(self, key: ta.Any) -> Maybe[ta.Any]:
|
479
538
|
key = as_injector_key(key)
|
480
539
|
|
481
|
-
|
482
|
-
|
540
|
+
cr: _Injector._Request
|
541
|
+
with self._current_request() as cr:
|
542
|
+
if (rv := cr.handle_key(key)).present:
|
543
|
+
return rv.must()
|
483
544
|
|
484
|
-
|
485
|
-
|
486
|
-
return Maybe.just(fn(self))
|
545
|
+
if key == _INJECTOR_INJECTOR_KEY:
|
546
|
+
return cr.handle_provision(key, Maybe.just(self))
|
487
547
|
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
return Maybe.empty()
|
548
|
+
fn = self._pfm.get(key)
|
549
|
+
if fn is not None:
|
550
|
+
return cr.handle_provision(key, Maybe.just(fn(self)))
|
492
551
|
|
493
|
-
|
552
|
+
if self._p is not None:
|
553
|
+
pv = self._p.try_provide(key)
|
554
|
+
if pv is not None:
|
555
|
+
return cr.handle_provision(key, Maybe.empty())
|
556
|
+
|
557
|
+
return cr.handle_provision(key, Maybe.empty())
|
494
558
|
|
495
559
|
def provide(self, key: ta.Any) -> ta.Any:
|
496
560
|
v = self.try_provide(key)
|
@@ -591,6 +655,8 @@ class InjectorBinder:
|
|
591
655
|
to_key: ta.Any = None,
|
592
656
|
|
593
657
|
singleton: bool = False,
|
658
|
+
|
659
|
+
eager: bool = False,
|
594
660
|
) -> InjectorBindingOrBindings:
|
595
661
|
if obj is None or obj is inspect.Parameter.empty:
|
596
662
|
raise TypeError(obj)
|
@@ -664,13 +730,21 @@ class InjectorBinder:
|
|
664
730
|
if singleton:
|
665
731
|
provider = SingletonInjectorProvider(provider)
|
666
732
|
|
733
|
+
binding = InjectorBinding(key, provider)
|
734
|
+
|
667
735
|
##
|
668
736
|
|
669
|
-
|
737
|
+
extras: ta.List[InjectorBinding] = []
|
738
|
+
|
739
|
+
if eager:
|
740
|
+
extras.append(bind_injector_eager_key(key))
|
670
741
|
|
671
742
|
##
|
672
743
|
|
673
|
-
|
744
|
+
if extras:
|
745
|
+
return as_injector_bindings(binding, *extras)
|
746
|
+
else:
|
747
|
+
return binding
|
674
748
|
|
675
749
|
|
676
750
|
###
|
@@ -693,6 +767,26 @@ def make_injector_factory(
|
|
693
767
|
return outer
|
694
768
|
|
695
769
|
|
770
|
+
def bind_injector_array(
|
771
|
+
obj: ta.Any = None,
|
772
|
+
*,
|
773
|
+
tag: ta.Any = None,
|
774
|
+
) -> InjectorBindingOrBindings:
|
775
|
+
key = as_injector_key(obj)
|
776
|
+
if tag is not None:
|
777
|
+
if key.tag is not None:
|
778
|
+
raise ValueError('Must not specify multiple tags')
|
779
|
+
key = dc.replace(key, tag=tag)
|
780
|
+
|
781
|
+
if key.array:
|
782
|
+
raise ValueError('Key must not be array')
|
783
|
+
|
784
|
+
return InjectorBinding(
|
785
|
+
dc.replace(key, array=True),
|
786
|
+
ArrayInjectorProvider([]),
|
787
|
+
)
|
788
|
+
|
789
|
+
|
696
790
|
def make_injector_array_type(
|
697
791
|
ele: ta.Union[InjectorKey, InjectorKeyCls],
|
698
792
|
cls: U,
|
@@ -714,6 +808,10 @@ def make_injector_array_type(
|
|
714
808
|
return inner
|
715
809
|
|
716
810
|
|
811
|
+
def bind_injector_eager_key(key: ta.Any) -> InjectorBinding:
|
812
|
+
return InjectorBinding(_INJECTOR_EAGER_ARRAY_KEY, ConstInjectorProvider(_InjectorEager(as_injector_key(key))))
|
813
|
+
|
814
|
+
|
717
815
|
##
|
718
816
|
|
719
817
|
|
@@ -768,6 +866,8 @@ class Injection:
|
|
768
866
|
to_key: ta.Any = None,
|
769
867
|
|
770
868
|
singleton: bool = False,
|
869
|
+
|
870
|
+
eager: bool = False,
|
771
871
|
) -> InjectorBindingOrBindings:
|
772
872
|
return InjectorBinder.bind(
|
773
873
|
obj,
|
@@ -782,6 +882,8 @@ class Injection:
|
|
782
882
|
to_key=to_key,
|
783
883
|
|
784
884
|
singleton=singleton,
|
885
|
+
|
886
|
+
eager=eager,
|
785
887
|
)
|
786
888
|
|
787
889
|
# helpers
|
@@ -795,6 +897,15 @@ class Injection:
|
|
795
897
|
) -> InjectorBindingOrBindings:
|
796
898
|
return cls.bind(make_injector_factory(fn, cls_, ann))
|
797
899
|
|
900
|
+
@classmethod
|
901
|
+
def bind_array(
|
902
|
+
cls,
|
903
|
+
obj: ta.Any = None,
|
904
|
+
*,
|
905
|
+
tag: ta.Any = None,
|
906
|
+
) -> InjectorBindingOrBindings:
|
907
|
+
return bind_injector_array(obj, tag=tag)
|
908
|
+
|
798
909
|
@classmethod
|
799
910
|
def bind_array_type(
|
800
911
|
cls,
|
omlish/lite/io.py
CHANGED
@@ -3,6 +3,7 @@ import io
|
|
3
3
|
import typing as ta
|
4
4
|
|
5
5
|
from .check import check_isinstance
|
6
|
+
from .check import check_non_empty
|
6
7
|
from .check import check_not_none
|
7
8
|
from .strings import attr_repr
|
8
9
|
|
@@ -126,3 +127,100 @@ class DelimitingBuffer:
|
|
126
127
|
p = i + remaining_buf_capacity
|
127
128
|
yield self.Incomplete(self._append_and_reset(data[i:p]))
|
128
129
|
i = p
|
130
|
+
|
131
|
+
|
132
|
+
class ReadableListBuffer:
|
133
|
+
def __init__(self) -> None:
|
134
|
+
super().__init__()
|
135
|
+
self._lst: list[bytes] = []
|
136
|
+
|
137
|
+
def feed(self, d: bytes) -> None:
|
138
|
+
if d:
|
139
|
+
self._lst.append(d)
|
140
|
+
|
141
|
+
def _chop(self, i: int, e: int) -> bytes:
|
142
|
+
lst = self._lst
|
143
|
+
d = lst[i]
|
144
|
+
|
145
|
+
o = b''.join([
|
146
|
+
*lst[:i],
|
147
|
+
d[:e],
|
148
|
+
])
|
149
|
+
|
150
|
+
self._lst = [
|
151
|
+
*([d[e:]] if e < len(d) else []),
|
152
|
+
*lst[i + 1:],
|
153
|
+
]
|
154
|
+
|
155
|
+
return o
|
156
|
+
|
157
|
+
def read(self, n: ta.Optional[int] = None) -> ta.Optional[bytes]:
|
158
|
+
if n is None:
|
159
|
+
o = b''.join(self._lst)
|
160
|
+
self._lst = []
|
161
|
+
return o
|
162
|
+
|
163
|
+
if not (lst := self._lst):
|
164
|
+
return None
|
165
|
+
|
166
|
+
c = 0
|
167
|
+
for i, d in enumerate(lst):
|
168
|
+
r = n - c
|
169
|
+
if (l := len(d)) >= r:
|
170
|
+
return self._chop(i, r)
|
171
|
+
c += l
|
172
|
+
|
173
|
+
return None
|
174
|
+
|
175
|
+
def read_until(self, delim: bytes = b'\n') -> ta.Optional[bytes]:
|
176
|
+
if not (lst := self._lst):
|
177
|
+
return None
|
178
|
+
|
179
|
+
for i, d in enumerate(lst):
|
180
|
+
if (p := d.find(delim)) >= 0:
|
181
|
+
return self._chop(i, p + len(delim))
|
182
|
+
|
183
|
+
return None
|
184
|
+
|
185
|
+
|
186
|
+
class IncrementalWriteBuffer:
|
187
|
+
def __init__(
|
188
|
+
self,
|
189
|
+
data: bytes,
|
190
|
+
*,
|
191
|
+
write_size: int = 0x10000,
|
192
|
+
) -> None:
|
193
|
+
super().__init__()
|
194
|
+
|
195
|
+
check_non_empty(data)
|
196
|
+
self._len = len(data)
|
197
|
+
self._write_size = write_size
|
198
|
+
|
199
|
+
self._lst = [
|
200
|
+
data[i:i + write_size]
|
201
|
+
for i in range(0, len(data), write_size)
|
202
|
+
]
|
203
|
+
self._pos = 0
|
204
|
+
|
205
|
+
@property
|
206
|
+
def rem(self) -> int:
|
207
|
+
return self._len - self._pos
|
208
|
+
|
209
|
+
def write(self, fn: ta.Callable[[bytes], int]) -> int:
|
210
|
+
lst = check_non_empty(self._lst)
|
211
|
+
|
212
|
+
t = 0
|
213
|
+
for i, d in enumerate(lst): # noqa
|
214
|
+
n = fn(check_non_empty(d))
|
215
|
+
if not n:
|
216
|
+
break
|
217
|
+
t += n
|
218
|
+
|
219
|
+
if t:
|
220
|
+
self._lst = [
|
221
|
+
*([d[n:]] if n < len(d) else []),
|
222
|
+
*lst[i + 1:],
|
223
|
+
]
|
224
|
+
self._pos += t
|
225
|
+
|
226
|
+
return t
|
@@ -1,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=CxGnj-UiRPlZgmgWoovDWrOnqpSEmBy_kqA7cdfSA3w,1431
|
2
|
-
omlish/__about__.py,sha256=
|
2
|
+
omlish/__about__.py,sha256=PvbOOiIxO1ksGKnYclis2IJG4h8EtRZ-Qy1gtTkvgI4,3379
|
3
3
|
omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
|
4
4
|
omlish/argparse.py,sha256=cqKGAqcxuxv_s62z0gq29L9KAvg_3-_rFvXKjVpRJjo,8126
|
5
5
|
omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
|
@@ -300,11 +300,11 @@ omlish/lifecycles/states.py,sha256=zqMOU2ZU-MDNnWuwauM3_anIAiXM8LoBDElDEraptFg,1
|
|
300
300
|
omlish/lifecycles/transitions.py,sha256=qQtFby-h4VzbvgaUqT2NnbNumlcOx9FVVADP9t83xj4,1939
|
301
301
|
omlish/lite/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
302
302
|
omlish/lite/cached.py,sha256=Fs-ljXVJmHBjAaHc-JuJXMEV4MNSX5c_KHZIM3AEaIw,694
|
303
|
-
omlish/lite/check.py,sha256=
|
303
|
+
omlish/lite/check.py,sha256=pQC412ffe_Zh7eHa4C1UYn6fA71Ls1vpVM0ZIOroPAY,1765
|
304
304
|
omlish/lite/contextmanagers.py,sha256=_jfNdpYvxkbKwyjQLbK-o69W89GoEuUfl_NrCosE9lU,1308
|
305
305
|
omlish/lite/docker.py,sha256=3IVZZtIm7-UdB2SwArmN_MosTva1_KifyYp3YWjODbE,337
|
306
|
-
omlish/lite/inject.py,sha256=
|
307
|
-
omlish/lite/io.py,sha256=
|
306
|
+
omlish/lite/inject.py,sha256=aRRmFb6azTKF208ogYwVCEopNZx7496Ta1GZmL_IKBA,23716
|
307
|
+
omlish/lite/io.py,sha256=3ECgUXdRnXyS6pGTSoVr6oB4moI38EpWxTq08zaTM-U,5339
|
308
308
|
omlish/lite/journald.py,sha256=f5Y2Q6-6O3iK_7MoGiwZwoQEOcP7LfkxxQNUR9tMjJM,3882
|
309
309
|
omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
|
310
310
|
omlish/lite/logs.py,sha256=1pcGu0ekhVCcLUckLSP16VccnAoprjtl5Vkdfm7y1Wg,6184
|
@@ -319,6 +319,12 @@ omlish/lite/socketserver.py,sha256=Esy9dAo9dPnNavNx5hW52YZi5hv504a8XQUudMrPs2A,1
|
|
319
319
|
omlish/lite/strings.py,sha256=QURcE4-1pKVW8eT_5VCJpXaHDWR2dW2pYOChTJnZDiQ,1504
|
320
320
|
omlish/lite/subprocesses.py,sha256=_YwUpvfaC2pV5TMC9-Ivuw1Ao-YxteD3a1NQwGERft4,3380
|
321
321
|
omlish/lite/typing.py,sha256=U3-JaEnkDSYxK4tsu_MzUn3RP6qALBe5FXQXpD-licE,1090
|
322
|
+
omlish/lite/fdio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
323
|
+
omlish/lite/fdio/corohttp.py,sha256=5kzuM1cssA2D7rfknHFxylpjBNfA5tQSPkIRY2Ilapo,3825
|
324
|
+
omlish/lite/fdio/handlers.py,sha256=ukUiwF8-UCr4mzTTfOaTipC0k3k7THiHnohVdYfH69o,1341
|
325
|
+
omlish/lite/fdio/kqueue.py,sha256=4SvSMwNdkXpQbkMhyLqiZIjFGPUGSgD22yr9mpxT2fk,3153
|
326
|
+
omlish/lite/fdio/manager.py,sha256=-gMVzk4B1YTZS-d2TdM12woUme37pcNVUxNTiLe91lA,1250
|
327
|
+
omlish/lite/fdio/pollers.py,sha256=zSFi19SGQ2WKtMO_ONiqaJ_mbQmhrK59zgH5CqMYcu8,5434
|
322
328
|
omlish/lite/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
323
329
|
omlish/lite/http/coroserver.py,sha256=aBaYjP80yQHQxPxwi7PTYHub-fdRDKsMnB-tM8lBc2o,18095
|
324
330
|
omlish/lite/http/handlers.py,sha256=Yu0P3nqz-frklwCM2PbiWvoJNE-NqeTFLBvpNpqcdtA,753
|
@@ -482,9 +488,9 @@ omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,329
|
|
482
488
|
omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
|
483
489
|
omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
|
484
490
|
omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
|
485
|
-
omlish-0.0.0.
|
486
|
-
omlish-0.0.0.
|
487
|
-
omlish-0.0.0.
|
488
|
-
omlish-0.0.0.
|
489
|
-
omlish-0.0.0.
|
490
|
-
omlish-0.0.0.
|
491
|
+
omlish-0.0.0.dev130.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
492
|
+
omlish-0.0.0.dev130.dist-info/METADATA,sha256=1UOC8pu3kIMCWi1vb1n83h4VZjHWkmZpcmwpEzxERlk,4173
|
493
|
+
omlish-0.0.0.dev130.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
494
|
+
omlish-0.0.0.dev130.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
495
|
+
omlish-0.0.0.dev130.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
496
|
+
omlish-0.0.0.dev130.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|