diff options
author | Rob Gaddi <rgaddi@highlandtechnology.com> | 2017-02-24 11:51:37 -0800 |
---|---|---|
committer | Rob Gaddi <rgaddi@highlandtechnology.com> | 2017-02-24 11:51:37 -0800 |
commit | 06db381f702e8ea0700a2c6bb262ef4bad3e4233 (patch) | |
tree | ef9ae1981c4efbd503a1cb39fd63320605504925 | |
parent | fd59ad4446f754d2f2d3eacd82177b77cd4eedde (diff) | |
parent | f956057b989e7575492b792e9ee476b19bf4fbc3 (diff) | |
download | pyserial-git-06db381f702e8ea0700a2c6bb262ef4bad3e4233.tar.gz |
Merge branch 'master' into v3.2.1-rg
-rw-r--r-- | CHANGES.rst | 22 | ||||
-rw-r--r-- | README.rst | 2 | ||||
-rw-r--r-- | documentation/appendix.rst | 8 | ||||
-rw-r--r-- | documentation/examples.rst | 4 | ||||
-rw-r--r-- | documentation/pyserial_api.rst | 8 | ||||
-rw-r--r-- | documentation/shortintro.rst | 4 | ||||
-rw-r--r-- | documentation/tools.rst | 2 | ||||
-rw-r--r-- | documentation/url_handlers.rst | 11 | ||||
-rwxr-xr-x | examples/rfc2217_server.py | 4 | ||||
-rwxr-xr-x | examples/tcp_serial_redirect.py | 11 | ||||
-rw-r--r-- | serial/rfc2217.py | 15 | ||||
-rw-r--r-- | serial/serialposix.py | 4 | ||||
-rw-r--r-- | serial/serialutil.py | 3 | ||||
-rw-r--r-- | serial/serialwin32.py | 4 | ||||
-rw-r--r-- | serial/tools/list_ports_windows.py | 14 | ||||
-rw-r--r-- | serial/tools/miniterm.py | 11 | ||||
-rw-r--r-- | serial/urlhandler/protocol_socket.py | 86 |
17 files changed, 148 insertions, 65 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 03e0179..096007e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -118,8 +118,8 @@ Bugfixes (win32): - don't recreate overlapped structures and events on each read/write. - don't set unneeded event masks. -- dont use DOS device names for ports > 9. -- remove send timeout (its not used in the linux impl. anyway). +- don't use DOS device names for ports > 9. +- remove send timeout (it's not used in the linux impl. anyway). Version 1.21 30 Sep 2003 @@ -199,7 +199,7 @@ Bugfixes (posix): - ``fd == 0`` fix from Vsevolod Lobko - netbsd fixes from Erik Lindgren -- Dynamicaly lookup baudrates and some cleanups +- Dynamically lookup baudrates and some cleanups Bugfixes (examples): @@ -234,7 +234,7 @@ Bugfixes (win32): New Features: - ``dsrdtr`` setting to enable/disable DSR/DTR flow control independently - from the ``rtscts`` setting. (Currenly Win32 only, ignored on other + from the ``rtscts`` setting. (Currently Win32 only, ignored on other platforms) @@ -379,7 +379,7 @@ New Features: affects Win32 as on other platforms, that setting was ignored anyway. - Improved xreadlines, it is now a generator function that yields lines as they are received (previously it called readlines which would only return all - lines read after a read-timeout). However xreadlines is deprecated an not + lines read after a read-timeout). However xreadlines is deprecated and not available when the io module is used. Use ``for line in Serial(...):`` instead. @@ -405,13 +405,13 @@ New Features: - Moved some of the examples to serial.tools so that they can be used with ``python -m`` - serial port enumeration now included as ``serial.tools.list_ports`` -- URL handers for ``serial_for_url`` are now imported dynamically. This allows +- URL handlers for ``serial_for_url`` are now imported dynamically. This allows to add protocols w/o editing files. The list ``serial.protocol_handler_packages`` can be used to add or remove user packages with protocol handlers (see docs for details). - new URL type: hwgrep://<regexp> uses list_ports module to search for ports by their description -- serveral internal changes to improve Python 3.x compatibility (setup.py, +- several internal changes to improve Python 3.x compatibility (setup.py, use of absolute imports and more) Bugfixes: @@ -681,3 +681,11 @@ Bugfixes (win32): - [#144] Use Unicode API for list_ports - [#145] list_ports_windows: support devices with only VID - [#162] Write in non-blocking mode returns incorrect value on windows + + +Version 3.2.x 2017-xx-xx +-------------------------- + +Bugfixes (win32): + +- [#194] spurious write fails with ERROR_SUCCESS @@ -19,7 +19,7 @@ Documentation ============= For API documentation, usage and examples see files in the "documentation" directory. The ".rst" files can be read in any text editor or being converted to -HTML or PDF using Sphinx_. A HTML version is online at +HTML or PDF using Sphinx_. An HTML version is online at https://pythonhosted.org/pyserial/ Examples diff --git a/documentation/appendix.rst b/documentation/appendix.rst index 8bc2c1a..80ade6d 100644 --- a/documentation/appendix.rst +++ b/documentation/appendix.rst @@ -91,6 +91,14 @@ User supplied URL handlers so running ``sudo adduser $USER dialout`` (and logging-out and -in) enables the user to access the port. +Support for Python 2.6 or earlier + Support for older Python releases than 2.7 will not return to pySerial 3.x. + Python 2.7 is now many years old (released 2010). If you insist on using + Python 2.6 or earlier, it is recommend to use pySerial `2.7`_ + (or any 2.x version). + +.. _`2.7`: https://pypi.python.org/pypi/pyserial/2.7 + Related software ================ diff --git a/documentation/examples.rst b/documentation/examples.rst index 019ffc1..787fd00 100644 --- a/documentation/examples.rst +++ b/documentation/examples.rst @@ -82,7 +82,7 @@ portable (runs on POSIX, Windows, etc). using :rfc:`2217` requests. The status lines (DSR/CTS/RI/CD) are polled every second and notifications are sent to the client. - Telnet character IAC (0xff) needs to be doubled in data stream. IAC followed - by an other value is interpreted as Telnet command sequence. + by another value is interpreted as Telnet command sequence. - Telnet negotiation commands are sent when connecting to the server. - RTS/DTR are activated on client connect and deactivated on disconnect. - Default port settings are set again when client disconnects. @@ -187,7 +187,7 @@ Installation as daemon: - Copy the script ``port_publisher.py`` to ``/usr/local/bin``. - Copy the script ``port_publisher.sh`` to ``/etc/init.d``. - Add links to the runlevels using ``update-rc.d port_publisher.sh defaults 99`` -- Thats it :-) the service will be started on next reboot. Alternatively run +- That's it :-) the service will be started on next reboot. Alternatively run ``invoke-rc.d port_publisher.sh start`` as root. .. versionadded:: 2.5 new example diff --git a/documentation/pyserial_api.rst b/documentation/pyserial_api.rst index 3887204..54d2ff3 100644 --- a/documentation/pyserial_api.rst +++ b/documentation/pyserial_api.rst @@ -498,6 +498,8 @@ Native ports .. versionchanged:: 3.0 renamed from ``applySettingsDict`` + .. _context-manager: + This class can be used as context manager. The serial port is closed when the context is left. @@ -579,7 +581,7 @@ Native ports :platform: Posix :platform: Windows - Cancel a pending read operation from an other thread. A blocking + Cancel a pending read operation from another thread. A blocking :meth:`read` call is aborted immediately. :meth:`read` will not report any error but return all data received up to that point (similar to a timeout). @@ -593,7 +595,7 @@ Native ports :platform: Posix :platform: Windows - Cancel a pending write operation from an other thread. The + Cancel a pending write operation from another thread. The :meth:`write` method will return immediately (no error indicated). However the OS may still be sending from the buffer, a separate call to :meth:`reset_output_buffer` may be needed. @@ -1260,7 +1262,7 @@ asyncio ``asyncio`` was introduced with Python 3.4. Experimental support for pySerial is provided via a separate distribution `pyserial-asyncio`_. -It is currently under developement, see: +It is currently under development, see: - http://pyserial-asyncio.readthedocs.io/ - https://github.com/pyserial/pyserial-asyncio diff --git a/documentation/shortintro.rst b/documentation/shortintro.rst index 8f33a68..02385d9 100644 --- a/documentation/shortintro.rst +++ b/documentation/shortintro.rst @@ -44,9 +44,9 @@ Get a Serial instance and configure/open it later:: >>> ser.is_open False -Also supported with context manager:: +Also supported with :ref:`context manager <context-manager>`:: - serial.Serial() as ser: + with serial.Serial() as ser: ser.baudrate = 19200 ser.port = 'COM1' ser.open() diff --git a/documentation/tools.rst b/documentation/tools.rst index 45e7aef..437a884 100644 --- a/documentation/tools.rst +++ b/documentation/tools.rst @@ -97,7 +97,7 @@ serial.tools.list_ports``). It also contains the following functions. .. attribute:: interface - Interface specifc description, e.g. used in compound USB devices. + Interface specific description, e.g. used in compound USB devices. Comparison operators are implemented such that the :obj:`ListPortInfo` objects can be sorted by ``device``. Strings are split into groups of numbers and diff --git a/documentation/url_handlers.rst b/documentation/url_handlers.rst index 81e6ff8..adacc2e 100644 --- a/documentation/url_handlers.rst +++ b/documentation/url_handlers.rst @@ -197,6 +197,15 @@ Outputs:: 000002.284 RX 00F0 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ 000002.284 BRK send_break 0.25 +Another example, on POSIX, open a second terminal window and find out it's +device (e.g. with the ``ps`` command in the TTY column), assumed to be +``/dev/pts/2`` here, double quotes are used so that the ampersand in the URL is +not interpreted by the shell:: + + python -m serial.tools.miniterm "spy:///dev/ttyUSB0?file=/dev/pts/2&color" 115200 + +The spy output will be live in the second terminal window. + .. versionadded:: 3.0 @@ -204,7 +213,7 @@ Outputs:: ========== This handler allows to select alternate implementations of the native serial port. -Currently only the Posix platform provides alternative implementations. +Currently only the POSIX platform provides alternative implementations. ``PosixPollSerial`` Poll based read implementation. Not all systems support poll properly. diff --git a/examples/rfc2217_server.py b/examples/rfc2217_server.py index 5955fc0..42660dd 100755 --- a/examples/rfc2217_server.py +++ b/examples/rfc2217_server.py @@ -56,7 +56,7 @@ class Redirector(object): data = self.serial.read(self.serial.in_waiting or 1) if data: # escape outgoing data when needed (Telnet IAC (0xff) character) - self.write(serial.to_bytes(self.rfc2217.escape(data))) + self.write(b''.join(self.rfc2217.escape(data))) except socket.error as msg: self.log.error('{}'.format(msg)) # probably got disconnected @@ -76,7 +76,7 @@ class Redirector(object): data = self.socket.recv(1024) if not data: break - self.serial.write(serial.to_bytes(self.rfc2217.filter(data))) + self.serial.write(b''.join(self.rfc2217.filter(data))) except socket.error as msg: self.log.error('{}'.format(msg)) # probably got disconnected diff --git a/examples/tcp_serial_redirect.py b/examples/tcp_serial_redirect.py index 8440296..53dc0ad 100755 --- a/examples/tcp_serial_redirect.py +++ b/examples/tcp_serial_redirect.py @@ -171,10 +171,13 @@ it waits for the next connect. # connection: After 1 second of idle, start sending TCP keep-alive # packets every 1 second. If 3 consecutive keep-alive packets # fail, assume the client is gone and close the connection. - client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1) - client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1) - client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3) + try: + client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1) + client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1) + client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3) + client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + except AttributeError: + pass # XXX not available on windows client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) try: ser_to_net.socket = client_socket diff --git a/serial/rfc2217.py b/serial/rfc2217.py index dee5c2b..a8bb006 100644 --- a/serial/rfc2217.py +++ b/serial/rfc2217.py @@ -380,7 +380,6 @@ class Serial(SerialBase): 9600, 19200, 38400, 57600, 115200) def __init__(self, *args, **kwargs): - super(Serial, self).__init__(*args, **kwargs) self._thread = None self._socket = None self._linestate = 0 @@ -396,6 +395,7 @@ class Serial(SerialBase): self._rfc2217_port_settings = None self._rfc2217_options = None self._read_buffer = None + super(Serial, self).__init__(*args, **kwargs) # must be last call in case of auto-open def open(self): """\ @@ -609,10 +609,13 @@ class Serial(SerialBase): raise portNotOpenError data = bytearray() try: + timeout = Timeout(self._timeout) while len(data) < size: if self._thread is None: raise SerialException('connection failed (reader thread died)') - data += self._read_buffer.get(True, self._timeout) + data += self._read_buffer.get(True, timeout.time_left()) + if timeout.expired(): + break except Queue.Empty: # -> timeout pass return bytes(data) @@ -850,12 +853,12 @@ class Serial(SerialBase): def telnet_send_option(self, action, option): """Send DO, DONT, WILL, WONT.""" - self._internal_raw_write(to_bytes([IAC, action, option])) + self._internal_raw_write(IAC + action + option) def rfc2217_send_subnegotiation(self, option, value=b''): """Subnegotiation of RFC2217 parameters.""" value = value.replace(IAC, IAC_DOUBLED) - self._internal_raw_write(to_bytes([IAC, SB, COM_PORT_OPTION, option] + list(value) + [IAC, SE])) + self._internal_raw_write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) def rfc2217_send_purge(self, value): """\ @@ -989,12 +992,12 @@ class PortManager(object): def telnet_send_option(self, action, option): """Send DO, DONT, WILL, WONT.""" - self.connection.write(to_bytes([IAC, action, option])) + self.connection.write(IAC + action + option) def rfc2217_send_subnegotiation(self, option, value=b''): """Subnegotiation of RFC 2217 parameters.""" value = value.replace(IAC, IAC_DOUBLED) - self.connection.write(to_bytes([IAC, SB, COM_PORT_OPTION, option] + list(value) + [IAC, SE])) + self.connection.write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) # - check modem lines, needs to be called periodically from user to # establish polling diff --git a/serial/serialposix.py b/serial/serialposix.py index e6ef534..cff3417 100644 --- a/serial/serialposix.py +++ b/serial/serialposix.py @@ -527,7 +527,7 @@ class Serial(SerialBase, PlatformSpecific): if not self.is_open: raise portNotOpenError d = to_bytes(data) - tx_len = len(d) + tx_len = length = len(d) timeout = Timeout(self._write_timeout) while tx_len > 0: try: @@ -566,7 +566,7 @@ class Serial(SerialBase, PlatformSpecific): # still calculate and check timeout if timeout.expired(): raise writeTimeoutError - return len(data) + return length - len(d) def flush(self): """\ diff --git a/serial/serialutil.py b/serial/serialutil.py index 37eb321..322b7e3 100644 --- a/serial/serialutil.py +++ b/serial/serialutil.py @@ -652,6 +652,7 @@ class SerialBase(io.RawIOBase): """ lenterm = len(terminator) line = bytearray() + timeout = Timeout(self._timeout) while True: c = self.read(1) if c: @@ -662,6 +663,8 @@ class SerialBase(io.RawIOBase): break else: break + if timeout.expired(): + break return bytes(line) def iread_until(self, *args, **kwargs): diff --git a/serial/serialwin32.py b/serial/serialwin32.py index b275ea3..829a71b 100644 --- a/serial/serialwin32.py +++ b/serial/serialwin32.py @@ -254,7 +254,7 @@ class Serial(SerialBase): flags = win32.DWORD() comstat = win32.COMSTAT() if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): - raise SerialException('call to ClearCommError failed') + raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) return comstat.cbInQue def read(self, size=1): @@ -311,7 +311,7 @@ class Serial(SerialBase): n = win32.DWORD() success = win32.WriteFile(self._port_handle, data, len(data), ctypes.byref(n), self._overlapped_write) if self._write_timeout != 0: # if blocking (None) or w/ write timeout (>0) - if not success and win32.GetLastError() != win32.ERROR_IO_PENDING: + if not success and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError())) # Wait for the write to complete. diff --git a/serial/tools/list_ports_windows.py b/serial/tools/list_ports_windows.py index a070559..93fa128 100644 --- a/serial/tools/list_ports_windows.py +++ b/serial/tools/list_ports_windows.py @@ -124,6 +124,7 @@ ERROR_INSUFFICIENT_BUFFER = 122 SPDRP_HARDWAREID = 1 SPDRP_FRIENDLYNAME = 12 SPDRP_LOCATION_PATHS = 35 +SPDRP_MFG = 11 DICS_FLAG_GLOBAL = 1 DIREG_DEV = 0x00000001 KEY_READ = 0x20019 @@ -269,6 +270,19 @@ def iterate_comports(): #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value)) # ignore errors and still include the port in the list, friendly name will be same as port name + + # manufacturer + szManufacturer = ctypes.create_unicode_buffer(250) + if SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_MFG, + #~ SPDRP_DEVICEDESC, + None, + ctypes.byref(szManufacturer), + ctypes.sizeof(szManufacturer) - 1, + None): + info.manufacturer = szManufacturer.value yield info SetupDiDestroyDeviceInfoList(g_hdi) diff --git a/serial/tools/miniterm.py b/serial/tools/miniterm.py index 7c68e9d..14182f0 100644 --- a/serial/tools/miniterm.py +++ b/serial/tools/miniterm.py @@ -135,15 +135,12 @@ if os.name == 'nt': # noqa elif os.name == 'posix': import atexit import termios - import select + import fcntl class Console(ConsoleBase): def __init__(self): super(Console, self).__init__() self.fd = sys.stdin.fileno() - # an additional pipe is used in getkey, so that the cancel method - # can abort the waiting getkey method - self.pipe_r, self.pipe_w = os.pipe() self.old = termios.tcgetattr(self.fd) atexit.register(self.cleanup) if sys.version_info < (3, 0): @@ -159,17 +156,13 @@ elif os.name == 'posix': termios.tcsetattr(self.fd, termios.TCSANOW, new) def getkey(self): - ready, _, _ = select.select([self.enc_stdin, self.pipe_r], [], [], None) - if self.pipe_r in ready: - os.read(self.pipe_r, 1) - return c = self.enc_stdin.read(1) if c == unichr(0x7f): c = unichr(8) # map the BS key (which yields DEL) to backspace return c def cancel(self): - os.write(self.pipe_w, b"x") + fcntl.ioctl(self.fd, termios.TIOCSTI, b'\0') def cleanup(self): termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old) diff --git a/serial/urlhandler/protocol_socket.py b/serial/urlhandler/protocol_socket.py index a017ee3..a35cf75 100644 --- a/serial/urlhandler/protocol_socket.py +++ b/serial/urlhandler/protocol_socket.py @@ -26,7 +26,8 @@ try: except ImportError: import urllib.parse as urlparse -from serial.serialutil import SerialBase, SerialException, portNotOpenError, to_bytes +from serial.serialutil import SerialBase, SerialException, to_bytes, \ + portNotOpenError, writeTimeoutError, Timeout # map log level names to constants. used in from_url() LOGGER_LEVELS = { @@ -61,6 +62,8 @@ class Serial(SerialBase): except Exception as msg: self._socket = None raise SerialException("Could not open port {}: {}".format(self.portstr, msg)) + # after connecting, switch to non-blocking, we're using select + self._socket.setblocking(False) # not that there is anything to configure... self._reconfigure_port() @@ -149,11 +152,10 @@ class Serial(SerialBase): 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._socket], [], [], timeout) + ready, _, _ = select.select([self._socket], [], [], timeout.time_left()) # If select was used with a timeout, and the timeout occurs, it # returns with empty lists -> thus abort read operation. # For timeout == 0 (non-blocking operation) also abort when @@ -166,27 +168,19 @@ class Serial(SerialBase): if not buf: raise SerialException('socket disconnected') read.extend(buf) - if timeout is not None: - timeout -= time.time() - start_time - if timeout <= 0: - break - except socket.timeout: - # timeout is used for write support, just go reading again - pass - except socket.error as e: - # connection fails -> terminate loop - raise SerialException('connection failed ({})'.format(e)) except OSError as e: # this is for Python 3.x where select.error is a subclass of # OSError ignore EAGAIN errors. all other errors are shown if e.errno != errno.EAGAIN: raise SerialException('read failed: {}'.format(e)) - except select.error as e: + except (select.error, socket.error) as e: # this is for Python 2.x # ignore EAGAIN errors. all other errors are shown # see also http://www.python.org/dev/peps/pep-3151/#select if e[0] != errno.EAGAIN: raise SerialException('read failed: {}'.format(e)) + if timeout.expired(): + break return bytes(read) def write(self, data): @@ -197,19 +191,65 @@ class Serial(SerialBase): """ if not self.is_open: raise portNotOpenError - try: - self._socket.sendall(to_bytes(data)) - except socket.error as e: - # XXX what exception if socket connection fails - raise SerialException("socket connection failed: {}".format(e)) - return len(data) + + d = to_bytes(data) + tx_len = length = len(d) + timeout = Timeout(self._write_timeout) + while tx_len > 0: + try: + n = self._socket.send(d) + if timeout.is_non_blocking: + # Zero timeout indicates non-blocking - simply return the + # number of bytes of data actually written + return n + elif not timeout.is_infinite: + # when timeout is set, use select to wait for being ready + # with the time left as timeout + if timeout.expired(): + raise writeTimeoutError + _, ready, _ = select.select([], [self._socket], [], timeout.time_left()) + if not ready: + raise writeTimeoutError + else: + assert timeout.time_left() is None + # wait for write operation + _, ready, _ = select.select([], [self._socket], [], None) + if not ready: + raise SerialException('write failed (select)') + d = d[n:] + tx_len -= n + except SerialException: + raise + except OSError as v: + if v.errno != errno.EAGAIN: + raise SerialException('write failed: {}'.format(v)) + # still calculate and check timeout + if timeout.expired(): + raise writeTimeoutError + return length - len(d) def reset_input_buffer(self): """Clear input buffer, discarding all that is in the buffer.""" if not self.is_open: raise portNotOpenError - if self.logger: - self.logger.info('ignored reset_input_buffer') + + # just use recv to remove input, while there is some + ready = True + while ready: + ready, _, _ = select.select([self._socket], [], [], 0) + try: + self._socket.recv(4096) + except OSError as e: + # this is for Python 3.x where select.error is a subclass of + # OSError ignore EAGAIN errors. all other errors are shown + if e.errno != errno.EAGAIN: + raise SerialException('reset_input_buffer failed: {}'.format(e)) + except (select.error, socket.error) as e: + # this is for Python 2.x + # ignore EAGAIN errors. all other errors are shown + # see also http://www.python.org/dev/peps/pep-3151/#select + if e[0] != errno.EAGAIN: + raise SerialException('reset_input_buffer failed: {}'.format(e)) def reset_output_buffer(self): """\ |