Skip to content

Commit c860ab6

Browse files
committed
Remove SSLConnectionProxyMeta using class decorator
1 parent a26fd31 commit c860ab6

File tree

6 files changed

+93
-86
lines changed

6 files changed

+93
-86
lines changed

.DS_Store

10 KB
Binary file not shown.

.flake8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ per-file-ignores =
129129
cheroot/makefile.py: DAR101, DAR201, DAR401, E800, I003, I004, N801, N802, S101, WPS100, WPS110, WPS111, WPS117, WPS120, WPS121, WPS122, WPS123, WPS130, WPS204, WPS210, WPS212, WPS213, WPS220, WPS229, WPS231, WPS232, WPS338, WPS420, WPS422, WPS429, WPS431, WPS504, WPS604, WPS606
130130
cheroot/server.py: DAR003, DAR101, DAR201, DAR202, DAR301, DAR401, E800, I001, I003, I004, I005, N806, RST201, RST301, RST303, RST304, WPS100, WPS110, WPS111, WPS115, WPS120, WPS121, WPS122, WPS130, WPS132, WPS201, WPS202, WPS204, WPS210, WPS211, WPS212, WPS213, WPS214, WPS220, WPS221, WPS225, WPS226, WPS229, WPS230, WPS231, WPS236, WPS237, WPS238, WPS301, WPS338, WPS342, WPS410, WPS420, WPS421, WPS422, WPS429, WPS432, WPS504, WPS505, WPS601, WPS602, WPS608, WPS617
131131
cheroot/ssl/builtin.py: DAR101, DAR201, DAR401, I001, I003, N806, RST304, WPS110, WPS111, WPS115, WPS117, WPS120, WPS121, WPS122, WPS130, WPS201, WPS210, WPS214, WPS229, WPS231, WPS338, WPS422, WPS501, WPS505, WPS529, WPS608, WPS612
132-
cheroot/ssl/pyopenssl.py: C815, DAR101, DAR201, DAR401, I001, I003, I005, N801, N804, RST304, WPS100, WPS110, WPS111, WPS117, WPS120, WPS121, WPS130, WPS210, WPS220, WPS221, WPS225, WPS229, WPS231, WPS238, WPS301, WPS335, WPS338, WPS420, WPS422, WPS430, WPS432, WPS501, WPS504, WPS505, WPS601, WPS608, WPS615
132+
cheroot/ssl/pyopenssl.py: C815, DAR101, DAR201, DAR401, I001, I003, I005, N801, N804, RST304, WPS100, WPS110, WPS111, WPS117, WPS120, WPS121, WPS130, WPS210, WPS220, WPS221, WPS225, WPS229, WPS231, WPS238, WPS301, WPS335, WPS338, WPS420, WPS422, WPS430, WPS432, WPS501, WPS504, WPS505, WPS601, WPS602, WPS608, WPS615
133133
cheroot/test/conftest.py: DAR101, DAR201, DAR301, I001, I003, I005, WPS100, WPS130, WPS325, WPS354, WPS420, WPS422, WPS430, WPS457
134134
cheroot/test/helper.py: DAR101, DAR201, DAR401, I001, I003, I004, N802, WPS110, WPS111, WPS121, WPS201, WPS220, WPS231, WPS301, WPS414, WPS421, WPS422, WPS505
135135
cheroot/test/ssl/test_ssl_pyopenssl.py: DAR101, DAR201, WPS226

.vscode/settings.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"python.defaultInterpreterPath": "/Users/julian/Documents/GitHub/cheroot/.venv/bin/python",
3+
"python.testing.pytestEnabled": true,
4+
"python.testing.pytestPath": "/Users/julian/Documents/GitHub/cheroot/.venv/bin/pytest",
5+
"python.testing.pytestArgs": [
6+
"--rootdir=.",
7+
"--import-mode=importlib" // Optional: A modern import mode that may help
8+
]
9+
}

cheroot/ssl/pyopenssl.py

Lines changed: 79 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -216,92 +216,89 @@ class SSLFileobjectStreamWriter(SSLFileobjectMixin, StreamWriter):
216216
"""SSL file object attached to a socket object."""
217217

218218

