Skip to content

Commit b01333c

Browse files
jmfrank63njsmith
authored andcommitted
Simplify trio.socket namespace construction (gh-670)
This should make trio.socket more intelligible to static analysis tools (see gh-542).
1 parent ce131cd commit b01333c

File tree

3 files changed

+95
-95
lines changed

3 files changed

+95
-95
lines changed

newsfragments/542.feature.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
* Reworked :mod:`trio` namespace construction, making it more understandable by static analysis tools. This should improve tab completion in editors, reduce false positives from pylint, and is a first step towards providing type hints.
1+
Reworked :mod:`trio`, :mod:`trio.testing`, and :mod:`trio.socket` namespace construction, making them more understandable by static analysis tools. This should improve tab completion in editors, reduce false positives from pylint, and is a first step towards providing type hints.

trio/_socket.py

+25-92
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os as _os
2-
import socket as _stdlib_socket
32
import sys as _sys
3+
import socket as _stdlib_socket
44
from functools import wraps as _wraps
55

66
import idna as _idna
@@ -9,22 +9,6 @@
99
from ._threads import run_sync_in_worker_thread
1010
from ._util import fspath
1111

12-
__all__ = []
13-
14-
################################################################
15-
# misc utilities
16-
################################################################
17-
18-
19-
def _reexport(name):
20-
globals()[name] = getattr(_stdlib_socket, name)
21-
__all__.append(name)
22-
23-
24-
def _add_to_all(obj):
25-
__all__.append(obj.__name__)
26-
return obj
27-
2812

2913
# Usage:
3014
#
@@ -62,54 +46,13 @@ async def __aexit__(self, etype, value, tb):
6246
# CONSTANTS
6347
################################################################
6448

65-
# Hopefully will show up in 3.7:
66-
# https://github.com/python/cpython/pull/477
67-
if not hasattr(_stdlib_socket, "TCP_NOTSENT_LOWAT"): # pragma: no branch
68-
if _sys.platform == "darwin":
69-
TCP_NOTSENT_LOWAT = 0x201
70-
__all__.append("TCP_NOTSENT_LOWAT")
71-
elif _sys.platform == "linux":
72-
TCP_NOTSENT_LOWAT = 25
73-
__all__.append("TCP_NOTSENT_LOWAT")
74-
75-
for _name in _stdlib_socket.__dict__.keys():
76-
if _name == _name.upper():
77-
_reexport(_name)
78-
79-
if _sys.platform == "win32":
80-
# See https://github.com/python-trio/trio/issues/39
81-
# (you can still get it from stdlib socket, of course, if you want it)
82-
globals().pop("SO_REUSEADDR", None)
83-
__all__.remove("SO_REUSEADDR")
84-
49+
try:
50+
from socket import IPPROTO_IPV6
51+
except ImportError:
8552
# As of at least 3.6, python on Windows is missing IPPROTO_IPV6
8653
# https://bugs.python.org/issue29515
87-
if not hasattr(_stdlib_socket, "IPPROTO_IPV6"): # pragma: no branch
54+
if _sys.platform == "win32":
8855
IPPROTO_IPV6 = 41
89-
__all__.append("IPPROTO_IPV6")
90-
91-
################################################################
92-
# Simple re-exports
93-
################################################################
94-
95-
for _name in [
96-
"gaierror",
97-
"herror",
98-
"gethostname",
99-
"ntohs",
100-
"htonl",
101-
"htons",
102-
"inet_aton",
103-
"inet_ntoa",
104-
"inet_pton",
105-
"inet_ntop",
106-
"sethostname",
107-
"if_nameindex",
108-
"if_nametoindex",
109-
"if_indextoname",
110-
]:
111-
if hasattr(_stdlib_socket, _name):
112-
_reexport(_name)
11356

11457
################################################################
11558
# Overrides
@@ -119,7 +62,6 @@ async def __aexit__(self, etype, value, tb):
11962
_socket_factory = _core.RunVar("socket_factory")
12063

12164

122-
@_add_to_all
12365
def set_custom_hostname_resolver(hostname_resolver):
12466
"""Set a custom hostname resolver.
12567
@@ -152,7 +94,6 @@ def set_custom_hostname_resolver(hostname_resolver):
15294
return old
15395

15496

155-
@_add_to_all
15697
def set_custom_socket_factory(socket_factory):
15798
"""Set a custom socket object factory.
15899
@@ -187,7 +128,6 @@ def set_custom_socket_factory(socket_factory):
187128
_NUMERIC_ONLY = _stdlib_socket.AI_NUMERICHOST | _stdlib_socket.AI_NUMERICSERV
188129

189130

190-
@_add_to_all
191131
async def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
192132
"""Look up a numeric address given a name.
193133
@@ -249,7 +189,6 @@ def numeric_only_failure(exc):
249189
)
250190

251191

252-
@_add_to_all
253192
async def getnameinfo(sockaddr, flags):
254193
"""Look up a name given a numeric address.
255194
@@ -269,7 +208,6 @@ async def getnameinfo(sockaddr, flags):
269208
)
270209

271210

272-
@_add_to_all
273211
async def getprotobyname(name):
274212
"""Look up a protocol number by name. (Rarely used.)
275213
@@ -289,7 +227,6 @@ async def getprotobyname(name):
289227
################################################################
290228

291229

292-
@_add_to_all
293230
def from_stdlib_socket(sock):
294231
"""Convert a standard library :func:`socket.socket` object into a trio
295232
socket object.
@@ -299,7 +236,6 @@ def from_stdlib_socket(sock):
299236

300237

