Hello community,

here is the log from the commit of package python-pyserial for openSUSE:Factory 
checked in at 2017-08-12 19:43:24
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pyserial (Old)
 and      /work/SRC/openSUSE:Factory/.python-pyserial.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pyserial"

Sat Aug 12 19:43:24 2017 rev:15 rq:515045 version:3.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pyserial/python-pyserial.changes  
2017-06-28 10:33:08.278102490 +0200
+++ /work/SRC/openSUSE:Factory/.python-pyserial.new/python-pyserial.changes     
2017-08-12 19:43:25.544109791 +0200
@@ -1,0 +2,26 @@
+Mon Aug  7 16:32:29 UTC 2017 - toddrme2...@gmail.com
+
+- Further fix shebangs
+
+-------------------------------------------------------------------
+Sun Aug  6 18:07:57 UTC 2017 - toddrme2...@gmail.com
+
+- Update to version 3.4
+  + Improvements:
+    * miniterm: suspend function (temporarily release port, :kbd:`Ctrl-T s`)
+    * [#240] context manager automatically opens port on ``__enter__``
+    * [#141] list_ports: add interface number to location string
+    * [#225] protocol_socket: Retry if ``BlockingIOError`` occurs in
+      ``reset_input_buffer``.
+  + Bugfixes:
+    * [#153] list_ports: option to include symlinked devices
+    * [#237] list_ports: workaround for special characters in port names
+  + Bugfixes (posix):
+    * allow calling cancel functions w/o error if port is closed
+    * [#220] protocol_socket: sync error handling with posix version
+    * [#227] posix: ignore more blocking errors and EINTR, timeout only
+      applies to blocking I/O
+    * [#228] fix: port_publisher typo
+- Fix shebangs
+
+-------------------------------------------------------------------

Old:
----
  pyserial-3.3.tar.gz

New:
----
  pyserial-3.4.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pyserial.spec ++++++
--- /var/tmp/diff_new_pack.LstlaT/_old  2017-08-12 19:43:26.615959450 +0200
+++ /var/tmp/diff_new_pack.LstlaT/_new  2017-08-12 19:43:26.623958328 +0200
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-pyserial
-Version:        3.3
+Version:        3.4
 Release:        0
 Url:            https://github.com/pyserial/pyserial
 Summary:        Python Serial Port Extension
@@ -53,22 +53,43 @@
 
 %prep
 %setup -q -n pyserial-%{version}
-sed -i "s|#\!\/usr\/bin\/env python||" serial/{serialposix,__init__}.py # Fix 
non-executable scripts
+sed -i "s|^#\!\/usr\/bin\/env python$||" serial/{serialposix,__init__}.py # 
Fix non-executable scripts
 sed -i "s/\r//" examples/port_publisher.py # Fix EOL encoding
+find examples -name '*.py' -exec sed -i "s|^#!/usr/bin/env 
python$|#!%{__python3}|" {} \;
+find examples -name '*.py' -exec sed -i "s|^#! /usr/bin/env 
python$|#!%{__python3}|" {} \;
+find examples -name '*.py' -exec sed -i "s|^#! python$|#!%{__python3}|" {} \;
 
 %build
 %python_build
 make -C documentation html && rm documentation/_build/html/.buildinfo # Build 
HTML documentation
 
+
 %install
 %python_install
 
-%python_expand %fdupes %{buildroot}%{$python_sitelib}
-
 mv %{buildroot}%{_bindir}/miniterm.py %{buildroot}%{_bindir}/miniterm
 %python_clone -a %{buildroot}%{_bindir}/miniterm
 rm documentation/_build/doctrees/environment.pickle
 
+%{python_expand pushd %{buildroot}%{$python_sitelib}
+# Fix wrong-script-interpreter
+sed -i "s|^#!/usr/bin/env python$|#!%{__$python}|" serial/tools/*.py
+sed -i "s|^#!/usr/bin/env python3$|#!%{__$python}|" serial/threaded/__init__.py
+sed -i "s|^#!/usr/bin/env python$|#!%{__$python}|" serial/rs485.py
+sed -i "s|^#! python$||#!%{__$python}|" serial/tools/*.py
+chmod a+x serial/tools/*.py
+chmod a-x serial/tools/__init__.py
+chmod a-x serial/tools/list_ports_windows.py
+chmod a-x serial/tools/hexlify_codec.py
+chmod a+x serial/threaded/__init__.py
+chmod a+x serial/rs485.py
+# Deduplicating files can generate a RPMLINT warning for pyc mtime
+$python -m compileall -d %{$python_sitelib} serial/
+$python -O -m compileall -d %{$python_sitelib} serial/
+%fdupes .
+popd
+}
+
 %post
 %python_install_alternative miniterm
 

++++++ pyserial-3.3.tar.gz -> pyserial-3.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/CHANGES.rst new/pyserial-3.4/CHANGES.rst
--- old/pyserial-3.3/CHANGES.rst        2017-03-07 23:32:12.000000000 +0100
+++ new/pyserial-3.4/CHANGES.rst        2017-07-22 22:46:46.000000000 +0200
@@ -710,3 +710,27 @@
 Bugfixes (win32):
 
 - [#194] spurious write fails with ERROR_SUCCESS
+
+
+Version 3.4   2017-07-22
+------------------------
+Improvements:
+
+- miniterm: suspend function (temporarily release port, :kbd:`Ctrl-T s`)
+- [#240] context manager automatically opens port on ``__enter__``
+- [#141] list_ports: add interface number to location string
+- [#225] protocol_socket: Retry if ``BlockingIOError`` occurs in
+  ``reset_input_buffer``.
+
+Bugfixes:
+
+- [#153] list_ports: option to include symlinked devices
+- [#237] list_ports: workaround for special characters in port names
+
+Bugfixes (posix):
+
+- allow calling cancel functions w/o error if port is closed
+- [#220] protocol_socket: sync error handling with posix version
+- [#227] posix: ignore more blocking errors and EINTR, timeout only
+  applies to blocking I/O
+- [#228] fix: port_publisher typo
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/PKG-INFO new/pyserial-3.4/PKG-INFO
--- old/pyserial-3.3/PKG-INFO   2017-03-08 02:44:53.000000000 +0100
+++ new/pyserial-3.4/PKG-INFO   2017-07-22 22:51:52.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pyserial
-Version: 3.3
+Version: 3.4
 Summary: Python Serial Port Extension
 Home-page: https://github.com/pyserial/pyserial
 Author: Chris Liechti
@@ -35,6 +35,7 @@
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
 Classifier: Topic :: Communications
 Classifier: Topic :: Software Development :: Libraries
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/README.rst new/pyserial-3.4/README.rst
--- old/pyserial-3.3/README.rst 2016-12-20 23:03:41.000000000 +0100
+++ new/pyserial-3.4/README.rst 2017-03-20 23:20:55.000000000 +0100
@@ -12,7 +12,7 @@
 - Project Homepage: https://github.com/pyserial/pyserial
 - Download Page: https://pypi.python.org/pypi/pyserial
 
-BSD license, (C) 2001-2016 Chris Liechti <cliec...@gmx.net>
+BSD license, (C) 2001-2017 Chris Liechti <cliec...@gmx.net>
 
 
 Documentation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/documentation/appendix.rst 
new/pyserial-3.4/documentation/appendix.rst
--- old/pyserial-3.3/documentation/appendix.rst 2016-12-13 23:47:25.000000000 
+0100
+++ new/pyserial-3.4/documentation/appendix.rst 2017-03-21 00:16:14.000000000 
+0100
@@ -68,7 +68,7 @@
       used.
 
     - :func:`serial.serial_for_url` does a dynamic lookup of protocol handlers
-      at runtime.  If this function is used, the desired handlers have to be
+      at runtime. If this function is used, the desired handlers have to be
       included manually (e.g. 'serial.urlhandler.protocol_socket',
       'serial.urlhandler.protocol_rfc2217', etc.). This can be done either with
       the "includes" option in ``setup.py`` or by a dummy import in one of the
@@ -93,7 +93,7 @@
 
 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.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).
 
@@ -109,7 +109,7 @@
 
 License
 =======
-Copyright (c) 2001-2016 Chris Liechti <cliec...@gmx.net>
+Copyright (c) 2001-2017 Chris Liechti <cliec...@gmx.net>
 All Rights Reserved.
 
 Redistribution and use in source and binary forms, with or without
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/documentation/conf.py 
new/pyserial-3.4/documentation/conf.py
--- old/pyserial-3.3/documentation/conf.py      2017-03-07 23:32:00.000000000 
+0100
+++ new/pyserial-3.4/documentation/conf.py      2017-07-22 22:49:49.000000000 
+0200
@@ -45,9 +45,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = '3.3'
+version = '3.4'
 # The full version, including alpha/beta/rc tags.
-release = '3.3'
+release = '3.4'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/documentation/examples.rst 
new/pyserial-3.4/documentation/examples.rst
--- old/pyserial-3.3/documentation/examples.rst 2016-12-20 23:03:41.000000000 
+0100
+++ new/pyserial-3.4/documentation/examples.rst 2017-03-21 01:53:38.000000000 
+0100
@@ -237,8 +237,10 @@
 need a loop back connector. The scripts itself contain more information. All
 test scripts are contained in the directory ``test``.
 
-The unit tests are performed on port ``0`` unless a different device name or
-``rfc2217://`` URL is given on the command line (argv[1]).
+The unit tests are performed on port ``loop://`` unless a different device
+name or URL is given on the command line (``sys.argv[1]``). e.g. to run the
+test on an attached USB-serial converter ``hwgrep://USB`` could be used or
+the actual name such as ``/dev/ttyUSB0`` or ``COM1`` (depending on platform).
 
 run_all_tests.py_
     Collect all tests from all ``test*`` files and run them. By default, the
@@ -254,7 +256,7 @@
     Tests involving sending a lot of data.
 
 test_readline.py_
-    Tests involving readline.
+    Tests involving ``readline``.
 
 test_iolib.py_
     Tests involving the :mod:`io` library. Only available for Python 2.6 and
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/documentation/pyserial.rst 
new/pyserial-3.4/documentation/pyserial.rst
--- old/pyserial-3.3/documentation/pyserial.rst 2016-09-18 22:53:26.000000000 
+0200
+++ new/pyserial-3.4/documentation/pyserial.rst 2017-03-21 01:36:59.000000000 
+0100
@@ -48,7 +48,7 @@
 ============
 - Python 2.7 or Python 3.4 and newer
 
-- If running on Windows: Something newer than WinXP
+- If running on Windows: Windows 7 or newer
 
 - If running on Jython: "Java Communications" (JavaComm) or compatible
   extension for Java
@@ -93,7 +93,7 @@
 - Debian/Ubuntu: "python-serial", "python3-serial"
 - Fedora / RHEL / CentOS / EPEL: "pyserial"
 - Arch Linux: "python-pyserial"
-- Gento: "dev-python/pyserial"
+- Gentoo: "dev-python/pyserial"
 
 Note that some distributions may package an older version of pySerial.
 These packages are created and maintained by developers working on
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/documentation/pyserial_api.rst 
new/pyserial-3.4/documentation/pyserial_api.rst
--- old/pyserial-3.3/documentation/pyserial_api.rst     2017-03-07 
21:05:02.000000000 +0100
+++ new/pyserial-3.4/documentation/pyserial_api.rst     2017-07-20 
23:45:59.000000000 +0200
@@ -122,7 +122,7 @@
 
             Some OS and/or drivers may activate RTS and or DTR automatically,
             as soon as the port is opened. There may be a glitch on RTS/DTR
-            when :attr:`rts`` or :attr:`dtr` are set differently from their
+            when :attr:`rts` or :attr:`dtr` are set differently from their
             default value (``True`` / active).
 
         .. note::
@@ -205,7 +205,7 @@
 
     .. method:: reset_input_buffer()
 
-        Flush input buffer, discarding all it's contents.
+        Flush input buffer, discarding all its contents.
 
         .. versionchanged:: 3.0 renamed from ``flushInput()``
 
@@ -515,17 +515,22 @@
         >>> with serial.serial_for_url(port) as s:
         ...     s.write(b'hello')
 
-        Here no port argument is given, so it is not opened automatically:
+        The port is opened automatically:
 
-        >>> with serial.Serial() as s:
-        ...     s.port = ...
-        ...     s.open()
+        >>> port = serial.Serial()
+        >>> port.port = '...'
+        >>> with port as s:
         ...     s.write(b'hello')
 
+        Which also means that ``with`` statements can be used repeatedly,
+        each time opening and closing the port.
+
+        .. versionchanged:: 3.4 the port is automatically opened
+
 
     .. method:: __exit__(exc_type, exc_val, exc_tb)
 
-        Closes serial port.
+        Closes serial port (exceptions are not handled by ``__exit__``).
 
 
     Platform specific methods.
@@ -618,7 +623,7 @@
 
     .. method:: isOpen()
 
-       .. deprecated:: 3.0 see :attr:`is_open`
+        .. deprecated:: 3.0 see :attr:`is_open`
 
     .. attribute:: writeTimeout
 
@@ -704,6 +709,7 @@
 class :class:`SerialBase` and some by the platform specific class and
 others by the base class mentioned above.
 
+
 RS485 support
 -------------
 The :class:`Serial` class has a :attr:`Serial.rs485_mode` attribute which 
allows to
@@ -795,7 +801,6 @@
 
 
 
-
 :rfc:`2217` Network ports
 -------------------------
 
@@ -1060,7 +1065,7 @@
     :returns: a generator that yields bytes
 
     Some versions of Python (3.x) would return integers instead of bytes when
-    looping over an instance of ``bytes``.  This helper function ensures that
+    looping over an instance of ``bytes``. This helper function ensures that
     bytes are returned.
 
     .. versionadded:: 3.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/documentation/tools.rst 
new/pyserial-3.4/documentation/tools.rst
--- old/pyserial-3.3/documentation/tools.rst    2016-12-20 23:03:41.000000000 
+0100
+++ new/pyserial-3.4/documentation/tools.rst    2017-07-17 23:59:28.000000000 
+0200
@@ -12,8 +12,10 @@
 serial.tools.list_ports``). It also contains the following functions.
 
 
-.. function:: comports()
+.. function:: comports(include_links=False)
 
+    :param bool include_links: include symlinks under ``/dev`` when they point
+                               to a serial port
     :return: a list containing :class:`ListPortInfo` objects.
 
     The function returns a list of :obj:`ListPortInfo` objects.
@@ -26,22 +28,36 @@
               systems description and hardware ID will not be available
               (``None``).
 
+    Under Linux, OSX and Windows, extended information will be available for
+    USB devices (e.g. the :attr:`ListPortInfo.hwid` string contains `VID:PID`,
+    `SER` (serial number), `LOCATION` (hierarchy), which makes them searchable
+    via :func:`grep`. The USB info is also available as attributes of
+    :attr:`ListPortInfo`.
+
+    If *include_links* is true, all devices under ``/dev`` are inspected and
+    tested if they are a link to a known serial port device. These entries
+    will include ``LINK`` in their ``hwid`` string. This implies that the same
+    device listed twice, once under its original name and once under linked
+    name.
+
     :platform: Posix (/dev files)
     :platform: Linux (/dev files, sysfs)
     :platform: OSX (iokit)
     :platform: Windows (setupapi, registry)
 
 
-.. function:: grep(regexp)
+.. function:: grep(regexp, include_links=False)
 
     :param regexp: regular expression (see stdlib :mod:`re`)
+    :param bool include_links: include symlinks under ``/dev`` when they point
+                               to a serial port
     :return: an iterable that yields :class:`ListPortInfo` objects, see also
              :func:`comports`.
 
-    Search for ports using a regular expression. Port name, description and
-    hardware ID are searched (case insensitive). The function returns an
-    iterable that contains the same data that :func:`comports` generates, but
-    includes only those entries that match the regexp.
+    Search for ports using a regular expression. Port ``name``,
+    ``description`` and ``hwid`` are searched (case insensitive). The function
+    returns an iterable that contains the same data that :func:`comports`
+    generates, but includes only those entries that match the regexp.
 
 
 .. class:: ListPortInfo
@@ -109,18 +125,20 @@
 
 Help for ``python -m serial.tools.list_ports``::
 
-    usage: list_ports.py [-h] [-v] [-q] [-n N] [regexp]
+    usage: list_ports.py [-h] [-v] [-q] [-n N] [-s] [regexp]
 
     Serial port enumeration
 
     positional arguments:
-      regexp         only show ports that match this regex
+      regexp               only show ports that match this regex
 
     optional arguments:
-      -h, --help     show this help message and exit
-      -v, --verbose  show more messages
-      -q, --quiet    suppress all messages
-      -n N           only output the N-th entry
+      -h, --help           show this help message and exit
+      -v, --verbose        show more messages
+      -q, --quiet          suppress all messages
+      -n N                 only output the N-th entry
+      -s, --include-links  include entries that are symlinks to real devices
+
 
 Examples:
 
@@ -256,6 +274,11 @@
     ---    x X        disable/enable software flow control
     ---    r R        disable/enable hardware flow control
 
+:kbd:`Ctrl+T s` suspends the connection (port is opened) and reconnects when a
+key is pressed. This can be used to temporarily access the serial port with an
+other application, without exiting miniterm. If reconnecting fails it is
+also possible to exit (:kbd:`Ctrl+]`) or change the port (:kbd:`p`).
+
 .. versionchanged:: 2.5
     Added :kbd:`Ctrl+T` menu and added support for opening URLs.
 .. versionchanged:: 2.6
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/documentation/url_handlers.rst 
new/pyserial-3.4/documentation/url_handlers.rst
--- old/pyserial-3.3/documentation/url_handlers.rst     2016-12-20 
23:03:41.000000000 +0100
+++ new/pyserial-3.4/documentation/url_handlers.rst     2017-03-18 
23:35:49.000000000 +0100
@@ -211,7 +211,8 @@
 
 ``alt://``
 ==========
-This handler allows to select alternate implementations of the native serial 
port.
+This handler allows to select alternate implementations of the native serial
+port.
 
 Currently only the POSIX platform provides alternative implementations.
 
@@ -221,10 +222,10 @@
     disconnecting while it's in use (e.g. USB-serial unplugged).
 
 ``VTIMESerial``
-    Implement timeout using ``VTIME``/``VMIN`` of tty device instead of using
-    ``select``.  This means that inter character timeout and overall timeout
+    Implement timeout using ``VTIME``/``VMIN`` of TTY device instead of using
+    ``select``. This means that inter character timeout and overall timeout
     can not be used at the same time. Overall timeout is disabled when
-    inter-character timeout is used.  The error handling is degraded.
+    inter-character timeout is used. The error handling is degraded.
 
  
 Examples::
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/examples/port_publisher.py 
new/pyserial-3.4/examples/port_publisher.py
--- old/pyserial-3.3/examples/port_publisher.py 2016-06-21 20:46:49.000000000 
+0200
+++ new/pyserial-3.4/examples/port_publisher.py 2017-05-08 23:16:33.000000000 
+0200
@@ -465,7 +465,7 @@
             if pid > 0:
                 # exit from second parent, save eventual PID before
                 if args.pidfile is not None:
-                    open(args.pidfile, 'w').write("{}".formt(pid))
+                    open(args.pidfile, 'w').write("{}".format(pid))
                 sys.exit(0)
         except OSError as e:
             log.critical("fork #2 failed: {} ({})\n".format(e.errno, 
e.strerror))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/pyserial.egg-info/PKG-INFO 
new/pyserial-3.4/pyserial.egg-info/PKG-INFO
--- old/pyserial-3.3/pyserial.egg-info/PKG-INFO 2017-03-08 02:44:53.000000000 
+0100
+++ new/pyserial-3.4/pyserial.egg-info/PKG-INFO 2017-07-22 22:51:51.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pyserial
-Version: 3.3
+Version: 3.4
 Summary: Python Serial Port Extension
 Home-page: https://github.com/pyserial/pyserial
 Author: Chris Liechti
@@ -35,6 +35,7 @@
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
 Classifier: Topic :: Communications
 Classifier: Topic :: Software Development :: Libraries
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/pyserial.egg-info/SOURCES.txt 
new/pyserial-3.4/pyserial.egg-info/SOURCES.txt
--- old/pyserial-3.3/pyserial.egg-info/SOURCES.txt      2017-03-08 
02:44:53.000000000 +0100
+++ new/pyserial-3.4/pyserial.egg-info/SOURCES.txt      2017-07-22 
22:51:51.000000000 +0200
@@ -64,6 +64,7 @@
 test/test_advanced.py
 test/test_asyncio.py
 test/test_cancel.py
+test/test_context.py
 test/test_exclusive.py
 test/test_high_load.py
 test/test_iolib.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/__init__.py 
new/pyserial-3.4/serial/__init__.py
--- old/pyserial-3.3/serial/__init__.py 2017-03-07 23:32:56.000000000 +0100
+++ new/pyserial-3.4/serial/__init__.py 2017-07-22 22:49:29.000000000 +0200
@@ -13,7 +13,7 @@
 from serial.serialutil import *
 #~ SerialBase, SerialException, to_bytes, iterbytes
 
-__version__ = '3.3'
+__version__ = '3.4'
 
 VERSION = __version__
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/rfc2217.py 
new/pyserial-3.4/serial/rfc2217.py
--- old/pyserial-3.3/serial/rfc2217.py  2016-12-21 03:19:17.000000000 +0100
+++ new/pyserial-3.4/serial/rfc2217.py  2017-05-03 18:33:25.000000000 +0200
@@ -894,7 +894,7 @@
         """\
         get last modem state (cached value. If value is "old", request a new
         one. This cache helps that we don't issue to many requests when e.g. 
all
-        status lines, one after the other is queried by the user (getCTS, 
getDSR
+        status lines, one after the other is queried by the user (CTS, DSR
         etc.)
         """
         # active modem state polling enabled? is the value fresh enough?
@@ -1009,10 +1009,10 @@
         send updates on changes.
         """
         modemstate = (
-            (self.serial.getCTS() and MODEMSTATE_MASK_CTS) |
-            (self.serial.getDSR() and MODEMSTATE_MASK_DSR) |
-            (self.serial.getRI() and MODEMSTATE_MASK_RI) |
-            (self.serial.getCD() and MODEMSTATE_MASK_CD))
+            (self.serial.cts and MODEMSTATE_MASK_CTS) |
+            (self.serial.dsr and MODEMSTATE_MASK_DSR) |
+            (self.serial.ri and MODEMSTATE_MASK_RI) |
+            (self.serial.cd and MODEMSTATE_MASK_CD))
         # check what has changed
         deltas = modemstate ^ (self.last_modemstate or 0)  # when last is None 
-> 0
         if deltas & MODEMSTATE_MASK_CTS:
@@ -1234,12 +1234,12 @@
                         self.logger.warning("requested break state - not 
implemented")
                     pass  # XXX needs cached value
                 elif suboption[2:3] == SET_CONTROL_BREAK_ON:
-                    self.serial.setBreak(True)
+                    self.serial.break_condition = True
                     if self.logger:
                         self.logger.info("changed BREAK to active")
                     self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, 
SET_CONTROL_BREAK_ON)
                 elif suboption[2:3] == SET_CONTROL_BREAK_OFF:
-                    self.serial.setBreak(False)
+                    self.serial.break_condition = False
                     if self.logger:
                         self.logger.info("changed BREAK to inactive")
                     self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, 
SET_CONTROL_BREAK_OFF)
@@ -1248,12 +1248,12 @@
                         self.logger.warning("requested DTR state - not 
implemented")
                     pass  # XXX needs cached value
                 elif suboption[2:3] == SET_CONTROL_DTR_ON:
-                    self.serial.setDTR(True)
+                    self.serial.dtr = True
                     if self.logger:
                         self.logger.info("changed DTR to active")
                     self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, 
SET_CONTROL_DTR_ON)
                 elif suboption[2:3] == SET_CONTROL_DTR_OFF:
-                    self.serial.setDTR(False)
+                    self.serial.dtr = False
                     if self.logger:
                         self.logger.info("changed DTR to inactive")
                     self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, 
SET_CONTROL_DTR_OFF)
@@ -1263,12 +1263,12 @@
                     pass  # XXX needs cached value
                     #~ self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, 
SET_CONTROL_RTS_ON)
                 elif suboption[2:3] == SET_CONTROL_RTS_ON:
-                    self.serial.setRTS(True)
+                    self.serial.rts = True
                     if self.logger:
                         self.logger.info("changed RTS to active")
                     self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, 
SET_CONTROL_RTS_ON)
                 elif suboption[2:3] == SET_CONTROL_RTS_OFF:
-                    self.serial.setRTS(False)
+                    self.serial.rts = False
                     if self.logger:
                         self.logger.info("changed RTS to inactive")
                     self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, 
SET_CONTROL_RTS_OFF)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/serialposix.py 
new/pyserial-3.4/serial/serialposix.py
--- old/pyserial-3.3/serial/serialposix.py      2017-03-03 23:36:08.000000000 
+0100
+++ new/pyserial-3.4/serial/serialposix.py      2017-07-14 21:49:14.000000000 
+0200
@@ -503,24 +503,27 @@
                 read.extend(buf)
             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 and e.errno != errno.EINTR:
+                # OSError ignore BlockingIOErrors and EINTR. other errors are 
shown
+                # https://www.python.org/dev/peps/pep-0475.
+                if e.errno not in (errno.EAGAIN, errno.EALREADY, 
errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
                     raise SerialException('read failed: {}'.format(e))
             except select.error as e:
                 # this is for Python 2.x
-                # ignore EAGAIN errors. all other errors are shown
+                # ignore BlockingIOErrors and EINTR. all errors are shown
                 # see also http://www.python.org/dev/peps/pep-3151/#select
-                if e[0] != errno.EAGAIN:
+                if e[0] not in (errno.EAGAIN, errno.EALREADY, 
errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
                     raise SerialException('read failed: {}'.format(e))
             if timeout.expired():
                 break
         return bytes(read)
 
     def cancel_read(self):
-        os.write(self.pipe_abort_read_w, b"x")
+        if self.is_open:
+            os.write(self.pipe_abort_read_w, b"x")
 
     def cancel_write(self):
-        os.write(self.pipe_abort_write_w, b"x")
+        if self.is_open:
+            os.write(self.pipe_abort_write_w, b"x")
 
     def write(self, data):
         """Output the given byte string over the serial port."""
@@ -560,12 +563,20 @@
                 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
+            except OSError as e:
+                # this is for Python 3.x where select.error is a subclass of
+                # OSError ignore BlockingIOErrors and EINTR. other errors are 
shown
+                # https://www.python.org/dev/peps/pep-0475.
+                if e.errno not in (errno.EAGAIN, errno.EALREADY, 
errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
+                    raise SerialException('write failed: {}'.format(e))
+            except select.error as e:
+                # this is for Python 2.x
+                # ignore BlockingIOErrors and EINTR. all errors are shown
+                # see also http://www.python.org/dev/peps/pep-3151/#select
+                if e[0] not in (errno.EAGAIN, errno.EALREADY, 
errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
+                    raise SerialException('write failed: {}'.format(e))
+            if not timeout.is_non_blocking and timeout.expired():
+                raise writeTimeoutError
         return length - len(d)
 
     def flush(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/serialutil.py 
new/pyserial-3.4/serial/serialutil.py
--- old/pyserial-3.3/serial/serialutil.py       2017-03-03 23:36:51.000000000 
+0100
+++ new/pyserial-3.4/serial/serialutil.py       2017-07-13 23:31:06.000000000 
+0200
@@ -557,6 +557,8 @@
     # context manager
 
     def __enter__(self):
+        if not self.is_open:
+            self.open()
         return self
 
     def __exit__(self, *args, **kwargs):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/tools/list_ports.py 
new/pyserial-3.4/serial/tools/list_ports.py
--- old/pyserial-3.3/serial/tools/list_ports.py 2016-06-08 06:05:48.000000000 
+0200
+++ new/pyserial-3.4/serial/tools/list_ports.py 2017-03-16 23:53:54.000000000 
+0100
@@ -34,14 +34,14 @@
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
 
-def grep(regexp):
+def grep(regexp, include_links=False):
     """\
     Search for ports using a regular expression. Port name, description and
     hardware ID are searched. The function returns an iterable that returns the
     same tuples as comport() would do.
     """
     r = re.compile(regexp, re.I)
-    for info in comports():
+    for info in comports(include_links):
         port, desc, hwid = info
         if r.search(port) or r.search(desc) or r.search(hwid):
             yield info
@@ -73,6 +73,11 @@
         type=int,
         help='only output the N-th entry')
 
+    parser.add_argument(
+        '-s', '--include-links',
+        action='store_true',
+        help='include entries that are symlinks to real devices')
+
     args = parser.parse_args()
 
     hits = 0
@@ -80,9 +85,9 @@
     if args.regexp:
         if not args.quiet:
             sys.stderr.write("Filtered list with regexp: 
{!r}\n".format(args.regexp))
-        iterator = sorted(grep(args.regexp))
+        iterator = sorted(grep(args.regexp, include_links=args.include_links))
     else:
-        iterator = sorted(comports())
+        iterator = sorted(comports(include_links=args.include_links))
     # list them
     for n, (port, desc, hwid) in enumerate(iterator, 1):
         if args.n is None or args.n == n:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/tools/list_ports_common.py 
new/pyserial-3.4/serial/tools/list_ports_common.py
--- old/pyserial-3.3/serial/tools/list_ports_common.py  2016-08-08 
01:08:11.000000000 +0200
+++ new/pyserial-3.4/serial/tools/list_ports_common.py  2017-05-24 
03:23:06.000000000 +0200
@@ -8,6 +8,8 @@
 #
 # SPDX-License-Identifier:    BSD-3-Clause
 import re
+import glob
+import os
 
 
 def numsplit(text):
@@ -42,6 +44,9 @@
         self.manufacturer = None
         self.product = None
         self.interface = None
+        # special handling for links
+        if device is not None and os.path.islink(device):
+            self.hwid = 'LINK={}'.format(os.path.realpath(device))
 
     def usb_description(self):
         """return a short string to name the port based on USB info"""
@@ -86,6 +91,18 @@
             raise IndexError('{} > 2'.format(index))
 
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+def list_links(devices):
+    """\
+    search all /dev devices and look for symlinks to known ports already
+    listed in devices.
+    """
+    links = []
+    for device in glob.glob('/dev/*'):
+        if os.path.islink(device) and os.path.realpath(device) in devices:
+            links.append(device)
+    return links
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 # test
 if __name__ == '__main__':
     print(ListPortInfo('dummy'))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/tools/list_ports_linux.py 
new/pyserial-3.4/serial/tools/list_ports_linux.py
--- old/pyserial-3.3/serial/tools/list_ports_linux.py   2016-06-08 
06:05:48.000000000 +0200
+++ new/pyserial-3.4/serial/tools/list_ports_linux.py   2017-05-24 
03:23:27.000000000 +0200
@@ -18,6 +18,12 @@
 
     def __init__(self, device):
         super(SysFS, self).__init__(device)
+        # special handling for links
+        if device is not None and os.path.islink(device):
+            device = os.path.realpath(device)
+            is_link = True
+        else:
+            is_link = False
         self.name = os.path.basename(device)
         self.usb_device_path = None
         if os.path.exists('/sys/class/tty/{}/device'.format(self.name)):
@@ -28,17 +34,28 @@
             self.subsystem = None
         # check device type
         if self.subsystem == 'usb-serial':
-            self.usb_device_path = 
os.path.dirname(os.path.dirname(self.device_path))
+            self.usb_interface_path = os.path.dirname(self.device_path)
         elif self.subsystem == 'usb':
-            self.usb_device_path = os.path.dirname(self.device_path)
+            self.usb_interface_path = self.device_path
         else:
-            self.usb_device_path = None
+            self.usb_interface_path = None
         # fill-in info for USB devices
-        if self.usb_device_path is not None:
+        if self.usb_interface_path is not None:
+            self.usb_device_path = os.path.dirname(self.usb_interface_path)
+
+            try:
+                num_if = int(self.read_line(self.usb_device_path, 
'bNumInterfaces'))
+            except ValueError:
+                num_if = 1
+
             self.vid = int(self.read_line(self.usb_device_path, 'idVendor'), 
16)
             self.pid = int(self.read_line(self.usb_device_path, 'idProduct'), 
16)
             self.serial_number = self.read_line(self.usb_device_path, 'serial')
-            self.location = os.path.basename(self.usb_device_path)
+            if num_if > 1:  # multi interface devices like FT4232
+                self.location = os.path.basename(self.usb_interface_path)
+            else:
+                self.location = os.path.basename(self.usb_device_path)
+
             self.manufacturer = self.read_line(self.usb_device_path, 
'manufacturer')
             self.product = self.read_line(self.usb_device_path, 'product')
             self.interface = self.read_line(self.device_path, 'interface')
@@ -53,6 +70,9 @@
             self.description = self.name
             self.hwid = os.path.basename(self.device_path)
 
+        if is_link:
+            self.hwid += ' LINK={}'.format(device)
+
     def read_line(self, *args):
         """\
         Helper function to read a single line from a file.
@@ -67,12 +87,15 @@
             return None
 
 
-def comports():
+def comports(include_links=False):
     devices = glob.glob('/dev/ttyS*')           # built-in serial ports
     devices.extend(glob.glob('/dev/ttyUSB*'))   # usb-serial with own driver
     devices.extend(glob.glob('/dev/ttyACM*'))   # usb-serial with CDC-ACM 
profile
     devices.extend(glob.glob('/dev/ttyAMA*'))   # ARM internal port (raspi)
     devices.extend(glob.glob('/dev/rfcomm*'))   # BT serial devices
+    devices.extend(glob.glob('/dev/ttyAP*'))    # Advantech multi-port serial 
controllers
+    if include_links:
+        devices.extend(list_ports_common.list_links(devices))
     return [info
             for info in [SysFS(d) for d in devices]
             if info.subsystem != "platform"]    # hide non-present internal 
serial ports
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/tools/list_ports_osx.py 
new/pyserial-3.4/serial/tools/list_ports_osx.py
--- old/pyserial-3.3/serial/tools/list_ports_osx.py     2016-06-08 
06:05:48.000000000 +0200
+++ new/pyserial-3.4/serial/tools/list_ports_osx.py     2017-03-17 
00:01:23.000000000 +0100
@@ -227,7 +227,8 @@
     return None
 
 
-def comports():
+def comports(include_links=False):
+    # XXX include_links is currently ignored. are links in /dev even supported 
here?
     # Scan for all iokit serial ports
     services = GetIOServicesByType('IOSerialBSDClient')
     ports = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/tools/list_ports_posix.py 
new/pyserial-3.4/serial/tools/list_ports_posix.py
--- old/pyserial-3.3/serial/tools/list_ports_posix.py   2016-06-08 
06:05:48.000000000 +0200
+++ new/pyserial-3.4/serial/tools/list_ports_posix.py   2017-03-16 
23:59:33.000000000 +0100
@@ -34,48 +34,64 @@
     # cygwin accepts /dev/com* in many contexts
     # (such as 'open' call, explicit 'ls'), but 'glob.glob'
     # and bare 'ls' do not; so use /dev/ttyS* instead
-    def comports():
+    def comports(include_links=False):
         devices = glob.glob('/dev/ttyS*')
+        if include_links:
+            devices.extend(list_ports_common.list_links(devices))
         return [list_ports_common.ListPortInfo(d) for d in devices]
 
 elif plat[:7] == 'openbsd':    # OpenBSD
-    def comports():
+    def comports(include_links=False):
         devices = glob.glob('/dev/cua*')
+        if include_links:
+            devices.extend(list_ports_common.list_links(devices))
         return [list_ports_common.ListPortInfo(d) for d in devices]
 
 elif plat[:3] == 'bsd' or plat[:7] == 'freebsd':
-    def comports():
+    def comports(include_links=False):
         devices = glob.glob('/dev/cua*[!.init][!.lock]')
+        if include_links:
+            devices.extend(list_ports_common.list_links(devices))
         return [list_ports_common.ListPortInfo(d) for d in devices]
 
 elif plat[:6] == 'netbsd':   # NetBSD
-    def comports():
+    def comports(include_links=False):
         """scan for available ports. return a list of device names."""
         devices = glob.glob('/dev/dty*')
+        if include_links:
+            devices.extend(list_ports_common.list_links(devices))
         return [list_ports_common.ListPortInfo(d) for d in devices]
 
 elif plat[:4] == 'irix':     # IRIX
-    def comports():
+    def comports(include_links=False):
         """scan for available ports. return a list of device names."""
         devices = glob.glob('/dev/ttyf*')
+        if include_links:
+            devices.extend(list_ports_common.list_links(devices))
         return [list_ports_common.ListPortInfo(d) for d in devices]
 
 elif plat[:2] == 'hp':       # HP-UX (not tested)
-    def comports():
+    def comports(include_links=False):
         """scan for available ports. return a list of device names."""
         devices = glob.glob('/dev/tty*p0')
+        if include_links:
+            devices.extend(list_ports_common.list_links(devices))
         return [list_ports_common.ListPortInfo(d) for d in devices]
 
 elif plat[:5] == 'sunos':    # Solaris/SunOS
-    def comports():
+    def comports(include_links=False):
         """scan for available ports. return a list of device names."""
         devices = glob.glob('/dev/tty*c')
+        if include_links:
+            devices.extend(list_ports_common.list_links(devices))
         return [list_ports_common.ListPortInfo(d) for d in devices]
 
 elif plat[:3] == 'aix':      # AIX
-    def comports():
+    def comports(include_links=False):
         """scan for available ports. return a list of device names."""
         devices = glob.glob('/dev/tty*')
+        if include_links:
+            devices.extend(list_ports_common.list_links(devices))
         return [list_ports_common.ListPortInfo(d) for d in devices]
 
 else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/tools/list_ports_windows.py 
new/pyserial-3.4/serial/tools/list_ports_windows.py
--- old/pyserial-3.3/serial/tools/list_ports_windows.py 2016-10-28 
21:46:08.000000000 +0200
+++ new/pyserial-3.4/serial/tools/list_ports_windows.py 2017-05-24 
03:34:50.000000000 +0200
@@ -143,6 +143,7 @@
 
     # repeat for all possible GUIDs
     for index in range(guids_size.value):
+        bInterfaceNumber = None
         g_hdi = SetupDiGetClassDevs(
             ctypes.byref(GUIDs[index]),
             None,
@@ -210,13 +211,15 @@
             # in case of USB, make a more readable string, similar to that form
             # that we also generate on other platforms
             if szHardwareID_str.startswith('USB'):
-                m = 
re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(\\(\w+))?', 
szHardwareID_str, re.I)
+                m = 
re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(\w+))?', 
szHardwareID_str, re.I)
                 if m:
                     info.vid = int(m.group(1), 16)
                     if m.group(3):
                         info.pid = int(m.group(3), 16)
                     if m.group(5):
-                        info.serial_number = m.group(5)
+                        bInterfaceNumber = int(m.group(5))
+                    if m.group(7):
+                        info.serial_number = m.group(7)
                 # calculate a location string
                 loc_path_str = ctypes.create_unicode_buffer(250)
                 if SetupDiGetDeviceRegistryProperty(
@@ -238,6 +241,10 @@
                             else:
                                 location.append('-')
                             location.append(g.group(2))
+                    if bInterfaceNumber is not None:
+                        location.append(':{}.{}'.format(
+                            'x',  # XXX how to determine correct 
bConfigurationValue?
+                            bInterfaceNumber))
                     if location:
                         info.location = ''.join(location)
                 info.hwid = info.usb_info()
@@ -287,7 +294,7 @@
         SetupDiDestroyDeviceInfoList(g_hdi)
 
 
-def comports():
+def comports(include_links=False):
     """Return a list of info objects about serial ports"""
     return list(iterate_comports())
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/tools/miniterm.py 
new/pyserial-3.4/serial/tools/miniterm.py
--- old/pyserial-3.3/serial/tools/miniterm.py   2016-12-06 04:10:38.000000000 
+0100
+++ new/pyserial-3.4/serial/tools/miniterm.py   2017-07-19 22:57:17.000000000 
+0200
@@ -315,7 +315,7 @@
     sys.stderr.write('\n--- Available ports:\n')
     ports = []
     for n, (port, desc, hwid) in enumerate(sorted(comports()), 1):
-        sys.stderr.write('--- {:2}: {:20} {}\n'.format(n, port, desc))
+        sys.stderr.write('--- {:2}: {:20} {!r}\n'.format(n, port, desc))
         ports.append(port)
     while True:
         port = raw_input('--- Enter port index or full name: ')
@@ -502,25 +502,7 @@
             if self.echo:
                 self.console.write(c)
         elif c == '\x15':                       # CTRL+U -> upload file
-            sys.stderr.write('\n--- File to upload: ')
-            sys.stderr.flush()
-            with self.console:
-                filename = sys.stdin.readline().rstrip('\r\n')
-                if filename:
-                    try:
-                        with open(filename, 'rb') as f:
-                            sys.stderr.write('--- Sending file {} 
---\n'.format(filename))
-                            while True:
-                                block = f.read(1024)
-                                if not block:
-                                    break
-                                self.serial.write(block)
-                                # Wait for output buffer to drain.
-                                self.serial.flush()
-                                sys.stderr.write('.')   # Progress indicator.
-                        sys.stderr.write('\n--- File {} sent 
---\n'.format(filename))
-                    except IOError as e:
-                        sys.stderr.write('--- ERROR opening file {}: {} 
---\n'.format(filename, e))
+            self.upload_file()
         elif c in '\x08hH?':                    # CTRL+H, h, H, ? -> Show help
             sys.stderr.write(self.get_help_text())
         elif c == '\x12':                       # CTRL+R -> Toggle RTS
@@ -536,22 +518,7 @@
             self.echo = not self.echo
             sys.stderr.write('--- local echo {} ---\n'.format('active' if 
self.echo else 'inactive'))
         elif c == '\x06':                       # CTRL+F -> edit filters
-            sys.stderr.write('\n--- Available Filters:\n')
-            sys.stderr.write('\n'.join(
-                '---   {:<10} = {.__doc__}'.format(k, v)
-                for k, v in sorted(TRANSFORMATIONS.items())))
-            sys.stderr.write('\n--- Enter new filter name(s) [{}]: '.format(' 
'.join(self.filters)))
-            with self.console:
-                new_filters = sys.stdin.readline().lower().split()
-            if new_filters:
-                for f in new_filters:
-                    if f not in TRANSFORMATIONS:
-                        sys.stderr.write('--- unknown filter: 
{}\n'.format(repr(f)))
-                        break
-                else:
-                    self.filters = new_filters
-                    self.update_transformations()
-            sys.stderr.write('--- filters: {}\n'.format(' 
'.join(self.filters)))
+            self.change_filter()
         elif c == '\x0c':                       # CTRL+L -> EOL mode
             modes = list(EOL_TRANSFORMATIONS)  # keys
             eol = modes.index(self.eol) + 1
@@ -561,63 +528,17 @@
             sys.stderr.write('--- EOL: {} ---\n'.format(self.eol.upper()))
             self.update_transformations()
         elif c == '\x01':                       # CTRL+A -> set encoding
-            sys.stderr.write('\n--- Enter new encoding name [{}]: 
'.format(self.input_encoding))
-            with self.console:
-                new_encoding = sys.stdin.readline().strip()
-            if new_encoding:
-                try:
-                    codecs.lookup(new_encoding)
-                except LookupError:
-                    sys.stderr.write('--- invalid encoding name: 
{}\n'.format(new_encoding))
-                else:
-                    self.set_rx_encoding(new_encoding)
-                    self.set_tx_encoding(new_encoding)
-            sys.stderr.write('--- serial input encoding: 
{}\n'.format(self.input_encoding))
-            sys.stderr.write('--- serial output encoding: 
{}\n'.format(self.output_encoding))
+            self.change_encoding()
         elif c == '\x09':                       # CTRL+I -> info
             self.dump_port_settings()
         #~ elif c == '\x01':                       # CTRL+A -> cycle escape 
mode
         #~ elif c == '\x0c':                       # CTRL+L -> cycle linefeed 
mode
         elif c in 'pP':                         # P -> change port
-            with self.console:
-                try:
-                    port = ask_for_port()
-                except KeyboardInterrupt:
-                    port = None
-            if port and port != self.serial.port:
-                # reader thread needs to be shut down
-                self._stop_reader()
-                # save settings
-                settings = self.serial.getSettingsDict()
-                try:
-                    new_serial = serial.serial_for_url(port, do_not_open=True)
-                    # restore settings and open
-                    new_serial.applySettingsDict(settings)
-                    new_serial.rts = self.serial.rts
-                    new_serial.dtr = self.serial.dtr
-                    new_serial.open()
-                    new_serial.break_condition = self.serial.break_condition
-                except Exception as e:
-                    sys.stderr.write('--- ERROR opening new port: {} 
---\n'.format(e))
-                    new_serial.close()
-                else:
-                    self.serial.close()
-                    self.serial = new_serial
-                    sys.stderr.write('--- Port changed to: {} 
---\n'.format(self.serial.port))
-                # and restart the reader thread
-                self._start_reader()
+            self.change_port()
+        elif c in 'sS':                         # S -> suspend / open port 
temporarily
+            self.suspend_port()
         elif c in 'bB':                         # B -> change baudrate
-            sys.stderr.write('\n--- Baudrate: ')
-            sys.stderr.flush()
-            with self.console:
-                backup = self.serial.baudrate
-                try:
-                    self.serial.baudrate = int(sys.stdin.readline().strip())
-                except ValueError as e:
-                    sys.stderr.write('--- ERROR setting baudrate: {} 
---\n'.format(e))
-                    self.serial.baudrate = backup
-                else:
-                    self.dump_port_settings()
+            self.change_baudrate()
         elif c == '8':                          # 8 -> change to 8 bits
             self.serial.bytesize = serial.EIGHTBITS
             self.dump_port_settings()
@@ -657,6 +578,138 @@
         else:
             sys.stderr.write('--- unknown menu character {} 
--\n'.format(key_description(c)))
 
+    def upload_file(self):
+        """Ask user for filenname and send its contents"""
+        sys.stderr.write('\n--- File to upload: ')
+        sys.stderr.flush()
+        with self.console:
+            filename = sys.stdin.readline().rstrip('\r\n')
+            if filename:
+                try:
+                    with open(filename, 'rb') as f:
+                        sys.stderr.write('--- Sending file {} 
---\n'.format(filename))
+                        while True:
+                            block = f.read(1024)
+                            if not block:
+                                break
+                            self.serial.write(block)
+                            # Wait for output buffer to drain.
+                            self.serial.flush()
+                            sys.stderr.write('.')   # Progress indicator.
+                    sys.stderr.write('\n--- File {} sent 
---\n'.format(filename))
+                except IOError as e:
+                    sys.stderr.write('--- ERROR opening file {}: {} 
---\n'.format(filename, e))
+
+    def change_filter(self):
+        """change the i/o transformations"""
+        sys.stderr.write('\n--- Available Filters:\n')
+        sys.stderr.write('\n'.join(
+            '---   {:<10} = {.__doc__}'.format(k, v)
+            for k, v in sorted(TRANSFORMATIONS.items())))
+        sys.stderr.write('\n--- Enter new filter name(s) [{}]: '.format(' 
'.join(self.filters)))
+        with self.console:
+            new_filters = sys.stdin.readline().lower().split()
+        if new_filters:
+            for f in new_filters:
+                if f not in TRANSFORMATIONS:
+                    sys.stderr.write('--- unknown filter: 
{}\n'.format(repr(f)))
+                    break
+            else:
+                self.filters = new_filters
+                self.update_transformations()
+        sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters)))
+
+    def change_encoding(self):
+        """change encoding on the serial port"""
+        sys.stderr.write('\n--- Enter new encoding name [{}]: 
'.format(self.input_encoding))
+        with self.console:
+            new_encoding = sys.stdin.readline().strip()
+        if new_encoding:
+            try:
+                codecs.lookup(new_encoding)
+            except LookupError:
+                sys.stderr.write('--- invalid encoding name: 
{}\n'.format(new_encoding))
+            else:
+                self.set_rx_encoding(new_encoding)
+                self.set_tx_encoding(new_encoding)
+        sys.stderr.write('--- serial input encoding: 
{}\n'.format(self.input_encoding))
+        sys.stderr.write('--- serial output encoding: 
{}\n'.format(self.output_encoding))
+
+    def change_baudrate(self):
+        """change the baudrate"""
+        sys.stderr.write('\n--- Baudrate: ')
+        sys.stderr.flush()
+        with self.console:
+            backup = self.serial.baudrate
+            try:
+                self.serial.baudrate = int(sys.stdin.readline().strip())
+            except ValueError as e:
+                sys.stderr.write('--- ERROR setting baudrate: {} 
---\n'.format(e))
+                self.serial.baudrate = backup
+            else:
+                self.dump_port_settings()
+
+    def change_port(self):
+        """Have a conversation with the user to change the serial port"""
+        with self.console:
+            try:
+                port = ask_for_port()
+            except KeyboardInterrupt:
+                port = None
+        if port and port != self.serial.port:
+            # reader thread needs to be shut down
+            self._stop_reader()
+            # save settings
+            settings = self.serial.getSettingsDict()
+            try:
+                new_serial = serial.serial_for_url(port, do_not_open=True)
+                # restore settings and open
+                new_serial.applySettingsDict(settings)
+                new_serial.rts = self.serial.rts
+                new_serial.dtr = self.serial.dtr
+                new_serial.open()
+                new_serial.break_condition = self.serial.break_condition
+            except Exception as e:
+                sys.stderr.write('--- ERROR opening new port: {} 
---\n'.format(e))
+                new_serial.close()
+            else:
+                self.serial.close()
+                self.serial = new_serial
+                sys.stderr.write('--- Port changed to: {} 
---\n'.format(self.serial.port))
+            # and restart the reader thread
+            self._start_reader()
+
+    def suspend_port(self):
+        """\
+        open port temporarily, allow reconnect, exit and port change to get
+        out of the loop
+        """
+        # reader thread needs to be shut down
+        self._stop_reader()
+        self.serial.close()
+        sys.stderr.write('\n--- Port closed: {} 
---\n'.format(self.serial.port))
+        do_change_port = False
+        while not self.serial.is_open:
+            sys.stderr.write('--- Quit: {exit} | p: port change | any other 
key to reconnect ---\n'.format(
+                exit=key_description(self.exit_character)))
+            k = self.console.getkey()
+            if k == self.exit_character:
+                self.stop()             # exit app
+                break
+            elif k in 'pP':
+                do_change_port = True
+                break
+            try:
+                self.serial.open()
+            except Exception as e:
+                sys.stderr.write('--- ERROR opening port: {} ---\n'.format(e))
+        if do_change_port:
+            self.change_port()
+        else:
+            # and restart the reader thread
+            self._start_reader()
+            sys.stderr.write('--- Port opened: {} 
---\n'.format(self.serial.port))
+
     def get_help_text(self):
         """return the help text"""
         # help text, starts with blank line!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/serial/urlhandler/protocol_socket.py 
new/pyserial-3.4/serial/urlhandler/protocol_socket.py
--- old/pyserial-3.3/serial/urlhandler/protocol_socket.py       2016-12-18 
06:20:07.000000000 +0100
+++ new/pyserial-3.4/serial/urlhandler/protocol_socket.py       2017-05-06 
23:16:12.000000000 +0200
@@ -170,14 +170,15 @@
                 read.extend(buf)
             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:
+                # OSError ignore BlockingIOErrors and EINTR. other errors are 
shown
+                # https://www.python.org/dev/peps/pep-0475.
+                if e.errno not in (errno.EAGAIN, errno.EALREADY, 
errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
                     raise SerialException('read failed: {}'.format(e))
             except (select.error, socket.error) as e:
                 # this is for Python 2.x
-                # ignore EAGAIN errors. all other errors are shown
+                # ignore BlockingIOErrors and EINTR. all errors are shown
                 # see also http://www.python.org/dev/peps/pep-3151/#select
-                if e[0] != errno.EAGAIN:
+                if e[0] not in (errno.EAGAIN, errno.EALREADY, 
errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
                     raise SerialException('read failed: {}'.format(e))
             if timeout.expired():
                 break
@@ -220,12 +221,20 @@
                 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
+            except OSError as e:
+                # this is for Python 3.x where select.error is a subclass of
+                # OSError ignore BlockingIOErrors and EINTR. other errors are 
shown
+                # https://www.python.org/dev/peps/pep-0475.
+                if e.errno not in (errno.EAGAIN, errno.EALREADY, 
errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
+                    raise SerialException('write failed: {}'.format(e))
+            except select.error as e:
+                # this is for Python 2.x
+                # ignore BlockingIOErrors and EINTR. all errors are shown
+                # see also http://www.python.org/dev/peps/pep-3151/#select
+                if e[0] not in (errno.EAGAIN, errno.EALREADY, 
errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
+                    raise SerialException('write failed: {}'.format(e))
+            if not timeout.is_non_blocking and timeout.expired():
+                raise writeTimeoutError
         return length - len(d)
 
     def reset_input_buffer(self):
@@ -241,15 +250,16 @@
                 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))
+                # OSError ignore BlockingIOErrors and EINTR. other errors are 
shown
+                # https://www.python.org/dev/peps/pep-0475.
+                if e.errno not in (errno.EAGAIN, errno.EALREADY, 
errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
+                    raise SerialException('read failed: {}'.format(e))
             except (select.error, socket.error) as e:
                 # this is for Python 2.x
-                # ignore EAGAIN errors. all other errors are shown
+                # ignore BlockingIOErrors and EINTR. all 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))
+                if e[0] not in (errno.EAGAIN, errno.EALREADY, 
errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
+                    raise SerialException('read failed: {}'.format(e))
 
     def reset_output_buffer(self):
         """\
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/setup.cfg new/pyserial-3.4/setup.cfg
--- old/pyserial-3.3/setup.cfg  2017-03-08 02:44:53.000000000 +0100
+++ new/pyserial-3.4/setup.cfg  2017-07-22 22:51:52.000000000 +0200
@@ -6,7 +6,6 @@
 ignore = E265, E126, E241
 
 [egg_info]
-tag_svn_revision = 0
-tag_date = 0
 tag_build = 
+tag_date = 0
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/setup.py new/pyserial-3.4/setup.py
--- old/pyserial-3.3/setup.py   2016-06-18 21:41:56.000000000 +0200
+++ new/pyserial-3.4/setup.py   2017-03-20 23:21:15.000000000 +0100
@@ -6,7 +6,7 @@
 # For Python 3.x use the corresponding Python executable,
 # e.g. "python3 setup.py ..."
 #
-# (C) 2001-2016 Chris Liechti <cliec...@gmx.net>
+# (C) 2001-2017 Chris Liechti <cliec...@gmx.net>
 #
 # SPDX-License-Identifier:    BSD-3-Clause
 import io
@@ -89,6 +89,7 @@
         'Programming Language :: Python :: 3.3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
         'Topic :: Communications',
         'Topic :: Software Development :: Libraries',
         'Topic :: Software Development :: Libraries :: Python Modules',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/test/test_context.py 
new/pyserial-3.4/test/test_context.py
--- old/pyserial-3.3/test/test_context.py       1970-01-01 01:00:00.000000000 
+0100
+++ new/pyserial-3.4/test/test_context.py       2017-07-13 23:31:06.000000000 
+0200
@@ -0,0 +1,49 @@
+#! /usr/bin/env python
+#
+# This file is part of pySerial - Cross platform serial port support for Python
+# (C) 2017 Guillaume Galeazzi <guillaum...@leazzi.ch>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+"""\
+Some tests for the serial module.
+Part of pySerial (http://pyserial.sf.net)  (C)2001-2011 cliec...@gmx.net
+
+Intended to be run on different platforms, to ensure portability of
+the code.
+
+Cover some of the aspects of context managment
+"""
+
+import unittest
+import serial
+
+# on which port should the tests be performed:
+PORT = 'loop://'
+
+
+class Test_Context(unittest.TestCase):
+    """Test context"""
+
+    def setUp(self):
+        # create a closed serial port
+        self.s = serial.serial_for_url(PORT)
+
+    def tearDown(self):
+        self.s.close()
+
+    def test_with_idempotent(self):
+        with self.s as stream:
+            stream.write(b'1234')
+
+        # do other stuff like calling an exe which use COM4
+
+        with self.s as stream:
+            stream.write(b'5678')
+
+
+if __name__ == '__main__':
+    import sys
+    sys.stdout.write(__doc__)
+    sys.argv[1:] = ['-v']
+    # When this module is executed from the command-line, it runs all its tests
+    unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyserial-3.3/test/test_pty.py 
new/pyserial-3.4/test/test_pty.py
--- old/pyserial-3.3/test/test_pty.py   2016-05-10 23:47:12.000000000 +0200
+++ new/pyserial-3.4/test/test_pty.py   2017-05-03 18:33:25.000000000 +0200
@@ -18,6 +18,7 @@
 import unittest
 import serial
 
+DATA = b'Hello\n'
 
 @unittest.skipIf(pty is None, "pty module not supported on platform")
 class Test_Pty_Serial_Open(unittest.TestCase):
@@ -27,11 +28,50 @@
         # Open PTY
         self.master, self.slave = pty.openpty()
 
-    def test_pty_serial_open(self):
-        """Open serial port on slave"""
-        ser = serial.Serial(os.ttyname(self.slave))
-        ser.close()
-
+    def test_pty_serial_open_slave(self):
+        with serial.Serial(os.ttyname(self.slave), timeout=1) as slave:
+            pass  # OK
+
+    def test_pty_serial_write(self):
+        with serial.Serial(os.ttyname(self.slave), timeout=1) as slave:
+            with os.fdopen(self.master, "wb") as fd:
+                fd.write(DATA)
+                fd.flush()
+                out = slave.read(len(DATA))
+                self.assertEqual(DATA, out)
+
+    def test_pty_serial_read(self):
+        with serial.Serial(os.ttyname(self.slave), timeout=1) as slave:
+            with os.fdopen(self.master, "rb") as fd:
+                slave.write(DATA)
+                slave.flush()
+                out = fd.read(len(DATA))
+                self.assertEqual(DATA, out)
+
+    #~ def test_pty_serial_master_read(self):
+        #~ with serial.Serial(os.ttyname(self.master), timeout=1) as master:
+            #~ with os.fdopen(self.slave, "wb") as fd:
+                #~ fd.write(DATA)
+                #~ fd.flush()
+                #~ out = master.read(len(DATA))
+                #~ self.assertEqual(DATA, out)
+
+    #~ def test_pty_serial_master_write(self):
+        #~ with serial.Serial(os.ttyname(self.master), timeout=1) as master:
+            #~ with os.fdopen(self.slave, "rb") as fd:
+                #~ master.write(DATA)
+                #~ master.flush()
+                #~ out = fd.read(len(DATA))
+                #~ self.assertEqual(DATA, out)
+
+    #~ def test_pty_bidirectional(self):
+        #~ with serial.Serial(os.ttyname(self.master), timeout=1) as master:
+            #~ with serial.Serial(os.ttyname(self.slave), timeout=1) as slave:
+            #~ with os.fdopen(self.master, "rb") as fd:
+                #~ master.write(DATA)
+                #~ master.flush()
+                #~ out = slave.read(len(DATA))
+                #~ self.assertEqual(DATA, out)
 
 if __name__ == '__main__':
     sys.stdout.write(__doc__)


Reply via email to