219-
class SSLConnectionProxyMeta:
220-
"""Metaclass for generating a bunch of proxy methods."""
221-
222-
def __new__(mcl, name, bases, nmspc):
223-
"""Attach a list of proxy methods to a new class."""
224-
proxy_methods = (
225-
'get_context',
226-
'pending',
227-
'send',
228-
'write',
229-
'recv',
230-
'read',
231-
'renegotiate',
232-
'bind',
233-
'listen',
234-
'connect',
235-
'accept',
236-
'setblocking',
237-
'fileno',
238-
'close',
239-
'get_cipher_list',
240-
'getpeername',
241-
'getsockname',
242-
'getsockopt',
243-
'setsockopt',
244-
'makefile',
245-
'get_app_data',
246-
'set_app_data',
247-
'state_string',
248-
'sock_shutdown',
249-
'get_peer_certificate',
250-
'want_read',
251-
'want_write',
252-
'set_connect_state',
253-
'set_accept_state',
254-
'connect_ex',
255-
'sendall',
256-
'settimeout',
257-
'gettimeout',
258-
'shutdown',
259-
)
260-
proxy_methods_no_args = ('shutdown',)
261-
262-
proxy_props = ('family',)
263-
264-
def lock_decorator(method):
265-
"""Create a proxy method for a new class."""
266-
267-
def proxy_wrapper(self, *args):
268-
new_args = (
269-
args[:] if method not in proxy_methods_no_args else []
270-
)
271-
# translate any SysCallError to ConnectionError
272-
with _morph_syscall_to_connection_error(method), self._lock:
273-
return getattr(self._ssl_conn, method)(*new_args)
274-
275-
return proxy_wrapper
276-
277-
for m in proxy_methods:
278-
nmspc[m] = lock_decorator(m)
279-
nmspc[m].__name__ = m
280-
281-
def make_property(property_):
282-
"""Create a proxy method for a new class."""
283-
284-
def proxy_prop_wrapper(self):
285-
return getattr(self._ssl_conn, property_)
286-
287-
proxy_prop_wrapper.__name__ = property_
288-
return property(proxy_prop_wrapper)
289-
290-
for p in proxy_props:
291-
nmspc[p] = make_property(p)
219+
# Wrapper function to dynamically add thread-safe methods and properties
220+
def _wrap_ssl_connection_methods(cls):
221+
"""Dynamically attaches proxied methods and properties to the class."""
222+
# 1. Attach Methods
223+
for method in cls.proxy_methods:
224+
wrapped_func = cls._lock_decorator(method)
225+
setattr(cls, method, wrapped_func)
226+
227+
# 2. Attach Properties
228+
for prop in cls.proxy_props:
229+
setattr(cls, prop, cls._make_property(prop))
230+
231+
return cls
232+
233+
234+
@_wrap_ssl_connection_methods
235+
class SSLConnection:
236+
"""A thread-safe wrapper around :py:class:`SSL.Connection <pyopenssl:OpenSSL.SSL.Connection>`."""
237+
238+
proxy_methods = (
239+
'get_context',
240+
'pending',
241+
'send',
242+
'write',
243+
'recv',
244+
'read',
245+
'renegotiate',
246+
'bind',
247+
'listen',
248+
'connect',
249+
'accept',
250+
'setblocking',
251+
'fileno',
252+
'close',
253+
'get_cipher_list',
254+
'getpeername',
255+
'getsockname',
256+
'getsockopt',
257+
'setsockopt',
258+
'makefile',
259+
'get_app_data',
260+
'set_app_data',
261+
'state_string',
262+
'sock_shutdown',
263+
'get_peer_certificate',
264+
'want_read',
265+
'want_write',
266+
'set_connect_state',
267+
'set_accept_state',
268+
'connect_ex',
269+
'sendall',
270+
'settimeout',
271+
'gettimeout',
272+
'shutdown',
273+
)
274+
proxy_methods_no_args = ('shutdown',)
275+
proxy_props = ('family',)
276+
277+
@staticmethod
278+
def _lock_decorator(method):
279+
"""Create the thread-safe, error-translating proxy method."""
280+
281+
def proxy_wrapper(self, *args):
282+
new_args = (
283+
args[:]
284+
if method not in SSLConnection.proxy_methods_no_args
285+
else []
286+
)
287+
with self._lock, _morph_syscall_to_connection_error(method):
288+
return getattr(self._ssl_conn, method)(*new_args)
292289

293-
# Doesn't work via super() for some reason.
294-
# Falling back to type() instead:
295-
return type(name, bases, nmspc)
290+
proxy_wrapper.__name__ = method
291+
return proxy_wrapper
296292

293+
@staticmethod
294+
def _make_property(property_):
295+
"""Create the property proxy."""
297296

298-
class SSLConnection(metaclass=SSLConnectionProxyMeta):
299-
r"""A thread-safe wrapper for an ``SSL.Connection``.
297+
def proxy_prop_wrapper(self):
298+
return getattr(self._ssl_conn, property_)
300299

301-
:param tuple args: the arguments to create the wrapped \
302-
:py:class:`SSL.Connection(*args) \
303-
<pyopenssl:OpenSSL.SSL.Connection>`
304-
"""
300+
proxy_prop_wrapper.__name__ = property_
301+
return property(proxy_prop_wrapper)
305302

306303
def __init__(self, *args):
307304
"""Initialize SSLConnection instance."""

cheroot/ssl/pyopenssl.pyi

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ class SSLFileobjectMixin:
1818
class SSLFileobjectStreamReader(SSLFileobjectMixin, StreamReader): ... # type:ignore[misc]
1919
class SSLFileobjectStreamWriter(SSLFileobjectMixin, StreamWriter): ... # type:ignore[misc]
2020

21-
class SSLConnectionProxyMeta:
22-
def __new__(mcl, name, bases, nmspc): ...
23-
2421
class SSLConnection:
22+
proxy_methods: tuple[str, ...]
23+
proxy_methods_no_args: tuple[str, ...]
24+
proxy_props: tuple[str, ...]
25+
2526
def __init__(self, *args) -> None: ...
2627

2728
class pyOpenSSLAdapter(Adapter):

docs/.DS_Store

8 KB
Binary file not shown.

0 commit comments

Comments
 (0)