diff options
Diffstat (limited to 'Lib/socket.py')
-rw-r--r-- | Lib/socket.py | 131 |
1 files changed, 116 insertions, 15 deletions
diff --git a/Lib/socket.py b/Lib/socket.py index d4f1b65e46..8efd760696 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -35,11 +35,13 @@ SocketType -- type object for socket objects error -- exception raised for I/O errors has_ipv6 -- boolean value indicating if IPv6 is supported -Integer constants: +IntEnum constants: AF_INET, AF_UNIX -- socket domains (first argument to socket() call) SOCK_STREAM, SOCK_DGRAM, SOCK_RAW -- socket types (second argument) +Integer constants: + Many other constants may be defined; these may be used in calls to the setsockopt() and getsockopt() methods. """ @@ -48,6 +50,7 @@ import _socket from _socket import * import os, sys, io +from enum import IntEnum try: import errno @@ -57,9 +60,34 @@ EBADF = getattr(errno, 'EBADF', 9) EAGAIN = getattr(errno, 'EAGAIN', 11) EWOULDBLOCK = getattr(errno, 'EWOULDBLOCK', 11) -__all__ = ["getfqdn", "create_connection"] +__all__ = ["fromfd", "getfqdn", "create_connection", + "AddressFamily", "SocketKind"] __all__.extend(os._get_exports_list(_socket)) +# Set up the socket.AF_* socket.SOCK_* constants as members of IntEnums for +# nicer string representations. +# Note that _socket only knows about the integer values. The public interface +# in this module understands the enums and translates them back from integers +# where needed (e.g. .family property of a socket object). +AddressFamily = IntEnum('AddressFamily', + {name: value for name, value in globals().items() + if name.isupper() and name.startswith('AF_')}) +globals().update(AddressFamily.__members__) + +SocketKind = IntEnum('SocketKind', + {name: value for name, value in globals().items() + if name.isupper() and name.startswith('SOCK_')}) +globals().update(SocketKind.__members__) + +def _intenum_converter(value, enum_klass): + """Convert a numeric family value to an IntEnum member. + + If it's not a known member, return the numeric value itself. + """ + try: + return enum_klass(value) + except ValueError: + return value _realsocket = socket @@ -91,6 +119,10 @@ class socket(_socket.socket): __slots__ = ["__weakref__", "_io_refs", "_closed"] def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None): + # For user code address family and type values are IntEnum members, but + # for the underlying _socket.socket they're just integers. The + # constructor of _socket.socket converts the given argument to an + # integer automatically. _socket.socket.__init__(self, family, type, proto, fileno) self._io_refs = 0 self._closed = False @@ -103,13 +135,32 @@ class socket(_socket.socket): self.close() def __repr__(self): - """Wrap __repr__() to reveal the real class name.""" - s = _socket.socket.__repr__(self) - if s.startswith("<socket object"): - s = "<%s.%s%s%s" % (self.__class__.__module__, - self.__class__.__name__, - getattr(self, '_closed', False) and " [closed] " or "", - s[7:]) + """Wrap __repr__() to reveal the real class name and socket + address(es). + """ + closed = getattr(self, '_closed', False) + s = "<%s.%s%s fd=%i, family=%s, type=%s, proto=%i" \ + % (self.__class__.__module__, + self.__class__.__name__, + " [closed]" if closed else "", + self.fileno(), + self.family, + self.type, + self.proto) + if not closed: + try: + laddr = self.getsockname() + if laddr: + s += ", laddr=%s" % str(laddr) + except error: + pass + try: + raddr = self.getpeername() + if raddr: + s += ", raddr=%s" % str(raddr) + except error: + pass + s += '>' return s def __getstate__(self): @@ -118,7 +169,8 @@ class socket(_socket.socket): def dup(self): """dup() -> socket object - Return a new socket object connected to the same system resource. + Duplicate the socket. Return a new socket object connected to the same + system resource. The new socket is non-inheritable. """ fd = dup(self.fileno()) sock = self.__class__(self.family, self.type, self.proto, fileno=fd) @@ -149,9 +201,8 @@ class socket(_socket.socket): except the only mode characters supported are 'r', 'w' and 'b'. The semantics are similar too. (XXX refactor to share code?) """ - for c in mode: - if c not in {"r", "w", "b"}: - raise ValueError("invalid mode %r (only r, w, b allowed)") + if not set(mode) <= {"r", "w", "b"}: + raise ValueError("invalid mode %r (only r, w, b allowed)" % (mode,)) writing = "w" in mode reading = "r" in mode or not writing assert reading or writing @@ -210,6 +261,31 @@ class socket(_socket.socket): self._closed = True return super().detach() + @property + def family(self): + """Read-only access to the address family for this socket. + """ + return _intenum_converter(super().family, AddressFamily) + + @property + def type(self): + """Read-only access to the socket type. + """ + return _intenum_converter(super().type, SocketKind) + + if os.name == 'nt': + def get_inheritable(self): + return os.get_handle_inheritable(self.fileno()) + def set_inheritable(self, inheritable): + os.set_handle_inheritable(self.fileno(), inheritable) + else: + def get_inheritable(self): + return os.get_inheritable(self.fileno()) + def set_inheritable(self, inheritable): + os.set_inheritable(self.fileno(), inheritable) + get_inheritable.__doc__ = "Get the inheritable flag of the socket" + set_inheritable.__doc__ = "Set the inheritable flag of the socket" + def fromfd(fd, family, type, proto=0): """ fromfd(fd, family, type[, proto]) -> socket object @@ -223,10 +299,11 @@ if hasattr(_socket.socket, "share"): def fromshare(info): """ fromshare(info) -> socket object - Create a socket object from a the bytes object returned by + Create a socket object from the bytes object returned by socket.share(pid). """ return socket(0, 0, 0, info) + __all__.append("fromshare") if hasattr(_socket, "socketpair"): @@ -291,7 +368,7 @@ class SocketIO(io.RawIOBase): self._checkClosed() self._checkReadable() if self._timeout_occurred: - raise IOError("cannot read from timed out object") + raise OSError("cannot read from timed out object") while True: try: return self._sock.recv_into(b) @@ -435,3 +512,27 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, raise err else: raise error("getaddrinfo returns an empty list") + +def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0): + """Resolve host and port into list of address info entries. + + Translate the host/port argument into a sequence of 5-tuples that contain + all the necessary arguments for creating a socket connected to that service. + host is a domain name, a string representation of an IPv4/v6 address or + None. port is a string service name such as 'http', a numeric port number or + None. By passing None as the value of host and port, you can pass NULL to + the underlying C API. + + The family, type and proto arguments can be optionally specified in order to + narrow the list of addresses returned. Passing zero as a value for each of + these arguments selects the full range of results. + """ + # We override this function since we want to translate the numeric family + # and socket type values to enum constants. + addrlist = [] + for res in _socket.getaddrinfo(host, port, family, type, proto, flags): + af, socktype, proto, canonname, sa = res + addrlist.append((_intenum_converter(af, AddressFamily), + _intenum_converter(socktype, SocketKind), + proto, canonname, sa)) + return addrlist |