many more socketutils docs, now with an rst file for the sphinxing

This commit is contained in:
Mahmoud Hashemi 2016-04-28 00:57:36 -07:00
parent 1edb307882
commit 88d73c3e8b
2 changed files with 77 additions and 21 deletions

View File

@ -47,7 +47,7 @@ except Exception:
try: try:
from typeutils import make_sentinel from typeutils import make_sentinel
_UNSET = make_sentinel(var_name='_MISSING') _UNSET = make_sentinel(var_name='_UNSET')
except ImportError: except ImportError:
_UNSET = object() _UNSET = object()
@ -77,7 +77,7 @@ class BufferedSocket(object):
*timeout* and *maxsize* can both be overridden on individual socket *timeout* and *maxsize* can both be overridden on individual socket
operations. operations.
All ``recv`` methods return bytestrings (:type:`bytes`) and can All ``recv`` methods return bytestrings (:class:`bytes`) and can
raise :exc:`socket.error`. :exc:`Timeout`, raise :exc:`socket.error`. :exc:`Timeout`,
:exc:`ConnectionClosed`, and :exc:`MessageTooLong` all inherit :exc:`ConnectionClosed`, and :exc:`MessageTooLong` all inherit
from :exc:`socket.error` and exist to provide better error from :exc:`socket.error` and exist to provide better error
@ -87,7 +87,7 @@ class BufferedSocket(object):
BufferedSocket does not replace the built-in socket by any BufferedSocket does not replace the built-in socket by any
means. While the overlapping parts of the API are kept parallel to means. While the overlapping parts of the API are kept parallel to
the built-in :type:`socket.socket`, BufferedSocket does not the built-in :class:`socket.socket`, BufferedSocket does not
inherit from socket, and most socket functionality is only inherit from socket, and most socket functionality is only
available on the underlying socket. :meth:`socket.getpeername`, available on the underlying socket. :meth:`socket.getpeername`,
:meth:`socket.getsockname`, :meth:`socket.fileno`, and others are :meth:`socket.getsockname`, :meth:`socket.fileno`, and others are
@ -113,12 +113,12 @@ class BufferedSocket(object):
self.recv_lock = RLock() self.recv_lock = RLock()
def settimeout(self, timeout): def settimeout(self, timeout):
"Set the default timeout for future operations, in float seconds." "Set the default *timeout* for future operations, in seconds."
self.timeout = timeout self.timeout = timeout
def setmaxsize(self, maxsize): def setmaxsize(self, maxsize):
"""Set the default maximum buffer size for future operations, in """Set the default maximum buffer size *maxsize* for future
integer of bytes. Does not truncate the current buffer. operations, in bytes. Does not truncate the current buffer.
""" """
self.maxsize = maxsize self.maxsize = maxsize
@ -140,15 +140,17 @@ class BufferedSocket(object):
size (int): The maximum number of bytes to receive. size (int): The maximum number of bytes to receive.
flags (int): Kept for API compatibility with sockets. Only flags (int): Kept for API compatibility with sockets. Only
the default, ``0``, is valid. the default, ``0``, is valid.
timeout (float): The timeout for this operation. Can be 0 for timeout (float): The timeout for this operation. Can be
nonblocking and None for no timeout. Defaults to the value ``0`` for nonblocking and ``None`` for no
set in the constructor of BufferedSocket. timeout. Defaults to the value set in the constructor
of BufferedSocket.
If the operation does not complete in *timeout* seconds, a If the operation does not complete in *timeout* seconds, a
:exc:`Timeout` is raised. Much like the built-in :exc:`Timeout` is raised. Much like the built-in
:type:`socket.socket`, if this method returns an empty string, :class:`socket.socket`, if this method returns an empty string,
then the socket is closed and recv buffer is empty. Further then the socket is closed and recv buffer is empty. Further
calls to recv will raise :exc:`socket.error`. calls to recv will raise :exc:`socket.error`.
""" """
with self.recv_lock: with self.recv_lock:
if timeout is _UNSET: if timeout is _UNSET:
@ -314,7 +316,9 @@ class BufferedSocket(object):
def send(self, data, flags=0, timeout=_UNSET): def send(self, data, flags=0, timeout=_UNSET):
"""Send the contents of the internal send buffer, as well as *data*, """Send the contents of the internal send buffer, as well as *data*,
to the receiving end of the connection. to the receiving end of the connection. Returns the total
number of bytes sent. If no exception is raised, all of *data* was
sent and the internal send buffer is empty.
Args: Args:
data (bytes): The bytes to send. data (bytes): The bytes to send.
@ -325,7 +329,9 @@ class BufferedSocket(object):
set in the constructor of BufferedSocket. set in the constructor of BufferedSocket.
Will raise :exc:`Timeout` if the send operation fails to Will raise :exc:`Timeout` if the send operation fails to
complete before *timeout*. complete before *timeout*. In the event of an exception, use
:meth:`BufferedSocket.getsendbuffer` to see which data was
unsent.
""" """
with self.send_lock: with self.send_lock:
@ -336,12 +342,13 @@ class BufferedSocket(object):
sbuf = self.sbuf sbuf = self.sbuf
sbuf.append(data) sbuf.append(data)
if len(sbuf) > 1: if len(sbuf) > 1:
sbuf[:] = [b''.join(sbuf)] sbuf[:] = [b''.join([s for s in sbuf if s])]
self.sock.settimeout(timeout) self.sock.settimeout(timeout)
start = time.time() start, total_sent = time.time(), 0
try: try:
while sbuf[0]: while sbuf[0]:
sent = self.sock.send(sbuf[0]) sent = self.sock.send(sbuf[0])
total_sent += sent
sbuf[0] = sbuf[0][sent:] sbuf[0] = sbuf[0][sent:]
if timeout: if timeout:
cur_timeout = timeout - (time.time() - start) cur_timeout = timeout - (time.time() - start)
@ -350,9 +357,13 @@ class BufferedSocket(object):
self.sock.settimeout(cur_timeout) self.sock.settimeout(cur_timeout)
except socket.timeout: except socket.timeout:
raise Timeout(timeout, '%s bytes unsent' % len(sbuf[0])) raise Timeout(timeout, '%s bytes unsent' % len(sbuf[0]))
return return total_sent
sendall = send def sendall(self, data, flags=0, timeout=_UNSET):
"""An passthrough to :meth:`~BufferedSocket.send`, retained for
parallelism to the :class:`socket.socket` API.
"""
return self.send(data, flags, timeout)
def flush(self): def flush(self):
"Send the contents of the internal send buffer." "Send the contents of the internal send buffer."
@ -367,7 +378,14 @@ class BufferedSocket(object):
return return
class Error(socket.error, Exception): class Error(socket.error):
"""A subclass of :exc:`socket.error` from which all other
``socketutils`` exceptions inherit.
When using :class:`BufferedSocket` and other ``socketutils``
types, generally you want to catch one of the specific exception
types below, or :exc:`socket.error`.
"""
pass pass
@ -376,13 +394,15 @@ class ConnectionClosed(Error):
class MessageTooLong(Error): class MessageTooLong(Error):
"""Only raised from :meth:`BufferedSocket.recv_until` when more than """Raised from :meth:`BufferedSocket.recv_until` and
*maxsize* bytes are read without the socket closing. :meth:`BufferedSocket.recv_closed` when more than *maxsize* bytes are
read without encountering the marker or a closed connection,
respectively.
""" """
def __init__(self, bytes_read=None, marker=None): def __init__(self, bytes_read=None, marker=None):
msg = 'message exceeded maximum size' msg = 'message exceeded maximum size'
if bytes_read is not None: if bytes_read is not None:
msg += '%s bytes read' % (bytes_read,) msg += '. %s bytes read' % (bytes_read,)
if marker is not None: if marker is not None:
msg += '. Marker not found: %r' % (marker,) msg += '. Marker not found: %r' % (marker,)
super(MessageTooLong, self).__init__(msg) super(MessageTooLong, self).__init__(msg)
@ -405,7 +425,7 @@ class NetstringSocket(object):
More info: https://en.wikipedia.org/wiki/Netstring More info: https://en.wikipedia.org/wiki/Netstring
Even more info: http://cr.yp.to/proto/netstrings.txt Even more info: http://cr.yp.to/proto/netstrings.txt
""" """
def __init__(self, sock, timeout=30, maxsize=32 * 1024): def __init__(self, sock, timeout=DEFAULT_TIMEOUT, maxsize=DEFAULT_MAXSIZE):
self.bsock = BufferedSocket(sock) self.bsock = BufferedSocket(sock)
self.timeout = timeout self.timeout = timeout
self.maxsize = maxsize self.maxsize = maxsize

36
docs/socketutils.rst Normal file
View File

@ -0,0 +1,36 @@
``socketutils`` - ``socket`` wrappers
=====================================
.. automodule:: boltons.socketutils
BufferedSocket
--------------
.. autoclass:: boltons.socketutils.BufferedSocket
:members:
Exceptions
^^^^^^^^^^
These are a few exceptions that derive from :exc:`socket.error` and
provide clearer code and better error messages.
.. autoexception:: boltons.socketutils.Error
.. autoexception:: boltons.socketutils.Timeout
.. autoexception:: boltons.socketutils.ConnectionClosed
.. autoexception:: boltons.socketutils.MessageTooLong
Netstring
---------
.. autoclass:: boltons.socketutils.NetstringSocket
:members:
Nestring Exceptions
^^^^^^^^^^^^^^^^^^^
These are a few higher-level exceptions for netstring connections.
.. autoexception:: boltons.socketutils.NetstringProtocolError
.. autoexception:: boltons.socketutils.NetstringInvalidSize
.. autoexception:: boltons.socketutils.NetstringMessageTooLong