Package: plib.io
The PLIB.IO package contains classes that encapsulate various forms
of client/server I/O channels.Note: PLIB.IO works with Python 2.7. If you are using
Python 3, see the PLIB3.IO package, available athttps://pypi.org/project/plib3.io.Thesetup.pyscript for PLIB.IO uses thesetuputilshelper module, which helps to automate away much of the
boilerplate in Python setup scripts. This module is available
as a separate release athttps://pypi.org/project/setuputils.The PLIB.IO Packages and ModulesThecommsub-package provides utilities for managing and
communicating with child threads and processes. This is shipped
as a separateplibsub-package fromio, so it can be
used independently of theiocode.Thebasesub-package contains base classes that implement
common basic functionality that is built on by the rest of PLIB.IO.Most of the remaining sub-packages fall into four main categories:Device Types:socketandserial. Each device type has
aBaseClientandBaseServerclass; thesockettype
also has aBaseRequestclass. These will usually not need to
be used directly; they are used by the I/O mode classes, and are
factored out so that each I/O mode sees the same API for a given
device type.Channel Types: Thecommsub-package contains classes that
implement the basic functionality of the three types of I/O
channels: clients, servers, and “persistent” (the latter is only
available with the async I/O mode–see below). Each I/O mode then
builds on these base classes to implement its specific channels.I/O modes:asyncandblocking(the latter does not just
mean synchronous: it includes a forking TCP socket server). Each
I/O mode has a client and server class for both device types, and
a request class for thesocketdevice type: the class names areSerialClient,SerialServer,SocketClient,SocketServer,
andBaseRequestHandler. Theasynctype also has “persistent”
classes, which support full-duplex asynchronous communication; these
are thePersistentSerial,PersistentSocket, andPersistentRequestHandlerclasses. Mixin versions of these classes
(class names withMixinat the end) are also provided, for use
if alternate data handling is desired (see next bullet), but it is
normally not necessary to use these “by hand”–see “automatic mixins”
below.Data Handling: the I/O mode classes given above include basic
data handling, but it isverybasic: the only way it can detect
that a “message” has been fully received is to detect a closed
channel. For some applications this is enough, but often more
sophisticated and robust data handling is needed. Thedatasub-package provides three mixin classes for this purpose,ShutdownReadWrite,TerminatorReadWriteandReadWrite.
The first of these detects the end of a received message by a
shutdown of the other end of the data channel, but keeps the channel
open to allow further writes (all the other classes default to
closing the channel when the other end closes). The other two
classes allow the detection of multiple “messages” in the data
stream, either by detecting a “terminator” string or by having
each message include its length at the beginning. These classes
also format outgoing messages the same way.There is also amixinssub-package containing classes that
are used as mixins by the other sub-packages, and aclassessub-package containing higher-level classes that use the API.
Finally, there is autilsmodule that implements the automatic
mixin functionality described below.Automatic MixinsTo derive your own client or server classes with alternate data
handling “by hand”, you would need to use the “mixin” versions of
the appropriate I/O mode classes, and splice the data handling
class into the middle of the base class list; for example:from plib.io.async import SerialClientMixin, SerialBase
from plib.io.data import TerminatorReadWrite

class AsyncSerialClientWithTerminator(SerialClientMixin,
    TerminatorReadWrite, SerialBase): passThis is a bit clumsy, but necessary since the read/write handling has
to bebeforethe client/server class in the MRO, butafterthe
base device type, for the cooperativesupercalls that underlie
the functionality to work properly. However, since the pattern is the
same in each case, it can be automated, and this has been done in theasyncandblockingsub-package namespaces, so that instead of
doing the above class construction “by hand”, you can just append a
suffix to your desired class name, thus:from plib.io.async import SerialClientWithTerminatorTheWithTerminatorsuffix (or, alternately,WithShutdownorWithReadWrite) will cause the equivalent of the above class
definition to occur on the fly, so that the resulting class appears
in theplib.io.asyncnamespace (of course theplib.io.blockingnamespace has the same capability).
Once this has happened the first time, however, the class definition
is stored in the appropriate namespace, so additional imports of the
same class name (in different modules of your application) will not
re-do the “on the fly” construction; they will just retrieve the
same class object that was previously constructed.The above machinery is also made available for use with your own custom
read/write handling classes; theasyncandblockingsub-packages
each export aget_readwrite_classfunction that does the same
on-the-fly class definition as above, but with your custom read/write
class instead of one of the built-in ones. All you have to do is pass
the function the name of your desired I/O class and your custom
read/write class object:from plib.io import async

class CustomReadWrite(object):
    # class definition

MyAsyncSerialClient = async.get_readwrite_class('SerialClient',
    CustomReadWrite)API Notes: One of the goals of this sub-package is to provide a
common, consistent API for all the different types of I/O, so that
switching one specific implementation of a certain functionality
for another can be done transparently to the rest of your application’s
code. Thus, all of the usable classes follow the same basic pattern of
mixing in the various pieces of functionality: from left to right
in a class’s MRO, one finds the type of endpoint (a client or
server mixin class, which may be specialized to the type of I/O),
the type of data formatting, if any (a mixin class from theReadWritemodule), and the type of I/O, including device type
(socket, serial port, etc.), mode (non-blocking/asynchronous vs.
blocking), and basic data handling. Also, each endpoint type has
a common API independent of the specific type of I/O and mode; a
client can always use theclient_communicatemethod to send
data to the server and receive a response; a server can always use
theserve_forevermethod to start itself; and all I/O objects
override the same methods to implement application-specific
functionality:process_data, to deal with data as it comes in,
andquery_done, to determine when the I/O channel should be
closed. (To see examples of all this in action, look at the test
suite intest_io.pyand the library module for it,io_testlib.py; the library module can use the same
mixin classes to implement test functionality forallof the
different mixes of I/O classes in the test suite.)InstallationTo install PLIB.IO, you can simply run:$ python setup.py installat a shell prompt from the directory into which you
unzipped the source tarball (the same directory that this
README file is in). This will install PLIB and then
run each of the post-install scripts in the scripts
directory.Example ProgramsPLIB.IO comes with example programs that illustrate key features
of the package. After installation, these can be found in the$PREFIX/share/plib/examplesdirectory. If you have a
POSIX system (Linux or Mac OSX), theplib-setup-examplespost-install script will install symlinks to the example
programs in the$PREFIX/bindirectory.