omlish 0.0.0.dev129__py3-none-any.whl → 0.0.0.dev131__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omlish/__about__.py +3 -3
- omlish/formats/dotenv.py +166 -152
- omlish/inject/__init__.py +1 -1
- omlish/inject/impl/injector.py +2 -2
- omlish/inject/impl/scopes.py +4 -4
- omlish/inject/scopes.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.dev131.dist-info}/METADATA +3 -3
- {omlish-0.0.0.dev129.dist-info → omlish-0.0.0.dev131.dist-info}/RECORD +21 -15
- {omlish-0.0.0.dev129.dist-info → omlish-0.0.0.dev131.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev129.dist-info → omlish-0.0.0.dev131.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev129.dist-info → omlish-0.0.0.dev131.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev129.dist-info → omlish-0.0.0.dev131.dist-info}/top_level.txt +0 -0
omlish/inject/__init__.py
CHANGED
omlish/inject/impl/injector.py
CHANGED
@@ -33,7 +33,7 @@ from ..listeners import ProvisionListener
|
|
33
33
|
from ..listeners import ProvisionListenerBinding
|
34
34
|
from ..scopes import ScopeBinding
|
35
35
|
from ..scopes import Singleton
|
36
|
-
from ..scopes import
|
36
|
+
from ..scopes import ThreadScope
|
37
37
|
from ..types import Scope
|
38
38
|
from ..types import Unscoped
|
39
39
|
from .elements import ElementCollection
|
@@ -48,7 +48,7 @@ log = logging.getLogger(__name__)
|
|
48
48
|
DEFAULT_SCOPES: list[Scope] = [
|
49
49
|
Unscoped(),
|
50
50
|
Singleton(),
|
51
|
-
|
51
|
+
ThreadScope(),
|
52
52
|
]
|
53
53
|
|
54
54
|
|
omlish/inject/impl/scopes.py
CHANGED
@@ -24,7 +24,7 @@ from ..providers import Provider
|
|
24
24
|
from ..scopes import ScopeSeededProvider
|
25
25
|
from ..scopes import SeededScope
|
26
26
|
from ..scopes import Singleton
|
27
|
-
from ..scopes import
|
27
|
+
from ..scopes import ThreadScope
|
28
28
|
from ..types import Scope
|
29
29
|
from ..types import Unscoped
|
30
30
|
from .bindings import BindingImpl
|
@@ -86,8 +86,8 @@ class ThreadScopeImpl(ScopeImpl, lang.Final):
|
|
86
86
|
self._local = threading.local()
|
87
87
|
|
88
88
|
@property
|
89
|
-
def scope(self) ->
|
90
|
-
return
|
89
|
+
def scope(self) -> ThreadScope:
|
90
|
+
return ThreadScope()
|
91
91
|
|
92
92
|
def provide(self, binding: BindingImpl, injector: Injector) -> ta.Any:
|
93
93
|
dct: dict[BindingImpl, ta.Any]
|
@@ -190,7 +190,7 @@ class SeededScopeImpl(ScopeImpl):
|
|
190
190
|
SCOPE_IMPLS_BY_SCOPE: dict[type[Scope], ta.Callable[..., ScopeImpl]] = {
|
191
191
|
Unscoped: lambda _: UnscopedScopeImpl(),
|
192
192
|
Singleton: lambda _: SingletonScopeImpl(),
|
193
|
-
|
193
|
+
ThreadScope: lambda _: ThreadScopeImpl(),
|
194
194
|
SeededScope: lambda s: SeededScopeImpl(s),
|
195
195
|
}
|
196
196
|
|
omlish/inject/scopes.py
CHANGED
@@ -49,11 +49,11 @@ SCOPE_ALIASES['singleton'] = Singleton()
|
|
49
49
|
##
|
50
50
|
|
51
51
|
|
52
|
-
class
|
52
|
+
class ThreadScope(Scope, lang.Singleton, lang.Final):
|
53
53
|
pass
|
54
54
|
|
55
55
|
|
56
|
-
SCOPE_ALIASES['thread'] =
|
56
|
+
SCOPE_ALIASES['thread'] = ThreadScope()
|
57
57
|
|
58
58
|
|
59
59
|
##
|
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
|