summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Liechti <cliechti@gmx.net>2016-08-30 23:39:15 +0200
committerChris Liechti <cliechti@gmx.net>2016-08-30 23:39:15 +0200
commit935a262a779020442ef1f7f306bc98165a126414 (patch)
tree2be75b1998cdd28127a41fd1e0a5220e4a567d7a
parent6dc58e8f292c761038cd8bbb20e9ec187e00a423 (diff)
downloadpyserial-git-935a262a779020442ef1f7f306bc98165a126414.tar.gz
posix: abstraction of timeout via class
- preparation for use of monotonic clock support
-rw-r--r--serial/serialposix.py31
-rw-r--r--serial/serialutil.py29
2 files changed, 42 insertions, 18 deletions
diff --git a/serial/serialposix.py b/serial/serialposix.py
index e7bbc13..23e19c4 100644
--- a/serial/serialposix.py
+++ b/serial/serialposix.py
@@ -37,7 +37,8 @@ import termios
import time
import serial
-from serial.serialutil import SerialBase, SerialException, to_bytes, portNotOpenError, writeTimeoutError
+from serial.serialutil import SerialBase, SerialException, to_bytes, \
+ portNotOpenError, writeTimeoutError, Timeout
class PlatformSpecificBase(object):
@@ -450,11 +451,10 @@ class Serial(SerialBase, PlatformSpecific):
if not self.is_open:
raise portNotOpenError
read = bytearray()
- timeout = self._timeout
+ timeout = Timeout(self._timeout)
while len(read) < size:
try:
- start_time = time.time()
- ready, _, _ = select.select([self.fd, self.pipe_abort_read_r], [], [], timeout)
+ ready, _, _ = select.select([self.fd, self.pipe_abort_read_r], [], [], timeout.time_left())
if self.pipe_abort_read_r in ready:
os.read(self.pipe_abort_read_r, 1000)
break
@@ -486,10 +486,8 @@ class Serial(SerialBase, PlatformSpecific):
# see also http://www.python.org/dev/peps/pep-3151/#select
if e[0] != errno.EAGAIN:
raise SerialException('read failed: {}'.format(e))
- if timeout is not None:
- timeout -= time.time() - start_time
- if timeout <= 0:
- break
+ if timeout.expired():
+ break
return bytes(read)
def cancel_read(self):
@@ -504,30 +502,27 @@ class Serial(SerialBase, PlatformSpecific):
raise portNotOpenError
d = to_bytes(data)
tx_len = len(d)
- timeout = self._write_timeout
- if timeout and timeout > 0: # Avoid comparing None with zero
- timeout += time.time()
+ timeout = Timeout(self._write_timeout)
while tx_len > 0:
try:
n = os.write(self.fd, d)
- if timeout == 0:
+ if timeout.is_non_blocking:
# Zero timeout indicates non-blocking - simply return the
# number of bytes of data actually written
return n
- elif timeout and timeout > 0: # Avoid comparing None with zero
+ elif not timeout.is_infinite:
# when timeout is set, use select to wait for being ready
# with the time left as timeout
- timeleft = timeout - time.time()
- if timeleft < 0:
+ if timeout.expired():
raise writeTimeoutError
- abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], timeleft)
+ abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], timeout.time_left())
if abort:
os.read(self.pipe_abort_write_r, 1000)
break
if not ready:
raise writeTimeoutError
else:
- assert timeout is None
+ assert timeout.time_left() is None
# wait for write operation
abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], None)
if abort:
@@ -543,7 +538,7 @@ class Serial(SerialBase, PlatformSpecific):
if v.errno != errno.EAGAIN:
raise SerialException('write failed: {}'.format(v))
# still calculate and check timeout
- if timeout and timeout - time.time() < 0:
+ if timeout.expired():
raise writeTimeoutError
return len(data)
diff --git a/serial/serialutil.py b/serial/serialutil.py
index 474b4c2..0b2b9a3 100644
--- a/serial/serialutil.py
+++ b/serial/serialutil.py
@@ -104,6 +104,35 @@ writeTimeoutError = SerialTimeoutException('Write timeout')
portNotOpenError = SerialException('Attempting to use a port that is not open')
+class Timeout(object):
+ """\
+ Timeout implementation with time.time(). This is compatible with all
+ Python versions but has issues if the clock is adjusted while the
+ timeout is running.
+ """
+ def __init__(self, duration):
+ """Initialize a timeout with given duration"""
+ self.is_infinite = (duration is None)
+ self.is_non_blocking = (duration == 0)
+ if duration is not None:
+ self.target_time = time.time() + duration
+ else:
+ self.target_time = None
+
+ def expired(self):
+ """Return a boolean if the timeout has expired"""
+ return self.target_time is not None and time.time() > self.target_time
+
+ def time_left(self):
+ """Return how many seconds are left until the timeout expires"""
+ if self.is_non_blocking:
+ return 0
+ elif self.is_infinite:
+ return None
+ else:
+ return max(0, self.target_time - time.time())
+
+
class SerialBase(io.RawIOBase):
"""\
Serial port base class. Provides __init__ function and properties to