pytty

view src/__init__.py @ 31:34b161a8e076

Now supports whatever baud rates are offered by the termios module, which differs from platform to platform. This uses dict comprehension, so PyTTY no longer supports Python 2.6.
author Arc Riley <arcriley@gmail.com>
date Fri, 20 Jul 2012 18:21:54 -0400
parents 873320419f15
children 6dc760b9f35e
line source
1 # -*- coding: utf-8 -*-
3 '''Python serial access package
5 This package provides easy access to TTY devices from Python.
6 '''
8 __credits__ = '''Copyright (C) 2010,2011,2012 Arc Riley
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published
12 by the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public License
21 along with this program; if not, see http://www.gnu.org/licenses
22 '''
24 __author__ = '\n'.join((
25 'Arc Riley <arcriley@gmail.com>',
26 'Chris Koepke',
27 ))
29 __version__ = '0.3'
31 import io
33 class TTY (io.BufferedRWPair) :
34 '''TTY io class
36 This is a subclass of io.BufferedRWPair from the Python standard library
37 which opens a tty device, sets nonblock mode on the device, and allows the
38 user to change baud rate, flow control, and other settings often available
39 to tty devices.
41 >>> import pty
42 >>> ptys = pty.openpty() # create two connected ptys for testing
43 >>> master = pytty.TTY(ptys[0])
44 >>> slave = pytty.TTY(ptys[1])
45 >>> slave.write('Greetings, Master.'.encode()) == 18
46 True
47 >>> slave.flush()
48 >>> print(master.read().decode())
49 Greetings, Master.
50 '''
52 # This is intended to be overridden by some subclasses
53 _iobase = io.FileIO
55 # Generator to compile our baudrate/integer lookup dicts
56 def _baudrates() :
57 import termios
58 for member in termios.__dict__ :
59 if member[0] == 'B' and member[1:].isdigit() :
60 yield (int(member[1:]), termios.__dict__[member])
62 # {termios.B9600 : 9600, termios.B19200 : 19200, etc}
63 _baud2int = {b[1] : b[0] for b in _baudrates()}
65 # {9600 : termios.B9600, 19200 : termios.B19200, etc}
66 _int2baud = {b[0] : b[1] for b in _baudrates()}
68 def __init__ (self, name) :
69 from fcntl import fcntl, F_SETFL, F_GETFL
70 from os import O_NONBLOCK
72 reader = self._iobase(name, 'r')
74 # ensure this is actually a tty device
75 if not reader.isatty() :
76 raise IOError('%s is not a tty device' % name)
78 # set non-blocking mode on the reader
79 self._fdo = reader.fileno()
80 fcntl(self._fdo, F_SETFL, (fcntl(self._fdo, F_GETFL) | O_NONBLOCK))
82 # open a separate writer device
83 writer = self._iobase(name, 'w')
85 # set non-blocking mode on the reader
86 self._fdi = reader.fileno()
87 fcntl(self._fdi, F_SETFL, (fcntl(self._fdi, F_GETFL) | O_NONBLOCK))
89 # initialize self with BufferedRWPair
90 super(TTY, self).__init__(reader, writer)
92 def __repr__(self) :
93 return '<pytty.TTY (%s)>' % str(self)
95 def __str__(self) :
96 return '%s %i%s%i' % (self.baud, self.bits, self.parity, self.stops)
98 @property
99 def baud (self) :
100 '''Baud rate
102 Value must be supported by the tty device and the termios module.'''
103 import termios
104 try :
105 return self._baud2int[termios.tcgetattr(self._fdi)[4]]
106 except KeyError :
107 raise IOError('Current baud rate not supported by termios.')
109 @baud.setter
110 def baud (self, value) :
111 import termios
112 try :
113 tv = self._int2baud[value]
114 except KeyError :
115 raise IOError('Baud rate not supported by termios.')
116 tci = termios.tcgetattr(self._fdi)
117 tco = termios.tcgetattr(self._fdo)
118 tci[4], tci[5], tco[4], tco[5] = (tv,)*4
119 termios.tcsetattr(self._fdi, termios.TCSANOW, tci)
120 termios.tcsetattr(self._fdo, termios.TCSANOW, tco)
122 @property
123 def bits (self) :
124 '''Number of bits per byte
126 This property determines how many bits are in a byte, between 5 and 8.
127 '''
128 import termios
129 cflag = termios.tcgetattr(self._fdi)[2]
130 return ((cflag & termios.CS8 and 8) or (cflag & termios.CS7 and 7) or
131 (cflag & termios.CS6 and 6) or (cflag & termios.CS5 and 5))
133 @bits.setter
134 def bits (self, value) :
135 import termios
136 if value < 5 or value > 8 :
137 raise IOError('Byte size must be between 5 and 8 bits.')
138 cs = (termios.CS5, termios.CS6, termios.CS7, termios.CS8)[value-5]
139 for fd in (self._fdi, self._fdo) :
140 tc = termios.tcgetattr(fd)
141 tc[2] &= cs
142 termios.tcsetattr(fd, termios.TCSANOW, tc)
144 @property
145 def parity (self) :
146 '''Parity bit
148 This may be set to 'N' (none), 'E' (even), or 'O' (odd).
149 '''
150 import termios
151 cflag = termios.tcgetattr(self._fdi)[2]
152 return ((('N', 'N'), ('E', 'O'))
153 [cflag & termios.PARENB and 1][cflag & termios.PARODD and 1])
155 @parity.setter
156 def parity (self, value) :
157 import termios
158 for fd in (self._fdi, self._fdo) :
159 tc = termios.tcgetattr(fd)
160 if value in 'Nn' :
161 tc[2] &= ~termios.PARENB
162 else :
163 tc[2] |= termios.PARENB
164 if value in 'Ee' :
165 tc[2] &= ~termios.PARODD
166 elif value in 'Oo' :
167 tc[2] |= termios.PARODD
168 else :
169 raise IOError("Parity must be 'N', 'E', or 'O'.")
170 termios.tcsetattr(fd, termios.TCSANOW, tc)
172 @property
173 def stops (self) :
174 '''Number of stop bits
176 How many stop bits follow a byte, either 1 or 2.
177 '''
178 import termios
179 cflag = termios.tcgetattr(self._fdi)[2]
180 return (1, 2)[termios.tcgetattr(self._fdi)[2] & termios.CSTOPB and 1]
182 @stops.setter
183 def stops (self, value) :
184 import termios
185 for fd in (self._fdi, self._fdo) :
186 tc = termios.tcgetattr(fd)
187 if value == 1 :
188 tc[2] &= ~termios.CSTOPB
189 elif value == 2 :
190 tc[2] &= termios.CSTOPB
191 else :
192 raise IOError('Stop bits must be either 1 or 2.')
193 termios.tcsetattr(fd, termios.TCSANOW, tc)
195 # Clean up package namespace
196 del(io)