Modules
Module slip
The slip module contains configuration settings and
lower-level functions and classes that are mainly useful for extending the sliplib package.
The configuration settings, functions, classes, and exception in this module can also be imported directly from the
sliplib package.
Configuration
- config
The configuration settings are stored in the
configconfiguration object. Theconfigobject provides constants, configuration settings, and a context manager forsliplib.Constants
- config.ESC = b'\xdb'
Type:
bytesThe SLIP
ESCbyte. Used to escapeESCandENDbytes in the message.
For backwards compatibility reasons these constants can also be imported directly from the top-level
sliplibmodule.Settings
Context Manager
- config.use_leading_end_byte(value)
Temporarily modify the value of
USE_LEADING_END_BYTE.This context manager ensures that any
DriverandSlipWrapperinstances that are defined in its body use a specific value forUSE_LEADING_END_BYTE. This is particularly useful when the application interacts with different endpoints, some that require a leadingENDbyte, and others that cannot handle multiple subsequentENDbytes. By using this context manager, the order of the creation of the instances can be modified without having to worry about the current value ofUSE_LEADING_END_BYTE. Example:with use_leading_end_byte(True): slip_socket = SlipSocket(sock) # Where sock is a previously created socket. # Calling `slip_socket.send_msg(message)` will send the encoded message # with both a leading and trailing END byte.
Added in version 0.7.0.
Note
The temporary value of
USE_LEADING_END_BYTEdoes not propagate to processes that are created or started in the body of the context manager.Warning
USE_LEADING_END_BYTEis a global setting. For that reason, the temporary value ofUSE_LEADING_END_BYTEis protected from modification by other threads. This means that inside the body of this context manager, you should e.g. not wait for notifications from another thread when that other thread also uses this context manager. As an example, the following will deadlock because the spawned thread will never enter the body of the context manager.def make_server(address, handler_class, event): with config.use_leading_end_byte(False): slip_server = SlipServer(address, handler_class) slip_server.handle_request() event.set() with config.use_leading_end_byte(True): event = threading.Event() server_thread = threading.Thread( target=self.server, args=(address, SlipRequestHandler, event), ) server_thread.start() event.wait() client = SlipSocket.make_client(address)
As a general rule, the body of the context manager should only contain statements that directly create
Driverand/orSlipWrapperinstances. The above example should be rewritten to something like:def make_server(address, handler_class, event): with config.use_leading_end_byte(False): slip_server = SlipServer(address, handler_class) slip_server.handle_request() event.set() event = threading.Event() server_thread = threading.Thread( target=self.server, args=(address, SlipRequestHandler, event), ) server_thread.start() event.wait() with config.use_leading_end_byte(True): client = SlipSocket.make_client(address)
- Parameters:
value (
bool) – The temporary value ofUSE_LEADING_END_BYTE.- Return type:
The config configuration object and its constants
END, ESC, ESC_END, and ESC_ESC,
as well as the use_leading_end_byte() context manager
can also be imported directly from the sliplib module.
Functions
The following are lower-level functions that should normally not be used directly.
- encode(msg)[source]
Encode a message (a byte sequence) into a SLIP-encoded packet.
This function replaces any
ENDorESCbyte in the message with their SLIP-escaped value.
- decode(packet)[source]
Retrieve the message from the SLIP-encoded packet.
This function replaces any escaped SLIP bytes with their original values
- Parameters:
packet (
bytes) – The SLIP-encoded message. Note that this must be exactly one complete packet, without any leading and/or trailingENDbytes. Thedecode()function does not provide any buffering for incomplete packages, nor does it provide support for decoding data with multiple packets.- Return type:
- Returns:
The decoded message
- Raises:
ProtocolError – if the packet contains an invalid byte sequence.
Classes
- class Driver[source]
Handle the SLIP-encoding and decoding of messages.
Class
Driveroffers the following methods:- send(message)[source]
Encode a message into a SLIP-encoded packet.
The message can be any arbitrary byte sequence.
- receive(data)[source]
Extract and buffer SLIP packets.
Processes data, which must be a bytes-like object, and extracts and buffers the SLIP packets contained therein.
A non-terminated SLIP packet in data is also buffered, and extended with the next call to
receive().- Parameters:
A bytes-like object to be processed.
An empty data parameter indicates that no more data will follow.
To accommodate iteration over byte sequences, an integer in the range(0, 256) is also accepted.
- Return type:
Changed in version 0.7: receive() no longer returns a list of decoded messages.
- get(*, block=True, timeout=None)[source]
Get the next decoded message.
Remove and decode a SLIP packet from the internal buffer, and return the resulting message. If block is
Trueand timeout isNone(the default), then this method blocks until a message is available. If timeout is a positive number, the blocking will last for at most timeout seconds, and the method will returnNoneif no message became available in that time. If block isFalse, the method returns immediately with either a message orNone.Note
block and timeout are keyword-only parameters.
- Parameters:
- Return type:
Optional[bytes]- Returns:
Noneif no message is available,a decoded SLIP message, or
an empty bytestring
b""if no further messages will come available.
- Raises:
ProtocolError – When the packet that contained the message had an invalid byte sequence.
Added in version 0.7.
Exceptions
- exception ProtocolError[source]
Exception to indicate that a SLIP protocol error has occurred.
This exception is raised when an attempt is made to decode a packet with invalid bytes or byte sequences. Invalid bytes are
ENDbytes or a trailingESCbyte. An invalid byte sequence is anESCbyte followed by any byte that is not anESC_ESCorESC_ENDbyte.The
ProtocolErrorcarries the invalid packet as the first (and only) element in itsargstuple.
Module slipwrapper
The slipwrapper module contains the abstract base class SlipWrapper
that is the basis for concrete implementations that combine the SLIP protocol with a byte stream.
The SlipWrapper class can also be imported directly from the sliplib package.
- ByteStream = TypeVar(ByteStream)
Invariant
TypeVar.ByteStreamrepresents a generic byte stream.
- class SlipWrapper(stream)[source]
Bases:
ABC,Generic[ByteStream]Abstract base class that provides a message based interface to a byte stream.
SlipWrappercombines aDriverinstance with a (generic) byte stream. TheSlipWrapperclass is an abstract base class. It offers the methodssend_msg()andrecv_msg()to send and receive single messages over the byte stream, but it does not of itself provide the means to interact with the stream.To interact with a concrete stream, a derived class must implement the methods
send_bytes()andrecv_bytes()to write to and read from the stream.A
SlipWrapperinstance can be iterated over. Each iteration will provide the next message that is received from the byte stream.Changed in version 0.5: Allow iteration over a
SlipWrapperinstance.To instantiate a
SlipWrapper, the user must provide an existing byte stream.- Parameters:
stream (
ByteStream) – The byte stream that will be wrapped.
Class
SlipWrapperoffers the following methods and attributes:- recv_msg()[source]
Receive a single message from the stream.
- Return type:
- Returns:
A SLIP-decoded message
- Raises:
ProtocolError – when a SLIP protocol error has been encountered. A subsequent call to
recv_msg()(after handling the exception) will return the message from the next packet.
- driver
The
SlipWrapper’sDriverinstance.
- stream
The wrapped
ByteStream.
In addition,
SlipWrapperrequires that derived classes implement the following methods:- abstractmethod recv_bytes()[source]
Receive data from the stream.
Derived classes must implement this method.
Note
The convention used within the
SlipWrapperclass is thatrecv_bytes()returns an empty bytes objectb""to indicate that the end of the byte stream has been reached and no further data will follow. Derived implementations must ensure that this convention is followed.- Return type:
- Returns:
The bytes received from the stream
Module slipstream
The slipstream module contains the class SlipStream.
The SlipStream class can also be imported directly from the sliplib package.
- protocol IOStream[source]
Bases:
ProtocolProtocol class for wrappable byte streams.
Any object that produces and consumes a byte stream and contains the two required methods can be used. Typically, an IOStream is a subclass of
io.RawIOBase,io.BufferedIOBase,io.FileIO, or similar classes, but this is not required.Classes that implement this protocol must have the following methods / attributes:
- class SlipStream(stream, chunk_size=8192)[source]
Bases:
SlipWrapper[IOStream]Class that wraps an IO stream with a
Driver.SlipStreamcombines aDriverinstance with a concrete byte stream. The byte stream must support the methodsread()andwrite(). To avoid conflicts and ambiguities caused by different newline conventions, streams that have anencodingattribute (such asio.StringIOobjects, or text files that are not opened in binary mode) are not accepted as a byte stream.The
SlipStreamclass has all the methods and attributes from its base classSlipWrapper. In addition, it directly exposes all methods and attributes of the containedstream, except for the following:read*()andwrite*(). These methods are not supported, because byte-oriented read and write operations would invalidate the internal state maintained bySlipStream.Similarly,
seek(),tell(), andtruncate()are not supported, because repositioning or truncating the stream would invalidate the internal state.raw(),detach()and other methods that provide access to or manipulate the stream’s internal data.
Instead of the
read*()andwrite*()methods aSlipStreamobject provides the methodrecv_msg()andsend_msg()to read and write SLIP-encoded messages.Deprecated since version 0.6: Direct access to the methods and attributes of the contained
streamwill be removed in version 1.0.To instantiate a
SlipStreamobject, the user must provide a pre-constructed open byte stream that is ready for reading and/or writing.- Parameters:
stream (
IOStream) – The byte stream that will be wrapped.chunk_size (
int) –The number of bytes to read per read operation. The default value for chunck_size is
io.DEFAULT_BUFFER_SIZE.Setting chunk_size is useful when the stream has a low bandwidth and/or bursty data (e.g. a serial port interface). In such cases it is useful to have a chunk_size value of 1, to avoid that the application hangs or becomes unresponsive.
Added in version 0.6: The chunk_size parameter.
A
SlipStreaminstance can e.g. be useful to read slip-encoded messages from a file:with open('/path/to/a/slip/encoded/file', mode='rb') as f: slip_file = SlipStream(f) for msg in slip_file: # Do something with the message
A
SlipStreaminstance has the following attributes and read-only properties in addition to the attributes offered by its base classSlipWrapper:- chunk_size
The number of bytes to read during each read operation.
Module slipsocket
The slipsocket module contains the class SlipSocket.
The SlipSocket class can also be imported directly from the sliplib package.
- TCPAddress
TCPAddress stands for either an IPv4 address, a
(host: str, port: int)tuple, or an IPv6 address, a(host: str, port: int, flowinfo: int, scope_id: int)tuple.
- class SlipSocket(sock)[source]
Bases:
SlipWrapper[socket]Class that wraps a TCP
socketwith aDriver.SlipSocketcombines aDriverinstance with asocket. TheSlipSocketclass has all the methods from its base classSlipWrapper. In addition, it directly exposes all methods and attributes of the containedsocket, except for the following:send*()andrecv*(). These methods are not supported, because byte-oriented send and receive operations would invalidate the internal state maintained bySlipSocket.Similarly,
makefile()is not supported, because byte- or line-oriented read and write operations would invalidate the internal state.share()(Windows only) anddup(). The internal state of theSlipSocketwould have to be duplicated and shared to make these methods meaningful. Because of the lack of a convincing use case for this, sharing and duplication is not supported.The
accept()method is delegated to the containedsocket.socket. The socket that is returned by the socket’saccept()method is automatically wrapped in aSlipSocketobject.
Instead of the
socket’ssend*()andrecv*()methods aSlipSocketprovides the methodsend_msg()andrecv_msg()to send and receive SLIP-encoded messages.Deprecated since version 0.6: Direct access to the methods and attributes of the contained
socket.socketother thanfamily,type, andprotowill be removed in version 1.0Only TCP sockets are supported. Using the SLIP protocol on UDP sockets is not supported for the following reasons:
UDP is datagram-based. Using SLIP with UDP therefore introduces ambiguity: should SLIP packets be allowed to span multiple UDP datagrams or not?
UDP does not guarantee delivery, and does not guarantee that datagrams are delivered in the correct order.
To instantiate a
SlipSocket, the user must provide a pre-constructed TCPsocket. An alternative way to instantiate s SlipSocket is to use the class methodcreate_connection().- Parameters:
sock (socket.socket) – An existing TCP socket, i.e. a socket with type
socket.SOCK_STREAM
Class
SlipSocketoffers the following methods in addition to the methods offered by its base classSlipWrapper:- accept()[source]
Accept an incoming connection.
Returns a
SlipSocketand a remoteTCPAddress. The returnedSlipSocketinherits the leading-end-byte behavior of theSlipSocketinstance on whichaccept()was called.- Return type:
(
SlipSocket,TCPAddress):- Returns:
A tuple with a
SlipSocketobject and the remote IP address.
- classmethod create_connection(address, timeout=None, source_address=None)[source]
Create a
SlipSocketconnection.This convenience method creates a connection to a socket at the specified address using the
socket.create_connection()function. The socket that is returned from that call is wrapped in aSlipSocketobject.- Parameters:
address (
TCPAddress) – The remote address.timeout (
Optional[float]) – Optional timeout value.source_address (
Optional[TCPAddress]) – Optional local address for the near socket.
- Return type:
- Returns:
A
SlipSocketinstance that is connected to the socket at the remote address.
See also
Note
The
accept()andcreate_connection()methods do not magically turn the socket at the remote address into a socket that uses the SLIP protocol. For the connection to work properly, the remote socket must already have been configured to use the SLIP protocol.The following commonly used
socket.socketmethods are exposed through aSlipSocketobject. These methods are simply delegated to the wrappedsocket.socketinstance. See the documentation forsocket.socketfor more information on these methods.- bind(address)[source]
Bind the
SlipSocketinstance to address.- Parameters:
address (
TCPAddress) – The address to bind to.- Return type:
- close()[source]
Close the
SlipSocketinstance.- Return type:
- connect(address)[source]
Connect the
SlipSocketinstance to a remote socket at address.- Parameters:
address (
TCPAddress) – The IP address of the remote socket.- Return type:
- connect_ex(address)[source]
Connect the
SlipSocketinstance to a remote socket at address.- Parameters:
address (
TCPAddress) – The IP address of the remote socket.- Return type:
- fileno()[source]
Get the socket’s file descriptor.
- Return type:
- Returns:
The wrapped socket’s file descriptor, or -1 on failure.
- getpeername()[source]
Get the IP address of the remote socket to which the
SlipSocketinstance is connected.- Return type:
- Returns:
The remote IP address.
- getsockname()[source]
Get the
SlipSocketinstance’s own address.- Return type:
- Returns:
The local IP address.
- getsockopt(level, optname, ...)[source]
Get the socket option from the embedded socket. See the documentation for
socket.socket.getsockopt()for more information about the parameters.
- listen(backlog=None)[source]
Enable a
SlipSocketserver to accept connections.
- setsockopt(level, optname, ...)[source]
Set the socket option of the embedded socket. See the documentation for
socket.socket.setsockopt()for more information about the parameters.
Since the wrapped socket is available as the
socketattribute, any othersocket.socketmethod can be invoked through that attribute.Warning
Avoid using
socket.socketmethods that affect the bytes that are sent or received through the socket. Doing so will invalidate the internal state of the enclosedDriverinstance, resulting in corrupted SLIP messages. In particular, do not use any of therecv*()orsend*()methods on thesocketattribute.A
SlipSocketinstance has the following attributes and read-only properties in addition to the attributes and properties offered by its base classSlipWrapper:- socket
The wrapped
socket. This is actually just an alias for thestreamattribute in the base class.
- property family
The wrapped socket’s address family.
Usually
socket.AF_INET(IPv4) orsocket.AF_INET6(IPv6).- Return type:
- property type
The wrapped socket’s type.
Always
socket.SOCK_STREAM.- Return type:
Module slipserver
The slipserver module contains the classes SlipRequestHandler and SlipServer,
that offer building blocks to create a SLIP server.
The SlipRequestHandler and SlipServer classes
can also be imported directly from the sliplib package.
- class SlipRequestHandler(request, client_address, server)[source]
Bases:
BaseRequestHandlerBase class for request handlers for SLIP-based communication.
This class is derived from
socketserver.BaseRequestHandlerfor the purpose of creating TCP server instances that can handle incoming SLIP-based connections.To do anything useful, a derived class must define its own
handle()method that usesself.requestto send and receive SLIP-encoded messages.Other methods can of course also be overridden if necessary.
To initialize the request handler, a request, client address, and server instance must be provided.
The type of the request parameter depends on the type of server that instantiates the request handler. If the server is a
SlipServer, then request is aSlipSocketinstance. Otherwise, it is a regularsocket, and the request handler wraps it in aSlipSocketinstance.Note
If request is not a
SlipSocketinstance (as will happen when server is not aSlipServerinstance), the behavior of the wrappedSlipSocketinstance with respect to sending a leadingENDbyte is determined by the value ofUSE_LEADING_END_BYTEat the time the request handler is initialized.- Parameters:
request (
Union[socket,SlipSocket]) – The socket that is connected to the remote party.client_address (
TCPAddress) – The remote TCP address.server (
TCPServer) – The server instance that instantiated this handler object.
- handle()[source]
Service the request. Must be defined by a derived class.
Note that in general it does not make sense to use a
SlipRequestHandlerobject to handle a single transmission, as is e.g. common with HTTP. The purpose of the SLIP protocol is to allow separation of messages in a continuous byte stream. As such, it is expected that thehandle()method of a derived class is capable of handling multiple SLIP messages, for example:def handle(self): while True: msg = self.request.recv_msg() if msg == b"": break # Do something with the message
- Return type:
- class SlipServer(server_address, handler_class, bind_and_activate=True)[source]
Bases:
TCPServerBase class for SlipSocket based server classes.
This is a convenience class, that offers a few enhancements over the regular
TCPServerfrom the standard library.It uses the supplied server_address to determine if it must use an IPv4 or IPv6 socket. The class
TCPServeron the other hand is hardcoded to use only IPv4 addresses, and must be subclassed in order to use IPv6 addresses.The socket that is passed to the handler_class instance is a
SlipSocketinstance with the same leading-end-byte behavior as the server socket. That avoids ambiguity whenconfig.USE_LEADING_END_BYTEmay have been modified.
- Parameters:
server_address (
TCPAddress) – The address on which the server listens.handler_class (
type[SlipRequestHandler]) – The class that will be instantiated to handle an incoming request.bind_and_activate (
bool) – Flag to indicate if the server must be bound and activated at creation time.
Module legacy
This module contains the legacy versions of the encode()
and decode() functions,
as they were defined prior to version 0.7.0.
Note that usage of these functions is discouraged.
When using these functions, the responsibility to properly split incoming data into SLIP packets,
and buffer incomplete SLIP packets resides completely with the user.
- encode(message)[source]
Encode a message into a SLIP packet.
Note that the SLIP packet will start with a leading
ENDbyte if the current value ofUSE_LEADING_END_BYTEis True. A leadingENDbyte will be absent if the current value ofUSE_LEADING_END_BYTEis False.
- decode(packet)[source]
Extract the original message from a SLIP packet.
- Parameters:
packet (
bytes) – the SLIP packet to decode.- Return type:
- Returns:
The decoded message.
- Raises:
ProtocolError – if the packet cannot be decoded.