omlish 0.0.0.dev217__py3-none-any.whl → 0.0.0.dev219__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/docker/oci/__init__.py +0 -0
- omlish/docker/oci/data.py +71 -0
- omlish/docker/oci/media.py +124 -0
- omlish/formats/json5/Json5.g4 +0 -3
- omlish/http/coro/server.py +45 -25
- omlish/http/handlers.py +18 -1
- omlish/http/sessions.py +1 -1
- omlish/http/simple.py +101 -0
- omlish/iterators/tools.py +1 -0
- omlish/logs/all.py +13 -0
- omlish/logs/callers.py +45 -0
- omlish/logs/protocol.py +176 -0
- omlish/secrets/__init__.py +0 -24
- omlish/secrets/all.py +16 -0
- omlish/secrets/secrets.py +6 -0
- omlish/sockets/addresses.py +33 -12
- omlish/sockets/bind.py +333 -0
- omlish/sockets/handlers.py +4 -15
- omlish/sockets/io.py +69 -0
- omlish/sockets/server/__init__.py +0 -0
- omlish/sockets/server/handlers.py +134 -0
- omlish/sockets/server/server.py +152 -0
- omlish/sockets/server/threading.py +123 -0
- omlish/sql/alchemy/secrets.py +1 -1
- omlish/sql/dbs.py +1 -1
- {omlish-0.0.0.dev217.dist-info → omlish-0.0.0.dev219.dist-info}/METADATA +3 -3
- {omlish-0.0.0.dev217.dist-info → omlish-0.0.0.dev219.dist-info}/RECORD +32 -21
- omlish/docker/oci.py +0 -81
- omlish/sockets/server.py +0 -66
- {omlish-0.0.0.dev217.dist-info → omlish-0.0.0.dev219.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev217.dist-info → omlish-0.0.0.dev219.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev217.dist-info → omlish-0.0.0.dev219.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev217.dist-info → omlish-0.0.0.dev219.dist-info}/top_level.txt +0 -0
omlish/logs/protocol.py
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import abc
|
4
|
+
import logging
|
5
|
+
import sys
|
6
|
+
import typing as ta
|
7
|
+
|
8
|
+
from .callers import LoggingCaller
|
9
|
+
|
10
|
+
|
11
|
+
LogLevel = int # ta.TypeAlias
|
12
|
+
|
13
|
+
|
14
|
+
##
|
15
|
+
|
16
|
+
|
17
|
+
class Logging(ta.Protocol):
|
18
|
+
def isEnabledFor(self, level: LogLevel) -> bool: # noqa
|
19
|
+
...
|
20
|
+
|
21
|
+
def getEffectiveLevel(self) -> LogLevel: # noqa
|
22
|
+
...
|
23
|
+
|
24
|
+
#
|
25
|
+
|
26
|
+
def debug(self, msg: str, *args, **kwargs) -> None:
|
27
|
+
...
|
28
|
+
|
29
|
+
def info(self, msg: str, *args, **kwargs) -> None:
|
30
|
+
...
|
31
|
+
|
32
|
+
def warning(self, msg: str, *args, **kwargs) -> None:
|
33
|
+
...
|
34
|
+
|
35
|
+
def error(self, msg: str, *args, **kwargs) -> None:
|
36
|
+
...
|
37
|
+
|
38
|
+
def exception(self, msg: str, *args, exc_info=True, **kwargs) -> None:
|
39
|
+
...
|
40
|
+
|
41
|
+
def critical(self, msg: str, *args, **kwargs) -> None:
|
42
|
+
...
|
43
|
+
|
44
|
+
def log(self, level: LogLevel, msg: str, *args, **kwargs) -> None:
|
45
|
+
...
|
46
|
+
|
47
|
+
|
48
|
+
##
|
49
|
+
|
50
|
+
|
51
|
+
class AbstractLogging(abc.ABC):
|
52
|
+
@ta.final
|
53
|
+
def isEnabledFor(self, level: LogLevel) -> bool: # noqa
|
54
|
+
return self.is_enabled_for(level)
|
55
|
+
|
56
|
+
def is_enabled_for(self, level: LogLevel) -> bool: # noqa
|
57
|
+
return level >= self.getEffectiveLevel()
|
58
|
+
|
59
|
+
@ta.final
|
60
|
+
def getEffectiveLevel(self) -> LogLevel: # noqa
|
61
|
+
return self.get_effective_level()
|
62
|
+
|
63
|
+
@abc.abstractmethod
|
64
|
+
def get_effective_level(self) -> LogLevel: # noqa
|
65
|
+
raise NotImplementedError
|
66
|
+
|
67
|
+
#
|
68
|
+
|
69
|
+
def debug(self, msg: str, *args, **kwargs) -> None:
|
70
|
+
if self.is_enabled_for(logging.DEBUG):
|
71
|
+
self.log(logging.DEBUG, msg, args, **kwargs)
|
72
|
+
|
73
|
+
def info(self, msg: str, *args, **kwargs) -> None:
|
74
|
+
if self.is_enabled_for(logging.INFO):
|
75
|
+
self.log(logging.INFO, msg, args, **kwargs)
|
76
|
+
|
77
|
+
def warning(self, msg: str, *args, **kwargs) -> None:
|
78
|
+
if self.is_enabled_for(logging.WARNING):
|
79
|
+
self.log(logging.WARNING, msg, args, **kwargs)
|
80
|
+
|
81
|
+
def error(self, msg: str, *args, **kwargs) -> None:
|
82
|
+
if self.is_enabled_for(logging.ERROR):
|
83
|
+
self.log(logging.ERROR, msg, args, **kwargs)
|
84
|
+
|
85
|
+
def exception(self, msg: str, *args, exc_info=True, **kwargs) -> None:
|
86
|
+
self.error(msg, *args, exc_info=exc_info, **kwargs)
|
87
|
+
|
88
|
+
def critical(self, msg: str, *args, **kwargs) -> None:
|
89
|
+
if self.is_enabled_for(logging.CRITICAL):
|
90
|
+
self.log(logging.CRITICAL, msg, args, **kwargs)
|
91
|
+
|
92
|
+
def log(self, level: LogLevel, msg: str, *args, **kwargs) -> None:
|
93
|
+
if not isinstance(level, int):
|
94
|
+
raise TypeError('Level must be an integer.')
|
95
|
+
if self.is_enabled_for(level):
|
96
|
+
self._log(level, msg, args, **kwargs)
|
97
|
+
|
98
|
+
@abc.abstractmethod
|
99
|
+
def _log(
|
100
|
+
self,
|
101
|
+
level: int,
|
102
|
+
msg: str,
|
103
|
+
args: ta.Any,
|
104
|
+
*,
|
105
|
+
exc_info: ta.Any = None,
|
106
|
+
extra: ta.Any = None,
|
107
|
+
stack_info: bool = False,
|
108
|
+
) -> None:
|
109
|
+
raise NotImplementedError
|
110
|
+
|
111
|
+
|
112
|
+
##
|
113
|
+
|
114
|
+
|
115
|
+
class NopLogging(AbstractLogging):
|
116
|
+
def get_effective_level(self) -> LogLevel:
|
117
|
+
return logging.CRITICAL + 1
|
118
|
+
|
119
|
+
def _log(self, *args: ta.Any, **kwargs: ta.Any) -> None:
|
120
|
+
pass
|
121
|
+
|
122
|
+
|
123
|
+
##
|
124
|
+
|
125
|
+
|
126
|
+
class StdlibLogging(AbstractLogging):
|
127
|
+
def __init__(self, underlying: logging.Logger) -> None:
|
128
|
+
super().__init__()
|
129
|
+
|
130
|
+
if not isinstance(underlying, logging.Logger):
|
131
|
+
raise TypeError(underlying)
|
132
|
+
|
133
|
+
self._underlying = underlying
|
134
|
+
|
135
|
+
#
|
136
|
+
|
137
|
+
def is_enabled_for(self, level: int) -> bool: # noqa
|
138
|
+
return self._underlying.isEnabledFor(level)
|
139
|
+
|
140
|
+
def get_effective_level(self) -> int: # noqa
|
141
|
+
return self._underlying.getEffectiveLevel()
|
142
|
+
|
143
|
+
#
|
144
|
+
|
145
|
+
def _log(
|
146
|
+
self,
|
147
|
+
level: int,
|
148
|
+
msg: str,
|
149
|
+
args: ta.Any,
|
150
|
+
*,
|
151
|
+
exc_info: ta.Any = None,
|
152
|
+
extra: ta.Any = None,
|
153
|
+
stack_info: bool = False,
|
154
|
+
) -> None:
|
155
|
+
caller = LoggingCaller.find(stack_info)
|
156
|
+
|
157
|
+
if exc_info:
|
158
|
+
if isinstance(exc_info, BaseException):
|
159
|
+
exc_info = (type(exc_info), exc_info, exc_info.__traceback__)
|
160
|
+
elif not isinstance(exc_info, tuple):
|
161
|
+
exc_info = sys.exc_info()
|
162
|
+
|
163
|
+
record = self._underlying.makeRecord(
|
164
|
+
name=self._underlying.name,
|
165
|
+
level=level,
|
166
|
+
fn=caller.filename,
|
167
|
+
lno=caller.lineno,
|
168
|
+
msg=msg,
|
169
|
+
args=args,
|
170
|
+
exc_info=exc_info,
|
171
|
+
func=caller.func,
|
172
|
+
extra=extra,
|
173
|
+
sinfo=caller.sinfo,
|
174
|
+
)
|
175
|
+
|
176
|
+
self._underlying.handle(record)
|
omlish/secrets/__init__.py
CHANGED
@@ -1,24 +0,0 @@
|
|
1
|
-
from .secrets import ( # noqa
|
2
|
-
CachingSecrets,
|
3
|
-
CompositeSecrets,
|
4
|
-
EMPTY_SECRETS,
|
5
|
-
EmptySecrets,
|
6
|
-
EnvVarSecrets,
|
7
|
-
FnSecrets,
|
8
|
-
LoggingSecrets,
|
9
|
-
MappingSecrets,
|
10
|
-
Secret,
|
11
|
-
SecretRef,
|
12
|
-
SecretRefOrStr,
|
13
|
-
Secrets,
|
14
|
-
secret_field,
|
15
|
-
secret_repr,
|
16
|
-
)
|
17
|
-
|
18
|
-
|
19
|
-
##
|
20
|
-
|
21
|
-
|
22
|
-
from ..lang.imports import _register_conditional_import # noqa
|
23
|
-
|
24
|
-
_register_conditional_import('..marshal', '.marshal', __package__)
|
omlish/secrets/all.py
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
from .secrets import ( # noqa
|
2
|
+
CachingSecrets,
|
3
|
+
CompositeSecrets,
|
4
|
+
EMPTY_SECRETS,
|
5
|
+
EmptySecrets,
|
6
|
+
EnvVarSecrets,
|
7
|
+
FnSecrets,
|
8
|
+
LoggingSecrets,
|
9
|
+
MappingSecrets,
|
10
|
+
Secret,
|
11
|
+
SecretRef,
|
12
|
+
SecretRefOrStr,
|
13
|
+
Secrets,
|
14
|
+
secret_field,
|
15
|
+
secret_repr,
|
16
|
+
)
|
omlish/secrets/secrets.py
CHANGED
omlish/sockets/addresses.py
CHANGED
@@ -2,11 +2,10 @@
|
|
2
2
|
# @omlish-lite
|
3
3
|
"""
|
4
4
|
TODO:
|
5
|
-
-
|
6
|
-
+ codification of https://docs.python.org/3/library/socket.html#socket-families
|
5
|
+
- codification of https://docs.python.org/3/library/socket.html#socket-families
|
7
6
|
"""
|
8
7
|
import dataclasses as dc
|
9
|
-
import socket
|
8
|
+
import socket as socket_
|
10
9
|
import typing as ta
|
11
10
|
|
12
11
|
|
@@ -20,34 +19,56 @@ SocketAddress = ta.Any
|
|
20
19
|
class SocketAddressInfoArgs:
|
21
20
|
host: ta.Optional[str]
|
22
21
|
port: ta.Union[str, int, None]
|
23
|
-
family:
|
22
|
+
family: socket_.AddressFamily = socket_.AddressFamily.AF_UNSPEC
|
24
23
|
type: int = 0
|
25
24
|
proto: int = 0
|
26
|
-
flags:
|
25
|
+
flags: socket_.AddressInfo = socket_.AddressInfo(0)
|
27
26
|
|
28
27
|
|
29
28
|
@dc.dataclass(frozen=True)
|
30
29
|
class SocketAddressInfo:
|
31
|
-
family:
|
30
|
+
family: socket_.AddressFamily
|
32
31
|
type: int
|
33
32
|
proto: int
|
34
33
|
canonname: ta.Optional[str]
|
35
34
|
sockaddr: SocketAddress
|
36
35
|
|
37
36
|
|
37
|
+
class SocketFamilyAndAddress(ta.NamedTuple):
|
38
|
+
family: socket_.AddressFamily
|
39
|
+
address: SocketAddress
|
40
|
+
|
41
|
+
|
38
42
|
def get_best_socket_family(
|
39
43
|
host: ta.Optional[str],
|
40
44
|
port: ta.Union[str, int, None],
|
41
|
-
family: ta.Union[int,
|
42
|
-
) ->
|
45
|
+
family: ta.Union[int, socket_.AddressFamily] = socket_.AddressFamily.AF_UNSPEC,
|
46
|
+
) -> SocketFamilyAndAddress:
|
43
47
|
"""https://github.com/python/cpython/commit/f289084c83190cc72db4a70c58f007ec62e75247"""
|
44
48
|
|
45
|
-
infos =
|
49
|
+
infos = socket_.getaddrinfo(
|
46
50
|
host,
|
47
51
|
port,
|
48
52
|
family,
|
49
|
-
type=
|
50
|
-
flags=
|
53
|
+
type=socket_.SOCK_STREAM,
|
54
|
+
flags=socket_.AI_PASSIVE,
|
51
55
|
)
|
52
56
|
ai = SocketAddressInfo(*next(iter(infos)))
|
53
|
-
return ai.family, ai.sockaddr
|
57
|
+
return SocketFamilyAndAddress(ai.family, ai.sockaddr)
|
58
|
+
|
59
|
+
|
60
|
+
class SocketAndAddress(ta.NamedTuple):
|
61
|
+
socket: socket_.socket
|
62
|
+
address: SocketAddress
|
63
|
+
|
64
|
+
def wrap_socket(self, fn: ta.Callable[[socket_.socket], socket_.socket]):
|
65
|
+
return self._replace(socket=fn(self.socket))
|
66
|
+
|
67
|
+
@classmethod
|
68
|
+
def socket_wrapper(
|
69
|
+
cls,
|
70
|
+
fn: ta.Callable[[socket_.socket], socket_.socket],
|
71
|
+
) -> ta.Callable[['SocketAndAddress'], 'SocketAndAddress']:
|
72
|
+
def inner(conn):
|
73
|
+
return conn.wrap_socket(fn)
|
74
|
+
return inner
|
omlish/sockets/bind.py
ADDED
@@ -0,0 +1,333 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
"""
|
4
|
+
TODO:
|
5
|
+
- DupSocketBinder
|
6
|
+
"""
|
7
|
+
import abc
|
8
|
+
import dataclasses as dc
|
9
|
+
import errno
|
10
|
+
import os
|
11
|
+
import socket as socket_
|
12
|
+
import stat
|
13
|
+
import typing as ta
|
14
|
+
|
15
|
+
from omlish.lite.check import check
|
16
|
+
from omlish.lite.dataclasses import dataclass_maybe_post_init
|
17
|
+
from omlish.sockets.addresses import SocketAddress
|
18
|
+
from omlish.sockets.addresses import SocketAndAddress
|
19
|
+
|
20
|
+
|
21
|
+
SocketBinderT = ta.TypeVar('SocketBinderT', bound='SocketBinder')
|
22
|
+
SocketBinderConfigT = ta.TypeVar('SocketBinderConfigT', bound='SocketBinder.Config')
|
23
|
+
CanSocketBinderConfig = ta.Union['SocketBinder.Config', int, ta.Tuple[str, int], str] # ta.TypeAlias
|
24
|
+
CanSocketBinder = ta.Union['SocketBinder', CanSocketBinderConfig] # ta.TypeAlias
|
25
|
+
|
26
|
+
|
27
|
+
##
|
28
|
+
|
29
|
+
|
30
|
+
class SocketBinder(abc.ABC, ta.Generic[SocketBinderConfigT]):
|
31
|
+
@dc.dataclass(frozen=True)
|
32
|
+
class Config:
|
33
|
+
listen_backlog: int = 5
|
34
|
+
|
35
|
+
allow_reuse_address: bool = True
|
36
|
+
allow_reuse_port: bool = True
|
37
|
+
|
38
|
+
set_inheritable: bool = False
|
39
|
+
|
40
|
+
#
|
41
|
+
|
42
|
+
@classmethod
|
43
|
+
def of(cls, obj: CanSocketBinderConfig) -> 'SocketBinder.Config':
|
44
|
+
if isinstance(obj, SocketBinder.Config):
|
45
|
+
return obj
|
46
|
+
|
47
|
+
elif isinstance(obj, int):
|
48
|
+
return TcpSocketBinder.Config(
|
49
|
+
port=obj,
|
50
|
+
)
|
51
|
+
|
52
|
+
elif isinstance(obj, tuple):
|
53
|
+
host, port = obj
|
54
|
+
return TcpSocketBinder.Config(
|
55
|
+
host=host,
|
56
|
+
port=port,
|
57
|
+
)
|
58
|
+
|
59
|
+
elif isinstance(obj, str):
|
60
|
+
return UnixSocketBinder.Config(
|
61
|
+
file=obj,
|
62
|
+
)
|
63
|
+
|
64
|
+
else:
|
65
|
+
raise TypeError(obj)
|
66
|
+
|
67
|
+
#
|
68
|
+
|
69
|
+
def __init__(self, config: SocketBinderConfigT) -> None:
|
70
|
+
super().__init__()
|
71
|
+
|
72
|
+
self._config = config
|
73
|
+
|
74
|
+
#
|
75
|
+
|
76
|
+
@classmethod
|
77
|
+
def of(cls, obj: CanSocketBinder) -> 'SocketBinder':
|
78
|
+
if isinstance(obj, SocketBinder):
|
79
|
+
return obj
|
80
|
+
|
81
|
+
config: SocketBinder.Config
|
82
|
+
if isinstance(obj, SocketBinder.Config):
|
83
|
+
config = obj
|
84
|
+
|
85
|
+
else:
|
86
|
+
config = SocketBinder.Config.of(obj)
|
87
|
+
|
88
|
+
if isinstance(config, TcpSocketBinder.Config):
|
89
|
+
return TcpSocketBinder(config)
|
90
|
+
|
91
|
+
elif isinstance(config, UnixSocketBinder.Config):
|
92
|
+
return UnixSocketBinder(config)
|
93
|
+
|
94
|
+
else:
|
95
|
+
raise TypeError(config)
|
96
|
+
|
97
|
+
#
|
98
|
+
|
99
|
+
class Error(RuntimeError):
|
100
|
+
pass
|
101
|
+
|
102
|
+
class NotBoundError(Error):
|
103
|
+
pass
|
104
|
+
|
105
|
+
class AlreadyBoundError(Error):
|
106
|
+
pass
|
107
|
+
|
108
|
+
#
|
109
|
+
|
110
|
+
@property
|
111
|
+
@abc.abstractmethod
|
112
|
+
def address_family(self) -> int:
|
113
|
+
raise NotImplementedError
|
114
|
+
|
115
|
+
@property
|
116
|
+
@abc.abstractmethod
|
117
|
+
def address(self) -> SocketAddress:
|
118
|
+
raise NotImplementedError
|
119
|
+
|
120
|
+
#
|
121
|
+
|
122
|
+
_socket: socket_.socket
|
123
|
+
|
124
|
+
@property
|
125
|
+
def is_bound(self) -> bool:
|
126
|
+
return hasattr(self, '_socket')
|
127
|
+
|
128
|
+
@property
|
129
|
+
def socket(self) -> socket_.socket:
|
130
|
+
try:
|
131
|
+
return self._socket
|
132
|
+
except AttributeError:
|
133
|
+
raise self.NotBoundError from None
|
134
|
+
|
135
|
+
_name: str
|
136
|
+
|
137
|
+
@property
|
138
|
+
def name(self) -> str:
|
139
|
+
try:
|
140
|
+
return self._name
|
141
|
+
except AttributeError:
|
142
|
+
raise self.NotBoundError from None
|
143
|
+
|
144
|
+
_port: ta.Optional[int]
|
145
|
+
|
146
|
+
@property
|
147
|
+
def port(self) -> ta.Optional[int]:
|
148
|
+
try:
|
149
|
+
return self._port
|
150
|
+
except AttributeError:
|
151
|
+
raise self.NotBoundError from None
|
152
|
+
|
153
|
+
#
|
154
|
+
|
155
|
+
def fileno(self) -> int:
|
156
|
+
return self.socket.fileno()
|
157
|
+
|
158
|
+
#
|
159
|
+
|
160
|
+
def __enter__(self: SocketBinderT) -> SocketBinderT:
|
161
|
+
self.bind()
|
162
|
+
|
163
|
+
return self
|
164
|
+
|
165
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
166
|
+
self.close()
|
167
|
+
|
168
|
+
#
|
169
|
+
|
170
|
+
def _init_socket(self) -> None:
|
171
|
+
if hasattr(self, '_socket'):
|
172
|
+
raise self.AlreadyBoundError
|
173
|
+
|
174
|
+
socket = socket_.socket(self.address_family, socket_.SOCK_STREAM)
|
175
|
+
self._socket = socket
|
176
|
+
|
177
|
+
if self._config.allow_reuse_address and hasattr(socket_, 'SO_REUSEADDR'):
|
178
|
+
socket.setsockopt(socket_.SOL_SOCKET, socket_.SO_REUSEADDR, 1)
|
179
|
+
|
180
|
+
# Since Linux 6.12.9, SO_REUSEPORT is not allowed on other address families than AF_INET/AF_INET6.
|
181
|
+
if (
|
182
|
+
self._config.allow_reuse_port and hasattr(socket_, 'SO_REUSEPORT') and
|
183
|
+
self.address_family in (socket_.AF_INET, socket_.AF_INET6)
|
184
|
+
):
|
185
|
+
try:
|
186
|
+
socket.setsockopt(socket_.SOL_SOCKET, socket_.SO_REUSEPORT, 1)
|
187
|
+
except OSError as err:
|
188
|
+
if err.errno not in (errno.ENOPROTOOPT, errno.EINVAL):
|
189
|
+
raise
|
190
|
+
|
191
|
+
if self._config.set_inheritable and hasattr(socket, 'set_inheritable'):
|
192
|
+
socket.set_inheritable(True)
|
193
|
+
|
194
|
+
def _pre_bind(self) -> None:
|
195
|
+
pass
|
196
|
+
|
197
|
+
def _post_bind(self) -> None:
|
198
|
+
pass
|
199
|
+
|
200
|
+
def bind(self) -> None:
|
201
|
+
self._init_socket()
|
202
|
+
|
203
|
+
self._pre_bind()
|
204
|
+
|
205
|
+
self.socket.bind(self.address)
|
206
|
+
|
207
|
+
self._post_bind()
|
208
|
+
|
209
|
+
check.state(all(hasattr(self, a) for a in ('_socket', '_name', '_port')))
|
210
|
+
|
211
|
+
#
|
212
|
+
|
213
|
+
def close(self) -> None:
|
214
|
+
if hasattr(self, '_socket'):
|
215
|
+
self._socket.close()
|
216
|
+
|
217
|
+
#
|
218
|
+
|
219
|
+
def listen(self) -> None:
|
220
|
+
self.socket.listen(self._config.listen_backlog)
|
221
|
+
|
222
|
+
@abc.abstractmethod
|
223
|
+
def accept(self, socket: ta.Optional[socket_.socket] = None) -> SocketAndAddress:
|
224
|
+
raise NotImplementedError
|
225
|
+
|
226
|
+
|
227
|
+
##
|
228
|
+
|
229
|
+
|
230
|
+
class TcpSocketBinder(SocketBinder):
|
231
|
+
@dc.dataclass(frozen=True)
|
232
|
+
class Config(SocketBinder.Config):
|
233
|
+
DEFAULT_HOST: ta.ClassVar[str] = 'localhost'
|
234
|
+
host: str = DEFAULT_HOST
|
235
|
+
|
236
|
+
port: int = 0
|
237
|
+
|
238
|
+
def __post_init__(self) -> None:
|
239
|
+
dataclass_maybe_post_init(super())
|
240
|
+
check.non_empty_str(self.host)
|
241
|
+
check.isinstance(self.port, int)
|
242
|
+
check.arg(self.port > 0)
|
243
|
+
|
244
|
+
def __init__(self, config: Config) -> None:
|
245
|
+
super().__init__(check.isinstance(config, self.Config))
|
246
|
+
|
247
|
+
self._address = (config.host, config.port)
|
248
|
+
|
249
|
+
#
|
250
|
+
|
251
|
+
address_family = socket_.AF_INET
|
252
|
+
|
253
|
+
@property
|
254
|
+
def address(self) -> SocketAddress:
|
255
|
+
return self._address
|
256
|
+
|
257
|
+
#
|
258
|
+
|
259
|
+
def _post_bind(self) -> None:
|
260
|
+
super()._post_bind()
|
261
|
+
|
262
|
+
host, port, *_ = self.socket.getsockname()
|
263
|
+
|
264
|
+
self._name = socket_.getfqdn(host)
|
265
|
+
self._port = port
|
266
|
+
|
267
|
+
#
|
268
|
+
|
269
|
+
def accept(self, socket: ta.Optional[socket_.socket] = None) -> SocketAndAddress:
|
270
|
+
if socket is None:
|
271
|
+
socket = self.socket
|
272
|
+
|
273
|
+
conn, client_address = socket.accept()
|
274
|
+
return SocketAndAddress(conn, client_address)
|
275
|
+
|
276
|
+
|
277
|
+
##
|
278
|
+
|
279
|
+
|
280
|
+
class UnixSocketBinder(SocketBinder):
|
281
|
+
@dc.dataclass(frozen=True)
|
282
|
+
class Config(SocketBinder.Config):
|
283
|
+
file: str = ''
|
284
|
+
|
285
|
+
unlink: bool = False
|
286
|
+
|
287
|
+
def __post_init__(self) -> None:
|
288
|
+
dataclass_maybe_post_init(super())
|
289
|
+
check.non_empty_str(self.file)
|
290
|
+
|
291
|
+
def __init__(self, config: Config) -> None:
|
292
|
+
super().__init__(check.isinstance(config, self.Config))
|
293
|
+
|
294
|
+
self._address = config.file
|
295
|
+
|
296
|
+
#
|
297
|
+
|
298
|
+
address_family = socket_.AF_UNIX
|
299
|
+
|
300
|
+
@property
|
301
|
+
def address(self) -> SocketAddress:
|
302
|
+
return self._address
|
303
|
+
|
304
|
+
#
|
305
|
+
|
306
|
+
def _pre_bind(self) -> None:
|
307
|
+
super()._pre_bind()
|
308
|
+
|
309
|
+
if self._config.unlink:
|
310
|
+
try:
|
311
|
+
os.unlink(self._config.file)
|
312
|
+
except FileNotFoundError:
|
313
|
+
pass
|
314
|
+
|
315
|
+
def _post_bind(self) -> None:
|
316
|
+
super()._post_bind()
|
317
|
+
|
318
|
+
name = self.socket.getsockname()
|
319
|
+
|
320
|
+
os.chmod(name, stat.S_IRWXU | stat.S_IRWXG) # noqa
|
321
|
+
|
322
|
+
self._name = name
|
323
|
+
self._port = None
|
324
|
+
|
325
|
+
#
|
326
|
+
|
327
|
+
def accept(self, sock: ta.Optional[socket_.socket] = None) -> SocketAndAddress:
|
328
|
+
if sock is None:
|
329
|
+
sock = self.socket
|
330
|
+
|
331
|
+
conn, _ = sock.accept()
|
332
|
+
client_address = ('', 0)
|
333
|
+
return SocketAndAddress(conn, client_address)
|
omlish/sockets/handlers.py
CHANGED
@@ -4,27 +4,16 @@ import abc
|
|
4
4
|
import typing as ta
|
5
5
|
|
6
6
|
from .addresses import SocketAddress
|
7
|
+
from .io import SocketIoPair # noqa
|
7
8
|
|
8
9
|
|
9
|
-
|
10
|
+
SocketHandler = ta.Callable[[SocketAddress, 'SocketIoPair'], None] # ta.TypeAlias
|
10
11
|
|
11
12
|
|
12
13
|
##
|
13
14
|
|
14
15
|
|
15
|
-
class
|
16
|
-
def __init__(
|
17
|
-
self,
|
18
|
-
client_address: SocketAddress,
|
19
|
-
rfile: ta.BinaryIO,
|
20
|
-
wfile: ta.BinaryIO,
|
21
|
-
) -> None:
|
22
|
-
super().__init__()
|
23
|
-
|
24
|
-
self._client_address = client_address
|
25
|
-
self._rfile = rfile
|
26
|
-
self._wfile = wfile
|
27
|
-
|
16
|
+
class SocketHandler_(abc.ABC): # noqa
|
28
17
|
@abc.abstractmethod
|
29
|
-
def
|
18
|
+
def __call__(self, addr: SocketAddress, f: SocketIoPair) -> None:
|
30
19
|
raise NotImplementedError
|