diff options
-rw-r--r-- | documentation/pyserial.rst | 4 | ||||
-rw-r--r-- | documentation/pyserial_api.rst | 74 | ||||
-rw-r--r-- | pyserial/examples/test_rawio.py | 57 | ||||
-rw-r--r-- | pyserial/serial/serialcli.py | 21 | ||||
-rw-r--r-- | pyserial/serial/serialjava.py | 21 | ||||
-rw-r--r-- | pyserial/serial/serialposix.py | 23 | ||||
-rw-r--r-- | pyserial/serial/serialutil.py | 107 | ||||
-rw-r--r-- | pyserial/serial/serialwin32.py | 19 | ||||
-rw-r--r-- | pyserial/setup.py | 6 |
9 files changed, 295 insertions, 37 deletions
diff --git a/documentation/pyserial.rst b/documentation/pyserial.rst index 1622498..2153f91 100644 --- a/documentation/pyserial.rst +++ b/documentation/pyserial.rst @@ -28,7 +28,7 @@ The homepage is at http://pyserial.sf.net. Features ======== * Same class based interface on all supported platforms. -* Access to the port settings through Python 2.2+ properties. +* Access to the port settings through Python 2.3+ properties. * Port numbering starts at zero, no need to know the port name in the user program. * Port string (device name) can be specified if access through numbering is @@ -45,7 +45,7 @@ Features Requirements ============ -* Python 2.2.3 or newer +* Python 2.3 or newer * ctypes extensions on Windows (is in standard library since Python 2.5+) * "Java Communications" (JavaComm) or compatible extension for Java/Jython diff --git a/documentation/pyserial_api.rst b/documentation/pyserial_api.rst index f5d5791..f5fa01a 100644 --- a/documentation/pyserial_api.rst +++ b/documentation/pyserial_api.rst @@ -56,7 +56,7 @@ Classes - ``timeout = None``: wait forever - ``timeout = 0``: non-blocking mode (return immediately on read) - - ``timeout = x``: set timeout to x seconds (float allowed) + - ``timeout = x``: set timeout to ``x`` seconds (float allowed) .. method:: open() @@ -67,9 +67,8 @@ Classes Close port immediately. - .. method:: setBaudrate(baudrate) - Change baud rate on an open port. + The following methods may rise :exc:`ValueError` when applied to a closed port. .. method:: inWaiting() @@ -159,11 +158,17 @@ Classes Read-only attributes: - .. attribute:: portstr + .. attribute:: name Device name. This is always the device name even if the port was opened by a number. (Read Only). + .. versionadded:: 2.5 + + .. attribute:: portstr + + :deprecated: use :attr:`name` instead + .. attribute:: BAUDRATES A list of valid baud rates. The list may be incomplete such that higher @@ -243,6 +248,19 @@ Classes Set software flow control state. +.. class:: RawSerial + + This class is only present when run with Python 2.6 and newer that prides + the module :mod:`io`. It shares the same interface with :class:`Serial` + with the difference that :meth:`read` and :meth:`write` work with + :class:`bytes`and :class:`bytearrays`. + + This also means that readline is borrowed from the :mod:`io` module and + lacks the ``eol`` parameter. + + .. versionadded:: 2.5 + + .. class:: FileLike An abstract file like class. It is used as base class for :class:`Serial`. @@ -296,6 +314,33 @@ Classes Raises NotImplementedError, needs to be overridden by subclass. + The following functions are implemented for compatibility with other + file-like objects, however serial ports are not seekable. + + + .. method:: seek(pos, whence=0) + + :exception IOError: always, as method is not supported on serial port + + .. versionadded:: 2.5 + + .. method:: tell() + + :exception IOError: always, as method is not supported on serial port + + .. versionadded:: 2.5 + + .. method:: truncate(self, n=None): + + :exception IOError: always, as method is not supported on serial port + + .. versionadded:: 2.5 + + .. method:: isatty() + + :exception IOError: always, as method is not supported on serial port + + .. versionadded:: 2.5 To be able to use the file like object as iterator for e.g. ``for line in Serial(0): ...`` usage: @@ -422,3 +467,24 @@ Default control characters for software flow control. .. data:: XON .. data:: XOFF + +Version + +.. data:: VERSION + + A string indicating the pySerial version, such as ``2.5``. + +Functions: + +.. function:: device(number) + + :param number: Port number. + :return: String containing device name. + :deprecated: Use device names directly. + + Convert a port number to a platform dependent device name. Unfortunately + this does not work well for all platforms; e.g. some may miss USB-Serial + converters and enumerate only internal serial ports. + + The conversion may be made off-line, that is, there is no guarantee that + the returned device name really exists on the system. diff --git a/pyserial/examples/test_rawio.py b/pyserial/examples/test_rawio.py new file mode 100644 index 0000000..674934f --- /dev/null +++ b/pyserial/examples/test_rawio.py @@ -0,0 +1,57 @@ +##! /usr/bin/env python +# Python Serial Port Extension for Win32, Linux, BSD, Jython +# see __init__.py +# +# (C) 2001-2008 Chris Liechti <cliechti@gmx.net> +# this is distributed under a free software license, see license.txt + +"""\ +Some tests for the serial module. +Part of pyserial (http://pyserial.sf.net) (C)2001-2009 cliechti@gmx.net + +Intended to be run on different platforms, to ensure portability of +the code. + +This modules contains test for RawSerial. This only works on Python 2.6+ with +the io library. + +For all these tests a simple hardware is required. +Loopback HW adapter: +Shortcut these pin pairs: + TX <-> RX + RTS <-> CTS + DTR <-> DSR + +On a 9 pole DSUB these are the pins (2-3) (4-6) (7-8) +""" + +import unittest, threading, time +import serial + +# on which port should the tests be performed: +PORT=0 + +class Test_RawSerial(unittest.TestCase): + + def setUp(self): + self.s = serial.RawSerial(PORT) + + def tearDown(self): + self.s.close() + + def test_hello(self): + self.s.write(bytes("hello")) + hello = self.s.read(5) + #~ print hello + self.failUnlessEqual(hello, bytes("hello")) + + +if __name__ == '__main__': + import sys + sys.stdout.write(__doc__) + if len(sys.argv) > 1: + PORT = sys.argv[1] + sys.stdout.write("Testing port: %r\n" % PORT) + sys.argv[1:] = ['-v'] + # When this module is executed from the command-line, it runs all its tests + unittest.main() diff --git a/pyserial/serial/serialcli.py b/pyserial/serial/serialcli.py index 6961bf6..5360cb3 100644 --- a/pyserial/serial/serialcli.py +++ b/pyserial/serial/serialcli.py @@ -21,7 +21,7 @@ sab = System.Array[System.Byte] def as_byte_array(string): return sab([ord(x) for x in string]) -class Serial(SerialBase): +class IronSerial(SerialBase): """Serial port implemenation for .NET/Mono.""" BAUDRATES = (50,75,110,134,150,200,300,600,1200,1800,2400,4800,9600, @@ -145,7 +145,7 @@ class Serial(SerialBase): if not self._port_handle: raise portNotOpenError return self._port_handle.BytesToRead - def read(self, size=1): + def _read(self, size=1): """Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read.""" @@ -162,7 +162,7 @@ class Serial(SerialBase): size -= 1 return ''.join(data) - def write(self, data): + def _write(self, data): """Output the given string over the serial port.""" if not self._port_handle: raise portNotOpenError if not isinstance(data, str): @@ -173,6 +173,7 @@ class Serial(SerialBase): self._port_handle.Write(as_byte_array(data), 0, len(data)) except System.TimeoutException, e: raise writeTimeoutError + return len(data) def flushInput(self): """Clear input buffer, discarding all that is in the buffer.""" @@ -230,6 +231,20 @@ class Serial(SerialBase): return self._port_handle.CDHolding # - - platform specific - - - - + # none + + +# assemble Serial class with the platform specifc implementation and the base +# for file-like behavior +class Serial(IronSerial, FileLike): + pass + +# for Python 2.6 and newer, that provide the new I/O library, implement a +# RawSerial object that plays nice with it. +if support_io_module: + class RawSerial(IronSerial, RawSerialBase): + pass + #Nur Testfunktion!! if __name__ == '__main__': diff --git a/pyserial/serial/serialjava.py b/pyserial/serial/serialjava.py index 2dbaad6..d7e68c4 100644 --- a/pyserial/serial/serialjava.py +++ b/pyserial/serial/serialjava.py @@ -46,7 +46,8 @@ def device(portnumber): ports.append(el) return ports[portnumber].getName() -class Serial(SerialBase): + +class JavaSerial(SerialBase): """Serial port class, implemented with Java Communications API and thus usable with jython and the appropriate java extension.""" @@ -144,7 +145,7 @@ class Serial(SerialBase): if not self.sPort: raise portNotOpenError return self._instream.available() - def read(self, size=1): + def _read(self, size=1): """Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read.""" @@ -160,10 +161,11 @@ class Serial(SerialBase): read = read + chr(x) return read - def write(self, data): + def _write(self, data): """Output the given string over the serial port.""" if not self.sPort: raise portNotOpenError self._outstream.write(data) + return len(data) def flushInput(self): """Clear input buffer, discarding all that is in the buffer.""" @@ -190,7 +192,7 @@ class Serial(SerialBase): """Set terminal status line: Request To Send""" if not self.sPort: raise portNotOpenError self.sPort.setRTS(level) - + def setDTR(self, level=1): """Set terminal status line: Data Terminal Ready""" if not self.sPort: raise portNotOpenError @@ -217,6 +219,17 @@ class Serial(SerialBase): self.sPort.isCD() +# assemble Serial class with the platform specifc implementation and the base +# for file-like behavior +class Serial(JaveSerial, FileLike): + pass + +# for Python 2.6 and newer, that provide the new I/O library, implement a +# RawSerial object that plays nice with it. +if support_io_module: + class RawSerial(JavaSerial, RawSerialBase): + pass + if __name__ == '__main__': s = Serial(0, diff --git a/pyserial/serial/serialposix.py b/pyserial/serial/serialposix.py index 0b87090..2a989fd 100644 --- a/pyserial/serial/serialposix.py +++ b/pyserial/serial/serialposix.py @@ -265,7 +265,7 @@ TIOCSBRK = hasattr(TERMIOS, 'TIOCSBRK') and TERMIOS.TIOCSBRK or 0x5427 TIOCCBRK = hasattr(TERMIOS, 'TIOCCBRK') and TERMIOS.TIOCCBRK or 0x5428 -class Serial(SerialBase): +class PosixSerial(SerialBase): """Serial port class POSIX implementation. Serial port configuration is done with termios and fcntl. Runs on Linux and many other Un*x like systems.""" @@ -273,9 +273,9 @@ class Serial(SerialBase): def open(self): """Open port with current settings. This may throw a SerialException if the port cannot be opened.""" + self.fd = None if self._port is None: raise SerialException("Port must be configured before it can be used.") - self.fd = None # open try: self.fd = os.open(self.portstr, os.O_RDWR|os.O_NOCTTY|os.O_NONBLOCK) @@ -434,7 +434,7 @@ class Serial(SerialBase): s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str) return struct.unpack('I',s)[0] - def read(self, size=1): + def _read(self, size=1): """Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read.""" @@ -453,9 +453,10 @@ class Serial(SerialBase): break # early abort on timeout return read - def write(self, data): + def _write(self, data): """Output the given string over the serial port.""" if self.fd is None: raise portNotOpenError + #~ if not isinstance(port, basestring): if not isinstance(data, str): raise TypeError('expected str, got %s' % type(data)) t = len(data) @@ -476,6 +477,7 @@ class Serial(SerialBase): except OSError,v: if v.errno != errno.EAGAIN: raise + return len(data) def flush(self): """Flush of file like objects. In this case, wait until all data @@ -568,6 +570,19 @@ class Serial(SerialBase): if self.fd is None: raise portNotOpenError return self.fd + +# assemble Serial class with the platform specifc implementation and the base +# for file-like behavior +class Serial(PosixSerial, FileLike): + pass + +# for Python 2.6 and newer, that provide the new I/O library, implement a +# RawSerial object that plays nice with it. +if support_io_module: + class RawSerial(PosixSerial, RawSerialBase): + pass + + if __name__ == '__main__': s = Serial(0, baudrate=19200, # baud rate diff --git a/pyserial/serial/serialutil.py b/pyserial/serial/serialutil.py index 71bafbf..da9a936 100644 --- a/pyserial/serial/serialutil.py +++ b/pyserial/serial/serialutil.py @@ -24,13 +24,14 @@ XOFF = chr(19) class SerialException(Exception): """Base class for serial port related exceptions.""" -portNotOpenError = SerialException('Port not open') +portNotOpenError = ValueError('Attempting to use a port that is not open') class SerialTimeoutException(SerialException): """Write timeouts give an exception""" writeTimeoutError = SerialTimeoutException("Write timeout") + class FileLike(object): """An abstract file like class. @@ -45,8 +46,31 @@ class FileLike(object): refuses to work (it raises an exception in this case)! """ - def read(self, size): raise NotImplementedError - def write(self, s): raise NotImplementedError + def __init__(self): + self.closed = True + + def close(self): + self.closed = True + + # so that ports are closed when objects are discarded + def __del__(self): + """Destructor. Calls close().""" + # The try/except block is in case this is called at program + # exit time, when it's possible that globals have already been + # deleted, and then the close() call might fail. Since + # there's nothing we can do about such failures and they annoy + # the end users, we suppress the traceback. + try: + self.close() + except: + pass + + # read and write directly use the platform dependent implementation + def read(self, size=1): + return self._read(size) + + def write(self, data): + return self._write(data) def readline(self, size=None, eol='\n'): """read a line which is terminated with end-of-line (eol) character @@ -101,15 +125,32 @@ class FileLike(object): def __iter__(self): return self + # other functions of file-likes - not used by pySerial + + #~ readinto(b) + + def seek(self, pos, whence=0): + raise IOError("file is not seekable") -class SerialBase(FileLike): + def tell(self): + raise IOError("file is not seekable") + + def truncate(self, n=None): + raise IOError("file is not seekable") + + def isatty(self): + return False + + +class SerialBase(object): """Serial port base class. Provides __init__ function and properties to get/set port settings.""" # default values, may be overridden in subclasses that do not support all values - BAUDRATES = (50,75,110,134,150,200,300,600,1200,1800,2400,4800,9600, - 19200,38400,57600,115200,230400,460800,500000,576000,921600, - 1000000,1152000,1500000,2000000,2500000,3000000,3500000,4000000) + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, + 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, + 3000000, 3500000, 4000000) BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS) PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE) STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO) @@ -194,13 +235,14 @@ class SerialBase(FileLike): was_open = self._isOpen if was_open: self.close() if port is not None: - if type(port) in [type(''), type(u'')]: # strings are taken directly + if isinstance(port, basestring): self.portstr = port else: self.portstr = self.makeDeviceName(port) else: self.portstr = None self._port = port + self.name = self.portstr if was_open: self.open() def getPort(self): @@ -384,11 +426,48 @@ class SerialBase(FileLike): self.dsrdtr, ) +# for Python 2.6 and newer, that provide the new I/O library, implement a +# RawSerial object that plays nice with it. +try: + import io +except ImportError: + support_io_module = False +else: + support_io_module = True + + class RawSerialBase(io.RawIOBase): + def readable(self): return True + def writable(self): return True + def readinto(self, b): + data = self._read(len(b)) + n = len(data) + try: + b[:n] = data + except TypeError, err: + import array + if not isinstance(b, array.array): + raise err + b[:n] = array.array(b'b', data) + return n + + def write(self, b): + if self.closed: + raise ValueError("write to closed file") + if isinstance(b, unicode): + raise TypeError("can't write unicode to binary stream") + n = len(b) + if n == 0: + return 0 + self._write(b) + + + if __name__ == '__main__': + import sys s = SerialBase() - sys.stdio.write('port name: %s\n' % s.portstr) - sys.stdio.write('baud rates: %s\n' % s.getSupportedBaudrates()) - sys.stdio.write('byte sizes: %s\n' % s.getSupportedByteSizes()) - sys.stdio.write('parities: %s\n' % s.getSupportedParities()) - sys.stdio.write('stop bits: %s\n' % s.getSupportedStopbits()) - sys.stdio.write('%s\n' % s) + sys.stdout.write('port name: %s\n' % s.portstr) + sys.stdout.write('baud rates: %s\n' % s.getSupportedBaudrates()) + sys.stdout.write('byte sizes: %s\n' % s.getSupportedByteSizes()) + sys.stdout.write('parities: %s\n' % s.getSupportedParities()) + sys.stdout.write('stop bits: %s\n' % s.getSupportedStopbits()) + sys.stdout.write('%s\n' % s) diff --git a/pyserial/serial/serialwin32.py b/pyserial/serial/serialwin32.py index 8c881b5..7df6236 100644 --- a/pyserial/serial/serialwin32.py +++ b/pyserial/serial/serialwin32.py @@ -17,7 +17,7 @@ def device(portnum): """Turn a port number into a device name""" return 'COM%d' % (portnum+1) #numbers are transformed to a string -class Serial(SerialBase): +class Win32Serial(SerialBase): """Serial port implementation for Win32 based on ctypes.""" BAUDRATES = (50,75,110,134,150,200,300,600,1200,1800,2400,4800,9600, @@ -196,7 +196,7 @@ class Serial(SerialBase): raise SerialException('call to ClearCommError failed') return comstat.cbInQue - def read(self, size=1): + def _read(self, size=1): """Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read.""" @@ -231,7 +231,7 @@ class Serial(SerialBase): read = '' return read - def write(self, data): + def _write(self, data): """Output the given string over the serial port.""" if not self.hComPort: raise portNotOpenError if not isinstance(data, str): @@ -247,6 +247,7 @@ class Serial(SerialBase): err = win32.GetOverlappedResult(self.hComPort, self._overlappedWrite, ctypes.byref(n), True) if n.value != len(data): raise writeTimeoutError + return n.value def flushInput(self): @@ -339,6 +340,18 @@ class Serial(SerialBase): raise SerialException('call to ClearCommError failed') return comstat.cbOutQue +# assemble Serial class with the platform specifc implementation and the base +# for file-like behavior +class Serial(Win32Serial, FileLike): + pass + +# for Python 2.6 and newer, that provide the new I/O library, implement a +# RawSerial object that plays nice with it. +if support_io_module: + class RawSerial(Win32Serial, RawSerialBase): + pass + + # Nur Testfunktion!! if __name__ == '__main__': diff --git a/pyserial/setup.py b/pyserial/setup.py index f9c41ef..4198f32 100644 --- a/pyserial/setup.py +++ b/pyserial/setup.py @@ -14,9 +14,9 @@ except ImportError: raise ImportError("build_py_2to3 not found in distutils - it is required for Python 3.x") from distutils.command.build_py import build_py -if sys.version < '2.2.3': - # distutils that old can't cope with the "classifiers" or - # "download_url" keywords and True/False constants are missing +if sys.version < '2.3': + # distutils that old can't cope with the "classifiers" or "download_url" + # keywords and True/False constants and basestring are missing raise ValueError("Sorry Python versions older than 2.2.3 are no longer" "supported - check http://pyserial.sf.net for older " "releases or upgrade your Python installation.") |