omlish 0.0.0.dev129__py3-none-any.whl → 0.0.0.dev130__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/lite/check.py +13 -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 +66 -8
- omlish/lite/io.py +98 -0
- {omlish-0.0.0.dev129.dist-info → omlish-0.0.0.dev130.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev129.dist-info → omlish-0.0.0.dev130.dist-info}/RECORD +16 -10
- {omlish-0.0.0.dev129.dist-info → omlish-0.0.0.dev130.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev129.dist-info → omlish-0.0.0.dev130.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev129.dist-info → omlish-0.0.0.dev130.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev129.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:
|
@@ -84,3 +85,15 @@ def check_not_in(v: T, c: ta.Container[T]) -> T:
|
|
84
85
|
def check_single(vs: ta.Iterable[T]) -> T:
|
85
86
|
[v] = vs
|
86
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,9 +1,4 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
-
"""
|
3
|
-
TODO:
|
4
|
-
- recursion detection
|
5
|
-
- bind empty array
|
6
|
-
"""
|
7
2
|
import abc
|
8
3
|
import contextlib
|
9
4
|
import dataclasses as dc
|
@@ -325,7 +320,11 @@ def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey,
|
|
325
320
|
|
326
321
|
for b in bs.bindings():
|
327
322
|
if b.key.array:
|
328
|
-
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)
|
329
328
|
else:
|
330
329
|
if b.key in pm:
|
331
330
|
raise KeyError(b.key)
|
@@ -473,6 +472,14 @@ def build_injection_kwargs_target(
|
|
473
472
|
_INJECTOR_INJECTOR_KEY: InjectorKey[Injector] = InjectorKey(Injector)
|
474
473
|
|
475
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
|
+
|
476
483
|
class _Injector(Injector):
|
477
484
|
def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
|
478
485
|
super().__init__()
|
@@ -487,6 +494,10 @@ class _Injector(Injector):
|
|
487
494
|
|
488
495
|
self.__cur_req: ta.Optional[_Injector._Request] = None
|
489
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
|
+
|
490
501
|
class _Request:
|
491
502
|
def __init__(self, injector: '_Injector') -> None:
|
492
503
|
super().__init__()
|
@@ -644,6 +655,8 @@ class InjectorBinder:
|
|
644
655
|
to_key: ta.Any = None,
|
645
656
|
|
646
657
|
singleton: bool = False,
|
658
|
+
|
659
|
+
eager: bool = False,
|
647
660
|
) -> InjectorBindingOrBindings:
|
648
661
|
if obj is None or obj is inspect.Parameter.empty:
|
649
662
|
raise TypeError(obj)
|
@@ -717,13 +730,21 @@ class InjectorBinder:
|
|
717
730
|
if singleton:
|
718
731
|
provider = SingletonInjectorProvider(provider)
|
719
732
|
|
733
|
+
binding = InjectorBinding(key, provider)
|
734
|
+
|
720
735
|
##
|
721
736
|
|
722
|
-
|
737
|
+
extras: ta.List[InjectorBinding] = []
|
738
|
+
|
739
|
+
if eager:
|
740
|
+
extras.append(bind_injector_eager_key(key))
|
723
741
|
|
724
742
|
##
|
725
743
|
|
726
|
-
|
744
|
+
if extras:
|
745
|
+
return as_injector_bindings(binding, *extras)
|
746
|
+
else:
|
747
|
+
return binding
|
727
748
|
|
728
749
|
|
729
750
|
###
|
@@ -746,6 +767,26 @@ def make_injector_factory(
|
|
746
767
|
return outer
|
747
768
|
|
748
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
|
+
|
749
790
|
def make_injector_array_type(
|
750
791
|
ele: ta.Union[InjectorKey, InjectorKeyCls],
|
751
792
|
cls: U,
|
@@ -767,6 +808,10 @@ def make_injector_array_type(
|
|
767
808
|
return inner
|
768
809
|
|
769
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
|
+
|
770
815
|
##
|
771
816
|
|
772
817
|
|
@@ -821,6 +866,8 @@ class Injection:
|
|
821
866
|
to_key: ta.Any = None,
|
822
867
|
|
823
868
|
singleton: bool = False,
|
869
|
+
|
870
|
+
eager: bool = False,
|
824
871
|
) -> InjectorBindingOrBindings:
|
825
872
|
return InjectorBinder.bind(
|
826
873
|
obj,
|
@@ -835,6 +882,8 @@ class Injection:
|
|
835
882
|
to_key=to_key,
|
836
883
|
|
837
884
|
singleton=singleton,
|
885
|
+
|
886
|
+
eager=eager,
|
838
887
|
)
|
839
888
|
|
840
889
|
# helpers
|
@@ -848,6 +897,15 @@ class Injection:
|
|
848
897
|
) -> InjectorBindingOrBindings:
|
849
898
|
return cls.bind(make_injector_factory(fn, cls_, ann))
|
850
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
|
+
|
851
909
|
@classmethod
|
852
910
|
def bind_array_type(
|
853
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
|