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/inject/__init__.py CHANGED
@@ -106,7 +106,7 @@ from .scopes import ( # noqa
106
106
  ScopeSeededProvider,
107
107
  SeededScope,
108
108
  Singleton,
109
- Thread,
109
+ ThreadScope,
110
110
  bind_scope,
111
111
  bind_scope_seed,
112
112
  enter_seeded_scope,
@@ -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 Thread
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
- Thread(),
51
+ ThreadScope(),
52
52
  ]
53
53
 
54
54
 
@@ -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 Thread
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) -> Thread:
90
- return Thread()
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
- Thread: lambda _: ThreadScopeImpl(),
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 Thread(Scope, lang.Singleton, lang.Final):
52
+ class ThreadScope(Scope, lang.Singleton, lang.Final):
53
53
  pass
54
54
 
55
55
 
56
- SCOPE_ALIASES['thread'] = 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