301238
@_wraps(_stdlib_socket.fromfd, assigned=(), updated=())
302-
@_add_to_all
303239
def fromfd(*args, **kwargs):
304240
"""Like :func:`socket.fromfd`, but returns a trio socket object.
305241
@@ -310,13 +246,11 @@ def fromfd(*args, **kwargs):
310246
if hasattr(_stdlib_socket, "fromshare"):
311247

312248
@_wraps(_stdlib_socket.fromshare, assigned=(), updated=())
313-
@_add_to_all
314249
def fromshare(*args, **kwargs):
315250
return from_stdlib_socket(_stdlib_socket.fromshare(*args, **kwargs))
316251

317252

318253
@_wraps(_stdlib_socket.socketpair, assigned=(), updated=())
319-
@_add_to_all
320254
def socketpair(*args, **kwargs):
321255
"""Like :func:`socket.socketpair`, but returns a pair of trio socket
322256
objects.
@@ -327,7 +261,6 @@ def socketpair(*args, **kwargs):
327261

328262

329263
@_wraps(_stdlib_socket.socket, assigned=(), updated=())
330-
@_add_to_all
331264
def socket(
332265
family=_stdlib_socket.AF_INET,
333266
type=_stdlib_socket.SOCK_STREAM,
@@ -374,7 +307,26 @@ def real_socket_type(type_num):
374307
return type_num & _SOCK_TYPE_MASK
375308

376309

377-
@_add_to_all
310+
def _make_simple_sock_method_wrapper(methname, wait_fn, maybe_avail=False):
311+
fn = getattr(_stdlib_socket.socket, methname)
312+
313+
@_wraps(fn, assigned=("__name__",), updated=())
314+
async def wrapper(self, *args, **kwargs):
315+
return await self._nonblocking_helper(fn, args, kwargs, wait_fn)
316+
317+
wrapper.__doc__ = (
318+
"""Like :meth:`socket.socket.{}`, but async.
319+
320+
""".format(methname)
321+
)
322+
if maybe_avail:
323+
wrapper.__doc__ += (
324+
"Only available on platforms where :meth:`socket.socket.{}` "
325+
"is available.".format(methname)
326+
)
327+
return wrapper
328+
329+
378330
class SocketType:
379331
def __init__(self):
380332
raise TypeError(
@@ -603,25 +555,6 @@ async def _nonblocking_helper(self, fn, args, kwargs, wait_fn):
603555
except BlockingIOError:
604556
pass
605557

606-
def _make_simple_sock_method_wrapper(methname, wait_fn, maybe_avail=False):
607-
fn = getattr(_stdlib_socket.socket, methname)
608-
609-
@_wraps(fn, assigned=("__name__",), updated=())
610-
async def wrapper(self, *args, **kwargs):
611-
return await self._nonblocking_helper(fn, args, kwargs, wait_fn)
612-
613-
wrapper.__doc__ = (
614-
"""Like :meth:`socket.socket.{}`, but async.
615-
616-
""".format(methname)
617-
)
618-
if maybe_avail:
619-
wrapper.__doc__ += (
620-
"Only available on platforms where :meth:`socket.socket.{}` "
621-
"is available.".format(methname)
622-
)
623-
return wrapper
624-
625558
################################################################
626559
# accept
627560
################################################################

trio/socket.py

+69-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,72 @@
44
# temporaries, imports, etc. when implementing the module. So we put the
55
# implementation in an underscored module, and then re-export the public parts
66
# here.
7-
from ._socket import *
8-
from ._socket import __all__
7+
# We still have some underscore names though but only a few.
8+
9+
from . import _socket
10+
import sys as _sys
11+
12+
# import the overwrites
13+
from ._socket import (
14+
fromfd, from_stdlib_socket, getprotobyname, socketpair, getnameinfo,
15+
socket, getaddrinfo, set_custom_hostname_resolver,
16+
set_custom_socket_factory, SocketType
17+
)
18+
19+
# not always available so expose only if
20+
try:
21+
from ._socket import fromshare
22+
except ImportError:
23+
pass
24+
25+
# expose these functions to trio.socket
26+
from socket import (
27+
gaierror,
28+
herror,
29+
gethostname,
30+
ntohs,
31+
htonl,
32+
htons,
33+
inet_aton,
34+
inet_ntoa,
35+
inet_pton,
36+
inet_ntop,
37+
)
38+
39+
# not always available so expose only if
40+
try:
41+
from socket import (
42+
sethostname, if_nameindex, if_nametoindex, if_indextoname
43+
)
44+
except ImportError:
45+
pass
46+
47+
# expose all uppercase names from standardlib socket to trio.socket
48+
import socket as _stdlib_socket
49+
50+
globals().update(
51+
{
52+
_name: getattr(_stdlib_socket, _name)
53+
for _name in _stdlib_socket.__dict__ if _name.isupper()
54+
}
55+
)
56+
57+
if _sys.platform == 'win32':
58+
# See https://github.com/python-trio/trio/issues/39
59+
# Do not import for windows platform
60+
# (you can still get it from stdlib socket, of course, if you want it)
61+
del SO_REUSEADDR
62+
63+
# get names used by trio that we define on our own
64+
from ._socket import IPPROTO_IPV6
65+
66+
# Not defined in all python versions and platforms but sometimes needed
67+
try:
68+
TCP_NOTSENT_LOWAT
69+
except NameError:
70+
# Hopefully will show up in 3.7:
71+
# https://github.com/python/cpython/pull/477
72+
if _sys.platform == "darwin":
73+
TCP_NOTSENT_LOWAT = 0x201
74+
elif _sys.platform == "linux":
75+
TCP_NOTSENT_LOWAT = 25

0 commit comments

Comments
 (0)