pytty

view src/__init__.py @ 35:9ecd879bcd15

Updated copyright for 2014 - Happy New Year!
author Arc Riley <arcriley@gmail.com>
date Wed, 01 Jan 2014 05:06:11 +0000
parents 79b42fa3c17b
children
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,2013,2014 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.4'
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)