diff --git a/newsfragments/542.feature.rst b/newsfragments/542.feature.rst index 650b489adc..678007c283 100644 --- a/newsfragments/542.feature.rst +++ b/newsfragments/542.feature.rst @@ -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. \ No newline at end of file +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. \ No newline at end of file diff --git a/trio/_socket.py b/trio/_socket.py index 507c4b07a6..cb0475611f 100644 --- a/trio/_socket.py +++ b/trio/_socket.py @@ -1,6 +1,6 @@ import os as _os -import socket as _stdlib_socket import sys as _sys +import socket as _stdlib_socket from functools import wraps as _wraps import idna as _idna @@ -9,22 +9,6 @@ from ._threads import run_sync_in_worker_thread from ._util import fspath -__all__ = [] - -################################################################ -# misc utilities -################################################################ - - -def _reexport(name): - globals()[name] = getattr(_stdlib_socket, name) - __all__.append(name) - - -def _add_to_all(obj): - __all__.append(obj.__name__) - return obj - # Usage: # @@ -62,54 +46,13 @@ async def __aexit__(self, etype, value, tb): # CONSTANTS ################################################################ -# Hopefully will show up in 3.7: -# https://github.com/python/cpython/pull/477 -if not hasattr(_stdlib_socket, "TCP_NOTSENT_LOWAT"): # pragma: no branch - if _sys.platform == "darwin": - TCP_NOTSENT_LOWAT = 0x201 - __all__.append("TCP_NOTSENT_LOWAT") - elif _sys.platform == "linux": - TCP_NOTSENT_LOWAT = 25 - __all__.append("TCP_NOTSENT_LOWAT") - -for _name in _stdlib_socket.__dict__.keys(): - if _name == _name.upper(): - _reexport(_name) - -if _sys.platform == "win32": - # See https://github.com/python-trio/trio/issues/39 - # (you can still get it from stdlib socket, of course, if you want it) - globals().pop("SO_REUSEADDR", None) - __all__.remove("SO_REUSEADDR") - +try: + from socket import IPPROTO_IPV6 +except ImportError: # As of at least 3.6, python on Windows is missing IPPROTO_IPV6 # https://bugs.python.org/issue29515 - if not hasattr(_stdlib_socket, "IPPROTO_IPV6"): # pragma: no branch + if _sys.platform == "win32": IPPROTO_IPV6 = 41 - __all__.append("IPPROTO_IPV6") - -################################################################ -# Simple re-exports -################################################################ - -for _name in [ - "gaierror", - "herror", - "gethostname", - "ntohs", - "htonl", - "htons", - "inet_aton", - "inet_ntoa", - "inet_pton", - "inet_ntop", - "sethostname", - "if_nameindex", - "if_nametoindex", - "if_indextoname", -]: - if hasattr(_stdlib_socket, _name): - _reexport(_name) ################################################################ # Overrides @@ -119,7 +62,6 @@ async def __aexit__(self, etype, value, tb): _socket_factory = _core.RunVar("socket_factory") -@_add_to_all def set_custom_hostname_resolver(hostname_resolver): """Set a custom hostname resolver. @@ -152,7 +94,6 @@ def set_custom_hostname_resolver(hostname_resolver): return old -@_add_to_all def set_custom_socket_factory(socket_factory): """Set a custom socket object factory. @@ -187,7 +128,6 @@ def set_custom_socket_factory(socket_factory): _NUMERIC_ONLY = _stdlib_socket.AI_NUMERICHOST | _stdlib_socket.AI_NUMERICSERV -@_add_to_all async def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0): """Look up a numeric address given a name. @@ -249,7 +189,6 @@ def numeric_only_failure(exc): ) -@_add_to_all async def getnameinfo(sockaddr, flags): """Look up a name given a numeric address. @@ -269,7 +208,6 @@ async def getnameinfo(sockaddr, flags): ) -@_add_to_all async def getprotobyname(name): """Look up a protocol number by name. (Rarely used.) @@ -289,7 +227,6 @@ async def getprotobyname(name): ################################################################ -@_add_to_all def from_stdlib_socket(sock): """Convert a standard library :func:`socket.socket` object into a trio socket object. @@ -299,7 +236,6 @@ def from_stdlib_socket(sock): @_wraps(_stdlib_socket.fromfd, assigned=(), updated=()) -@_add_to_all def fromfd(*args, **kwargs): """Like :func:`socket.fromfd`, but returns a trio socket object. @@ -310,13 +246,11 @@ def fromfd(*args, **kwargs): if hasattr(_stdlib_socket, "fromshare"): @_wraps(_stdlib_socket.fromshare, assigned=(), updated=()) - @_add_to_all def fromshare(*args, **kwargs): return from_stdlib_socket(_stdlib_socket.fromshare(*args, **kwargs)) @_wraps(_stdlib_socket.socketpair, assigned=(), updated=()) -@_add_to_all def socketpair(*args, **kwargs): """Like :func:`socket.socketpair`, but returns a pair of trio socket objects. @@ -327,7 +261,6 @@ def socketpair(*args, **kwargs): @_wraps(_stdlib_socket.socket, assigned=(), updated=()) -@_add_to_all def socket( family=_stdlib_socket.AF_INET, type=_stdlib_socket.SOCK_STREAM, @@ -374,7 +307,26 @@ def real_socket_type(type_num): return type_num & _SOCK_TYPE_MASK -@_add_to_all +def _make_simple_sock_method_wrapper(methname, wait_fn, maybe_avail=False): + fn = getattr(_stdlib_socket.socket, methname) + + @_wraps(fn, assigned=("__name__",), updated=()) + async def wrapper(self, *args, **kwargs): + return await self._nonblocking_helper(fn, args, kwargs, wait_fn) + + wrapper.__doc__ = ( + """Like :meth:`socket.socket.{}`, but async. + + """.format(methname) + ) + if maybe_avail: + wrapper.__doc__ += ( + "Only available on platforms where :meth:`socket.socket.{}` " + "is available.".format(methname) + ) + return wrapper + + class SocketType: def __init__(self): raise TypeError( @@ -603,25 +555,6 @@ async def _nonblocking_helper(self, fn, args, kwargs, wait_fn): except BlockingIOError: pass - def _make_simple_sock_method_wrapper(methname, wait_fn, maybe_avail=False): - fn = getattr(_stdlib_socket.socket, methname) - - @_wraps(fn, assigned=("__name__",), updated=()) - async def wrapper(self, *args, **kwargs): - return await self._nonblocking_helper(fn, args, kwargs, wait_fn) - - wrapper.__doc__ = ( - """Like :meth:`socket.socket.{}`, but async. - - """.format(methname) - ) - if maybe_avail: - wrapper.__doc__ += ( - "Only available on platforms where :meth:`socket.socket.{}` " - "is available.".format(methname) - ) - return wrapper - ################################################################ # accept ################################################################ diff --git a/trio/socket.py b/trio/socket.py index 93af20e383..1ab03234af 100644 --- a/trio/socket.py +++ b/trio/socket.py @@ -4,5 +4,72 @@ # temporaries, imports, etc. when implementing the module. So we put the # implementation in an underscored module, and then re-export the public parts # here. -from ._socket import * -from ._socket import __all__ +# We still have some underscore names though but only a few. + +from . import _socket +import sys as _sys + +# import the overwrites +from ._socket import ( + fromfd, from_stdlib_socket, getprotobyname, socketpair, getnameinfo, + socket, getaddrinfo, set_custom_hostname_resolver, + set_custom_socket_factory, SocketType +) + +# not always available so expose only if +try: + from ._socket import fromshare +except ImportError: + pass + +# expose these functions to trio.socket +from socket import ( + gaierror, + herror, + gethostname, + ntohs, + htonl, + htons, + inet_aton, + inet_ntoa, + inet_pton, + inet_ntop, +) + +# not always available so expose only if +try: + from socket import ( + sethostname, if_nameindex, if_nametoindex, if_indextoname + ) +except ImportError: + pass + +# expose all uppercase names from standardlib socket to trio.socket +import socket as _stdlib_socket + +globals().update( + { + _name: getattr(_stdlib_socket, _name) + for _name in _stdlib_socket.__dict__ if _name.isupper() + } +) + +if _sys.platform == 'win32': + # See https://github.com/python-trio/trio/issues/39 + # Do not import for windows platform + # (you can still get it from stdlib socket, of course, if you want it) + del SO_REUSEADDR + +# get names used by trio that we define on our own +from ._socket import IPPROTO_IPV6 + +# Not defined in all python versions and platforms but sometimes needed +try: + TCP_NOTSENT_LOWAT +except NameError: + # Hopefully will show up in 3.7: + # https://github.com/python/cpython/pull/477 + if _sys.platform == "darwin": + TCP_NOTSENT_LOWAT = 0x201 + elif _sys.platform == "linux": + TCP_NOTSENT_LOWAT = 25