Hello community,

here is the log from the commit of package python-txaio for openSUSE:Leap:15.2 
checked in at 2020-03-02 13:24:19
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Leap:15.2/python-txaio (Old)
 and      /work/SRC/openSUSE:Leap:15.2/.python-txaio.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-txaio"

Mon Mar  2 13:24:19 2020 rev:11 rq:777241 version:20.1.1

Changes:
--------
--- /work/SRC/openSUSE:Leap:15.2/python-txaio/python-txaio.changes      
2020-01-15 15:53:56.779615201 +0100
+++ /work/SRC/openSUSE:Leap:15.2/.python-txaio.new.26092/python-txaio.changes   
2020-03-02 13:24:20.134515060 +0100
@@ -1,0 +2,41 @@
+Wed Feb  5 13:11:01 UTC 2020 - Ondřej Súkup <[email protected]>
+
+- update to 20.1.1
+- drop pytest4.patch
+ * new: moved time_ns and perf_counter_ns helper functions here
+ * drop support for python 3.4 and older
+
+-------------------------------------------------------------------
+Fri Sep 13 11:40:25 UTC 2019 - Tomáš Chvátal <[email protected]>
+
+- Add patch to build with newer pytest releases:
+  * pytest4.patch
+
+-------------------------------------------------------------------
+Thu Sep  5 13:40:10 UTC 2019 - Todd R <[email protected]>
+
+- Set pytest maximum version.
+
+-------------------------------------------------------------------
+Tue Mar 19 12:35:11 UTC 2019 - John Vandenberg <[email protected]>
+
+- Fix invocation of test suite, previously silently not running
+- Add missing runtime dependencies
+- Update to v18.8.1
+  * add API to support cancellation; this means passing a 1-argument
+    callable to ``create_future`` and ``txaio.cancel`` to actually
+    cancel a future
+  * support Python 3.7 (CI / testing added)
+- from v18.7.1
+  * move to calver
+  * deprecate Python 3.3 support and CI testing
+- from v2.10.0
+  * the asyncio version of ``make_logger`` now deduces a proper
+    namespace instead of using the root
+
+-------------------------------------------------------------------
+Tue Dec  4 12:55:27 UTC 2018 - Matej Cepl <[email protected]>
+
+- Remove superfluous devel dependency for noarch package
+
+-------------------------------------------------------------------

Old:
----
  txaio-2.9.0.tar.gz

New:
----
  txaio-20.1.1.tar.gz

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

Other differences:
------------------
++++++ python-txaio.spec ++++++
--- /var/tmp/diff_new_pack.u5yAfR/_old  2020-03-02 13:24:20.482515752 +0100
+++ /var/tmp/diff_new_pack.u5yAfR/_new  2020-03-02 13:24:20.482515752 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-txaio
 #
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -12,29 +12,34 @@
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
+# Please submit bugfixes or comments via https://bugs.opensuse.org/
+#
 
 
+%define skip_python2 1
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-txaio
-Version:        2.9.0
+Version:        20.1.1
 Release:        0
-License:        MIT
 Summary:        WebSocket and WAMP in Python for Twisted and asyncio
-Url:            http://crossbar.io/autobahn
+License:        MIT
 Group:          Development/Languages/Python
+URL:            https://github.com/crossbario/txaio
 Source:         
https://files.pythonhosted.org/packages/source/t/txaio/txaio-%{version}.tar.gz
-BuildRequires:  python-rpm-macros
-BuildRequires:  %{python_module devel}
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
+BuildRequires:  python-rpm-macros
+BuildRequires:  python3-testsuite
+Requires:       python-six
+Recommends:     python-Twisted >= 12.1.0
+Recommends:     python-zope.interface >= 3.6
+BuildArch:      noarch
 # SECTION test requirements
-BuildRequires:  %{python_module Twisted}
+BuildRequires:  %{python_module Twisted >= 12.1.0}
+BuildRequires:  %{python_module mock}
+BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module six}
 # /SECTION
-Requires:       python-six
-BuildArch:      noarch
-
 %python_subpackages
 
 %description
@@ -52,10 +57,10 @@
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 
 %check
-%python_exec setup.py test
+%pytest -k 'not test_sdist'
 
 %files %{python_files}
-%doc LICENSE
+%license LICENSE
 %{python_sitelib}/*
 
 %changelog

++++++ txaio-2.9.0.tar.gz -> txaio-20.1.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/PKG-INFO new/txaio-20.1.1/PKG-INFO
--- old/txaio-2.9.0/PKG-INFO    2018-03-02 08:47:02.000000000 +0100
+++ new/txaio-20.1.1/PKG-INFO   2020-01-21 10:09:51.737460900 +0100
@@ -1,12 +1,11 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: txaio
-Version: 2.9.0
+Version: 20.1.1
 Summary: Compatibility API between asyncio/Twisted/Trollius
 Home-page: https://github.com/crossbario/txaio
 Author: Crossbar.io Technologies GmbH
 Author-email: [email protected]
 License: MIT License
-Description-Content-Type: UNKNOWN
 Description: txaio
         =====
         
@@ -31,20 +30,12 @@
         Platform support
         ----------------
         
-        **txaio** runs on CPython 2.7/3.3+ and PyPy 2/3, on top of Twisted or 
asyncio. Specifically, **txaio** is tested on the following platforms:
+        **txaio** runs on CPython 3.5+ and PyPy 3, on top of *Twisted* or 
*asyncio*. Specifically, **txaio** is tested on the following platforms:
         
-        **Python 2:**
+        * CPython 3.5, 3.7 and 3.8 on Twisted 18.7, 19.10, trunk and on 
asyncio (stdlib)
+        * PyPy 3 on Twisted 18.7, 19.10, trunk and on asyncio (stdlib)
         
-        * CPython 2.7 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 
2.0
-        * PyPy 2 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
-        
-        **Python 3:**
-        
-        * CPython 3.3 on Twisted 15.4, 16.5, trunk and on Trollius 2.0
-        * CPython 3.4 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-        * CPython 3.5 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-        * CPython 3.6 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-        * PyPy 3 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
+        > Note: txaio up to version 18.8.1 also supported Python 2.7 and 
Python 3.4. Beginning with release v20.1.1, txaio only supports Python 3.5+.
         
         
         How it works
@@ -99,14 +90,17 @@
 Classifier: Intended Audience :: Developers
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-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: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Software Development :: Libraries
 Classifier: Topic :: Software Development :: Libraries :: Application 
Frameworks
+Requires-Python: >=3.5
+Provides-Extra: twisted
+Provides-Extra: all
+Provides-Extra: dev
+Provides-Extra: asyncio
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/README.rst new/txaio-20.1.1/README.rst
--- old/txaio-2.9.0/README.rst  2017-09-23 21:50:59.000000000 +0200
+++ new/txaio-20.1.1/README.rst 2020-01-21 10:06:33.000000000 +0100
@@ -22,20 +22,12 @@
 Platform support
 ----------------
 
-**txaio** runs on CPython 2.7/3.3+ and PyPy 2/3, on top of Twisted or asyncio. 
Specifically, **txaio** is tested on the following platforms:
+**txaio** runs on CPython 3.5+ and PyPy 3, on top of *Twisted* or *asyncio*. 
Specifically, **txaio** is tested on the following platforms:
 
-**Python 2:**
+* CPython 3.5, 3.7 and 3.8 on Twisted 18.7, 19.10, trunk and on asyncio 
(stdlib)
+* PyPy 3 on Twisted 18.7, 19.10, trunk and on asyncio (stdlib)
 
-* CPython 2.7 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
-* PyPy 2 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
-
-**Python 3:**
-
-* CPython 3.3 on Twisted 15.4, 16.5, trunk and on Trollius 2.0
-* CPython 3.4 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-* CPython 3.5 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-* CPython 3.6 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-* PyPy 3 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
+> Note: txaio up to version 18.8.1 also supported Python 2.7 and Python 3.4. 
Beginning with release v20.1.1, txaio only supports Python 3.5+.
 
 
 How it works
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/docs/api.rst 
new/txaio-20.1.1/docs/api.rst
--- old/txaio-2.9.0/docs/api.rst        2016-11-06 20:03:50.000000000 +0100
+++ new/txaio-20.1.1/docs/api.rst       2020-01-21 00:10:08.000000000 +0100
@@ -71,19 +71,23 @@
     Select the Twisted framework (will fail if Twisted is not installed).
 
 
-.. py:function:: create_future(value=None, error=None)
+.. py:function:: create_future(result=None, error=None, canceller=None)
 
     Create and return a new framework-specific future object. On
     asyncio this returns a `Future`_, on Twisted it returns a
     `Deferred`_.
 
-    :param value: if not ``None``, the future is already fulfilled,
+    :param result: if not ``None``, the future is already fulfilled,
         with the given result.
 
     :param error: if not ``None`` then the future is already failed,
         with the given error.
     :type error: class:`IFailedFuture` or Exception
 
+    :param canceller: a single-argument callable which is invoked if
+        this future is cancelled (the single argument is the future
+        object which has been cancelled)
+
     :raises ValueError: if both ``value`` and ``error`` are provided.
 
     :return: under Twisted a `Deferred`_, under asyncio a `Future`_
@@ -129,6 +133,16 @@
     :type error: :class:`IFailedFuture` or :class:`Exception`
 
 
+.. py:function:: cancel(future)
+
+    Cancel the given future. If a ``canceller`` was registered, it is
+    invoked now. It is invalid to ``resolve`` or ``reject`` the future
+    after cancelling it.
+
+    :param future: an unresolved `Deferred`_/`Future`_ as returned by
+                    :meth:`create_future`
+
+
 .. py:function:: resolve(future, value)
 
     Resolve the given future with the provided value. This triggers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/docs/index.rst 
new/txaio-20.1.1/docs/index.rst
--- old/txaio-2.9.0/docs/index.rst      2017-09-23 21:50:59.000000000 +0200
+++ new/txaio-20.1.1/docs/index.rst     2020-01-21 10:06:33.000000000 +0100
@@ -22,20 +22,12 @@
 Platform support
 ----------------
 
-**txaio** runs on CPython 2.7/3.3+ and PyPy 2/3, on top of Twisted or asyncio. 
Specifically, **txaio** is tested on the following platforms:
+**txaio** runs on CPython 3.5+ and PyPy 3, on top of *Twisted* or *asyncio*. 
Specifically, **txaio** is tested on the following platforms:
 
-**Python 2:**
+* CPython 3.5, 3.7 and 3.8 on Twisted 18.7, 19.10, trunk and on asyncio 
(stdlib)
+* PyPy 3 on Twisted 18.7, 19.10, trunk and on asyncio (stdlib)
 
-* CPython 2.7 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
-* PyPy 2 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
-
-**Python 3:**
-
-* CPython 3.3 on Twisted 15.4, 16.5, trunk and on Trollius 2.0
-* CPython 3.4 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-* CPython 3.5 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-* CPython 3.6 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-* PyPy 3 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
+> Note: txaio up to version 18.8.1 also supported Python 2.7 and Python 3.4. 
Beginning with release v20.1.1, txaio only supports Python 3.5+.
 
 
 How it works
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/docs/releases.rst 
new/txaio-20.1.1/docs/releases.rst
--- old/txaio-2.9.0/docs/releases.rst   2018-03-02 08:07:17.000000000 +0100
+++ new/txaio-20.1.1/docs/releases.rst  2020-01-21 10:06:33.000000000 +0100
@@ -1,6 +1,37 @@
 txio releases
 =============
 
+20.1.1
+------
+
+- IMPORTANT: beginning release v20.1.1, we only support Python 3.5 or later
+- new: moved ``time_ns`` and ``perf_counter_ns`` helper functions here
+
+18.8.1
+------
+
+* IMPORTANT: release v18.8.1 is the last release supporting Python 2. We will 
support Python 3.5 and later beginning with release v20.1.1.
+- add API to support cancellation; this means passing a 1-argument
+  callable to ``create_future`` and ``txaio.cancel`` to actually
+  cancel a future
+- support Python 3.7 (CI / testing added)
+
+
+18.7.1
+------
+
+- move to calver
+- deprecate Python 3.3 support and CI testing
+
+
+2.10.0
+------
+
+- the asyncio version of ``make_logger`` now deduces a proper
+  namespace instead of using the root (thanks `spr0cketeer
+  <https://github.com/spr0cketeer>`_)
+
+
 2.9.0
 -----
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/setup.py new/txaio-20.1.1/setup.py
--- old/txaio-2.9.0/setup.py    2017-09-04 14:14:03.000000000 +0200
+++ new/txaio-20.1.1/setup.py   2020-01-21 10:06:33.000000000 +0100
@@ -48,27 +48,14 @@
     'twisted>=12.1.0',                  # MIT
 ]
 
-# asyncio dependencies
+# asyncio dependencies: Python 3.5+ has asyncio builtin
 #
-if PY3:
-    if PY33:
-        # "Tulip"
-        extras_require_asyncio = [
-            "asyncio>=3.4.3"            # Apache 2.0
-        ]
-    else:
-        # Python 3.4+ has asyncio builtin
-        extras_require_asyncio = []
-else:
-    # backport of asyncio for Python 2
-    extras_require_asyncio = [
-        "trollius>=2.0",                # Apache 2.0
-        "futures>=3.0.3"                # BSD license
-    ]
+extras_require_asyncio = []
 
 # development dependencies
 #
 extras_require_dev = [
+    'wheel',                            # MIT
     'pytest>=2.6.4',                    # MIT
     'pytest-cov>=1.8.1',                # MIT
     'pep8>=1.6.2',                      # MIT
@@ -96,6 +83,7 @@
     author_email='[email protected]',
     url='https://github.com/crossbario/txaio',
     platforms=('Any'),
+    python_requires='>=3.5',
     install_requires=[
         'six'
     ],
@@ -128,13 +116,11 @@
         "Intended Audience :: Developers",
         "Operating System :: OS Independent",
         "Programming Language :: Python",
-        "Programming Language :: Python :: 2",
-        "Programming Language :: Python :: 2.7",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.3",
-        "Programming Language :: Python :: 3.4",
         "Programming Language :: Python :: 3.5",
         "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: Implementation :: CPython",
         "Programming Language :: Python :: Implementation :: PyPy",
         "Topic :: Software Development :: Libraries",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/test/_asyncio_test_utils.py 
new/txaio-20.1.1/test/_asyncio_test_utils.py
--- old/txaio-2.9.0/test/_asyncio_test_utils.py 1970-01-01 01:00:00.000000000 
+0100
+++ new/txaio-20.1.1/test/_asyncio_test_utils.py        2020-01-21 
00:10:08.000000000 +0100
@@ -0,0 +1,568 @@
+"""Utilities shared by tests."""
+
+import collections
+import contextlib
+import io
+import logging
+import os
+import re
+import selectors
+import socket
+import socketserver
+import sys
+import tempfile
+import threading
+import time
+import unittest
+import weakref
+
+from unittest import mock
+
+from http.server import HTTPServer
+from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
+
+try:
+    import ssl
+except ImportError:  # pragma: no cover
+    ssl = None
+
+from asyncio import base_events
+from asyncio import events
+from asyncio import format_helpers
+from asyncio import futures
+from asyncio import tasks
+from asyncio.log import logger
+from test import support
+
+
+def data_file(filename):
+    if hasattr(support, 'TEST_HOME_DIR'):
+        fullname = os.path.join(support.TEST_HOME_DIR, filename)
+        if os.path.isfile(fullname):
+            return fullname
+    fullname = os.path.join(os.path.dirname(__file__), filename)
+    if os.path.isfile(fullname):
+        return fullname
+    raise FileNotFoundError(filename)
+
+
+ONLYCERT = data_file('ssl_cert.pem')
+ONLYKEY = data_file('ssl_key.pem')
+SIGNED_CERTFILE = data_file('keycert3.pem')
+SIGNING_CA = data_file('pycacert.pem')
+PEERCERT = {
+    'OCSP': ('http://testca.pythontest.net/testca/ocsp/',),
+    'caIssuers': ('http://testca.pythontest.net/testca/pycacert.cer',),
+    'crlDistributionPoints': 
('http://testca.pythontest.net/testca/revocation.crl',),
+    'issuer': ((('countryName', 'XY'),),
+            (('organizationName', 'Python Software Foundation CA'),),
+            (('commonName', 'our-ca-server'),)),
+    'notAfter': 'Jul  7 14:23:16 2028 GMT',
+    'notBefore': 'Aug 29 14:23:16 2018 GMT',
+    'serialNumber': 'CB2D80995A69525C',
+    'subject': ((('countryName', 'XY'),),
+             (('localityName', 'Castle Anthrax'),),
+             (('organizationName', 'Python Software Foundation'),),
+             (('commonName', 'localhost'),)),
+    'subjectAltName': (('DNS', 'localhost'),),
+    'version': 3
+}
+
+
+def simple_server_sslcontext():
+    server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+    server_context.load_cert_chain(ONLYCERT, ONLYKEY)
+    server_context.check_hostname = False
+    server_context.verify_mode = ssl.CERT_NONE
+    return server_context
+
+
+def simple_client_sslcontext(*, disable_verify=True):
+    client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+    client_context.check_hostname = False
+    if disable_verify:
+        client_context.verify_mode = ssl.CERT_NONE
+    return client_context
+
+
+def dummy_ssl_context():
+    if ssl is None:
+        return None
+    else:
+        return ssl.SSLContext(ssl.PROTOCOL_TLS)
+
+
+def run_briefly(loop):
+    async def once():
+        pass
+    gen = once()
+    t = loop.create_task(gen)
+    # Don't log a warning if the task is not done after run_until_complete().
+    # It occurs if the loop is stopped or if a task raises a BaseException.
+    t._log_destroy_pending = False
+    try:
+        loop.run_until_complete(t)
+    finally:
+        gen.close()
+
+
+def run_until(loop, pred, timeout=30):
+    deadline = time.time() + timeout
+    while not pred():
+        if timeout is not None:
+            timeout = deadline - time.time()
+            if timeout <= 0:
+                raise futures.TimeoutError()
+        loop.run_until_complete(tasks.sleep(0.001, loop=loop))
+
+
+def run_once(loop):
+    """Legacy API to run once through the event loop.
+
+    This is the recommended pattern for test code.  It will poll the
+    selector once and run all callbacks scheduled in response to I/O
+    events.
+    """
+    loop.call_soon(loop.stop)
+    loop.run_forever()
+
+
+class SilentWSGIRequestHandler(WSGIRequestHandler):
+
+    def get_stderr(self):
+        return io.StringIO()
+
+    def log_message(self, format, *args):
+        pass
+
+
+class SilentWSGIServer(WSGIServer):
+
+    request_timeout = 2
+
+    def get_request(self):
+        request, client_addr = super().get_request()
+        request.settimeout(self.request_timeout)
+        return request, client_addr
+
+    def handle_error(self, request, client_address):
+        pass
+
+
+class SSLWSGIServerMixin:
+
+    def finish_request(self, request, client_address):
+        # The relative location of our test directory (which
+        # contains the ssl key and certificate files) differs
+        # between the stdlib and stand-alone asyncio.
+        # Prefer our own if we can find it.
+        here = os.path.join(os.path.dirname(__file__), '..', 'tests')
+        if not os.path.isdir(here):
+            here = os.path.join(os.path.dirname(os.__file__),
+                                'test', 'test_asyncio')
+        keyfile = os.path.join(here, 'ssl_key.pem')
+        certfile = os.path.join(here, 'ssl_cert.pem')
+        context = ssl.SSLContext()
+        context.load_cert_chain(certfile, keyfile)
+
+        ssock = context.wrap_socket(request, server_side=True)
+        try:
+            self.RequestHandlerClass(ssock, client_address, self)
+            ssock.close()
+        except OSError:
+            # maybe socket has been closed by peer
+            pass
+
+
+class SSLWSGIServer(SSLWSGIServerMixin, SilentWSGIServer):
+    pass
+
+
+def _run_test_server(*, address, use_ssl=False, server_cls, server_ssl_cls):
+
+    def app(environ, start_response):
+        status = '200 OK'
+        headers = [('Content-type', 'text/plain')]
+        start_response(status, headers)
+        return [b'Test message']
+
+    # Run the test WSGI server in a separate thread in order not to
+    # interfere with event handling in the main thread
+    server_class = server_ssl_cls if use_ssl else server_cls
+    httpd = server_class(address, SilentWSGIRequestHandler)
+    httpd.set_app(app)
+    httpd.address = httpd.server_address
+    server_thread = threading.Thread(
+        target=lambda: httpd.serve_forever(poll_interval=0.05))
+    server_thread.start()
+    try:
+        yield httpd
+    finally:
+        httpd.shutdown()
+        httpd.server_close()
+        server_thread.join()
+
+
+if hasattr(socket, 'AF_UNIX'):
+
+    class UnixHTTPServer(socketserver.UnixStreamServer, HTTPServer):
+
+        def server_bind(self):
+            socketserver.UnixStreamServer.server_bind(self)
+            self.server_name = '127.0.0.1'
+            self.server_port = 80
+
+
+    class UnixWSGIServer(UnixHTTPServer, WSGIServer):
+
+        request_timeout = 2
+
+        def server_bind(self):
+            UnixHTTPServer.server_bind(self)
+            self.setup_environ()
+
+        def get_request(self):
+            request, client_addr = super().get_request()
+            request.settimeout(self.request_timeout)
+            # Code in the stdlib expects that get_request
+            # will return a socket and a tuple (host, port).
+            # However, this isn't true for UNIX sockets,
+            # as the second return value will be a path;
+            # hence we return some fake data sufficient
+            # to get the tests going
+            return request, ('127.0.0.1', '')
+
+
+    class SilentUnixWSGIServer(UnixWSGIServer):
+
+        def handle_error(self, request, client_address):
+            pass
+
+
+    class UnixSSLWSGIServer(SSLWSGIServerMixin, SilentUnixWSGIServer):
+        pass
+
+
+    def gen_unix_socket_path():
+        with tempfile.NamedTemporaryFile() as file:
+            return file.name
+
+
+    @contextlib.contextmanager
+    def unix_socket_path():
+        path = gen_unix_socket_path()
+        try:
+            yield path
+        finally:
+            try:
+                os.unlink(path)
+            except OSError:
+                pass
+
+
+    @contextlib.contextmanager
+    def run_test_unix_server(*, use_ssl=False):
+        with unix_socket_path() as path:
+            yield from _run_test_server(address=path, use_ssl=use_ssl,
+                                        server_cls=SilentUnixWSGIServer,
+                                        server_ssl_cls=UnixSSLWSGIServer)
+
+
[email protected]
+def run_test_server(*, host='127.0.0.1', port=0, use_ssl=False):
+    yield from _run_test_server(address=(host, port), use_ssl=use_ssl,
+                                server_cls=SilentWSGIServer,
+                                server_ssl_cls=SSLWSGIServer)
+
+
+def make_test_protocol(base):
+    dct = {}
+    for name in dir(base):
+        if name.startswith('__') and name.endswith('__'):
+            # skip magic names
+            continue
+        dct[name] = MockCallback(return_value=None)
+    return type('TestProtocol', (base,) + base.__bases__, dct)()
+
+
+class TestSelector(selectors.BaseSelector):
+
+    def __init__(self):
+        self.keys = {}
+
+    def register(self, fileobj, events, data=None):
+        key = selectors.SelectorKey(fileobj, 0, events, data)
+        self.keys[fileobj] = key
+        return key
+
+    def unregister(self, fileobj):
+        return self.keys.pop(fileobj)
+
+    def select(self, timeout):
+        return []
+
+    def get_map(self):
+        return self.keys
+
+
+class TestLoop(base_events.BaseEventLoop):
+    """Loop for unittests.
+
+    It manages self time directly.
+    If something scheduled to be executed later then
+    on next loop iteration after all ready handlers done
+    generator passed to __init__ is calling.
+
+    Generator should be like this:
+
+        def gen():
+            ...
+            when = yield ...
+            ... = yield time_advance
+
+    Value returned by yield is absolute time of next scheduled handler.
+    Value passed to yield is time advance to move loop's time forward.
+    """
+
+    def __init__(self, gen=None):
+        super().__init__()
+
+        if gen is None:
+            def gen():
+                yield
+            self._check_on_close = False
+        else:
+            self._check_on_close = True
+
+        self._gen = gen()
+        next(self._gen)
+        self._time = 0
+        self._clock_resolution = 1e-9
+        self._timers = []
+        self._selector = TestSelector()
+
+        self.readers = {}
+        self.writers = {}
+        self.reset_counters()
+
+        self._transports = weakref.WeakValueDictionary()
+
+    def time(self):
+        return self._time
+
+    def advance_time(self, advance):
+        """Move test time forward."""
+        if advance:
+            self._time += advance
+
+    def close(self):
+        super().close()
+        if self._check_on_close:
+            try:
+                self._gen.send(0)
+            except StopIteration:
+                pass
+            else:  # pragma: no cover
+                raise AssertionError("Time generator is not finished")
+
+    def _add_reader(self, fd, callback, *args):
+        self.readers[fd] = events.Handle(callback, args, self, None)
+
+    def _remove_reader(self, fd):
+        self.remove_reader_count[fd] += 1
+        if fd in self.readers:
+            del self.readers[fd]
+            return True
+        else:
+            return False
+
+    def assert_reader(self, fd, callback, *args):
+        if fd not in self.readers:
+            raise AssertionError(f'fd {fd} is not registered')
+        handle = self.readers[fd]
+        if handle._callback != callback:
+            raise AssertionError(
+                f'unexpected callback: {handle._callback} != {callback}')
+        if handle._args != args:
+            raise AssertionError(
+                f'unexpected callback args: {handle._args} != {args}')
+
+    def assert_no_reader(self, fd):
+        if fd in self.readers:
+            raise AssertionError(f'fd {fd} is registered')
+
+    def _add_writer(self, fd, callback, *args):
+        self.writers[fd] = events.Handle(callback, args, self, None)
+
+    def _remove_writer(self, fd):
+        self.remove_writer_count[fd] += 1
+        if fd in self.writers:
+            del self.writers[fd]
+            return True
+        else:
+            return False
+
+    def assert_writer(self, fd, callback, *args):
+        assert fd in self.writers, 'fd {} is not registered'.format(fd)
+        handle = self.writers[fd]
+        assert handle._callback == callback, '{!r} != {!r}'.format(
+            handle._callback, callback)
+        assert handle._args == args, '{!r} != {!r}'.format(
+            handle._args, args)
+
+    def _ensure_fd_no_transport(self, fd):
+        if not isinstance(fd, int):
+            try:
+                fd = int(fd.fileno())
+            except (AttributeError, TypeError, ValueError):
+                # This code matches selectors._fileobj_to_fd function.
+                raise ValueError("Invalid file object: "
+                                 "{!r}".format(fd)) from None
+        try:
+            transport = self._transports[fd]
+        except KeyError:
+            pass
+        else:
+            raise RuntimeError(
+                'File descriptor {!r} is used by transport {!r}'.format(
+                    fd, transport))
+
+    def add_reader(self, fd, callback, *args):
+        """Add a reader callback."""
+        self._ensure_fd_no_transport(fd)
+        return self._add_reader(fd, callback, *args)
+
+    def remove_reader(self, fd):
+        """Remove a reader callback."""
+        self._ensure_fd_no_transport(fd)
+        return self._remove_reader(fd)
+
+    def add_writer(self, fd, callback, *args):
+        """Add a writer callback.."""
+        self._ensure_fd_no_transport(fd)
+        return self._add_writer(fd, callback, *args)
+
+    def remove_writer(self, fd):
+        """Remove a writer callback."""
+        self._ensure_fd_no_transport(fd)
+        return self._remove_writer(fd)
+
+    def reset_counters(self):
+        self.remove_reader_count = collections.defaultdict(int)
+        self.remove_writer_count = collections.defaultdict(int)
+
+    def _run_once(self):
+        super()._run_once()
+        for when in self._timers:
+            advance = self._gen.send(when)
+            self.advance_time(advance)
+        self._timers = []
+
+    def call_at(self, when, callback, *args, context=None):
+        self._timers.append(when)
+        return super().call_at(when, callback, *args, context=context)
+
+    def _process_events(self, event_list):
+        return
+
+    def _write_to_self(self):
+        pass
+
+
+def MockCallback(**kwargs):
+    return mock.Mock(spec=['__call__'], **kwargs)
+
+
+class MockPattern(str):
+    """A regex based str with a fuzzy __eq__.
+
+    Use this helper with 'mock.assert_called_with', or anywhere
+    where a regex comparison between strings is needed.
+
+    For instance:
+       mock_call.assert_called_with(MockPattern('spam.*ham'))
+    """
+    def __eq__(self, other):
+        return bool(re.search(str(self), other, re.S))
+
+
+class MockInstanceOf:
+    def __init__(self, type):
+        self._type = type
+
+    def __eq__(self, other):
+        return isinstance(other, self._type)
+
+
+def get_function_source(func):
+    source = format_helpers._get_function_source(func)
+    if source is None:
+        raise ValueError("unable to get the source of %r" % (func,))
+    return source
+
+
+class TestCase(unittest.TestCase):
+    @staticmethod
+    def close_loop(loop):
+        executor = loop._default_executor
+        if executor is not None:
+            executor.shutdown(wait=True)
+        loop.close()
+
+    def set_event_loop(self, loop, *, cleanup=True):
+        assert loop is not None
+        # ensure that the event loop is passed explicitly in asyncio
+        events.set_event_loop(None)
+        if cleanup:
+            self.addCleanup(self.close_loop, loop)
+
+    def new_test_loop(self, gen=None):
+        loop = TestLoop(gen)
+        self.set_event_loop(loop)
+        return loop
+
+    def unpatch_get_running_loop(self):
+        events._get_running_loop = self._get_running_loop
+
+    def setUp(self):
+        self._get_running_loop = events._get_running_loop
+        events._get_running_loop = lambda: None
+        self._thread_cleanup = support.threading_setup()
+
+    def tearDown(self):
+        self.unpatch_get_running_loop()
+
+        events.set_event_loop(None)
+
+        # Detect CPython bug #23353: ensure that yield/yield-from is not used
+        # in an except block of a generator
+        self.assertEqual(sys.exc_info(), (None, None, None))
+
+        self.doCleanups()
+        support.threading_cleanup(*self._thread_cleanup)
+        support.reap_children()
+
+
[email protected]
+def disable_logger():
+    """Context manager to disable asyncio logger.
+
+    For example, it can be used to ignore warnings in debug mode.
+    """
+    old_level = logger.level
+    try:
+        logger.setLevel(logging.CRITICAL+1)
+        yield
+    finally:
+        logger.setLevel(old_level)
+
+
+def mock_nonblocking_socket(proto=socket.IPPROTO_TCP, type=socket.SOCK_STREAM,
+                            family=socket.AF_INET):
+    """Create a mock of a non-blocking socket."""
+    sock = mock.MagicMock(socket.socket)
+    sock.proto = proto
+    sock.type = type
+    sock.family = family
+    sock.gettimeout.return_value = 0.0
+    return sock
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/test/conftest.py 
new/txaio-20.1.1/test/conftest.py
--- old/txaio-2.9.0/test/conftest.py    2016-11-06 20:03:51.000000000 +0100
+++ new/txaio-20.1.1/test/conftest.py   2020-01-21 00:10:08.000000000 +0100
@@ -25,9 +25,9 @@
 
     try:
         if request.param == 'twisted':
-            return framework_tx()
+            return _notfixture_framework_tx()
         elif request.param == 'asyncio':
-            return framework_aio()
+            return _notfixture_framework_aio()
     except ImportError:
         pytest.skip()
 
@@ -43,6 +43,10 @@
 
 @pytest.fixture
 def framework_tx():
+    return _notfixture_framework_tx()
+
+
+def _notfixture_framework_tx():
     try:
         import txaio
         from txaio import tx
@@ -56,6 +60,10 @@
 
 @pytest.fixture
 def framework_aio():
+    return _notfixture_framework_aio()
+
+
+def _notfixture_framework_aio():
     try:
         import txaio
         from txaio import aio
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/test/test_cancel.py 
new/txaio-20.1.1/test/test_cancel.py
--- old/txaio-2.9.0/test/test_cancel.py 1970-01-01 01:00:00.000000000 +0100
+++ new/txaio-20.1.1/test/test_cancel.py        2020-01-21 00:10:08.000000000 
+0100
@@ -0,0 +1,52 @@
+###############################################################################
+#
+# The MIT License (MIT)
+#
+# Copyright (c) Crossbar.io Technologies GmbH
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+###############################################################################
+
+import txaio
+
+from util import run_once
+
+
+def test_cancel(framework):
+    cancels = []
+
+    def it_died(f):
+        cancels.append(f)
+
+    f = txaio.create_future(canceller=it_died)
+    # both Future and Deferred have .cancel() methods .. but seemed
+    # more "symmetric"/expected to make a method? But could just stick
+    # with "f.cancel()" here ...
+    txaio.cancel(f)
+
+    # at least for Twisted, we have to "handle" the "CancelledError"
+    # -- in practice, dropping a future on the floor with no
+    # error-handler is A Bad Thing anyway
+    txaio.add_callbacks(f, None, lambda _: None)
+
+    run_once()
+    run_once()
+
+    assert cancels == [f]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/test/test_gather.py 
new/txaio-20.1.1/test/test_gather.py
--- old/txaio-2.9.0/test/test_gather.py 2018-03-02 08:04:45.000000000 +0100
+++ new/txaio-20.1.1/test/test_gather.py        2020-01-21 00:10:08.000000000 
+0100
@@ -26,7 +26,7 @@
 
 import txaio
 
-from util import await
+from util import _await
 
 
 def test_gather_two(framework):
@@ -61,7 +61,7 @@
     txaio.add_callbacks(f2, done, error)
 
     for f in [f0, f1, f2]:
-        await(f)
+        _await(f)
 
     assert len(results) == 1
     assert len(errors) == 0
@@ -99,7 +99,7 @@
     # out of "run_until_complete()" as well; fix util.py?
     for f in [f0, f1, f2]:
         try:
-            await(f)
+            _await(f)
         except Exception:
             pass
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/test/test_util.py 
new/txaio-20.1.1/test/test_util.py
--- old/txaio-2.9.0/test/test_util.py   1970-01-01 01:00:00.000000000 +0100
+++ new/txaio-20.1.1/test/test_util.py  2020-01-21 10:06:33.000000000 +0100
@@ -0,0 +1,37 @@
+###############################################################################
+#
+# The MIT License (MIT)
+#
+# Copyright (c) Crossbar.io Technologies GmbH
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+###############################################################################
+
+import txaio
+
+
+def test_time_ns(framework):
+    now = txaio.time_ns()
+    assert now > 0
+
+
+def test_perf_counter_ns(framework):
+    now = txaio.perf_counter_ns()
+    assert now > 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/test/util.py 
new/txaio-20.1.1/test/util.py
--- old/txaio-2.9.0/test/util.py        2017-04-15 16:17:17.000000000 +0200
+++ new/txaio-20.1.1/test/util.py       2020-01-21 00:10:08.000000000 +0100
@@ -24,6 +24,8 @@
 #
 ###############################################################################
 
+import sys
+
 
 def run_once():
     '''
@@ -38,7 +40,11 @@
 
     try:
         import asyncio
-        from asyncio.test_utils import run_once as _run_once
+        if sys.version_info >= (3, 7):
+            # https://github.com/crossbario/txaio/issues/139
+            from _asyncio_test_utils import run_once as _run_once
+        else:
+            from asyncio.test_utils import run_once as _run_once
         return _run_once(txaio.config.loop or asyncio.get_event_loop())
 
     except ImportError:
@@ -57,7 +63,7 @@
         asyncio.gather(*asyncio.Task.all_tasks())
 
 
-def await(future):
+def _await(future):
     '''
     Essentially just a way to call "run_until_complete" that becomes a
     no-op if we're using Twisted.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/tox.ini new/txaio-20.1.1/tox.ini
--- old/txaio-2.9.0/tox.ini     2018-03-02 08:04:45.000000000 +0100
+++ new/txaio-20.1.1/tox.ini    2020-01-21 10:06:33.000000000 +0100
@@ -1,35 +1,27 @@
 [tox]
 envlist =
     flake8
-    py27-{tw121,tw132,tw154,tw165,twtrunk,asyncio}
-    pypy-{tw121,tw132,tw154,tw165,twtrunk,asyncio}
-    pypy3-{tw154,tw165,twtrunk,asyncio}
-    py33-{tw154,tw165,asyncio}
-    py34-{tw154,tw165,twtrunk,asyncio}
-    py35-{tw154,tw165,twtrunk,asyncio}
-    py36-{tw154,tw165,twtrunk,asyncio}
+
+    # CPython
+    py35-{tw187,tw1910,twtrunk,asyncio}
+    py37-{tw165,tw187,tw1910,twtrunk,asyncio}
+    py38-{tw165,tw187,tw1910,twtrunk,asyncio}
+
+    # PyPy
+    pypy3-{tw187,tw1910,twtrunk,asyncio}
 
 [testenv]
 deps =
     six
     mock
-    pytest
-    coverage==4.0
+    pytest==4.6.9
+    coverage==4.5.4
 
     ; twisted dependencies
-    tw121: twisted==12.1.0
-    tw132: twisted==13.2.0
-    tw154: twisted==15.4.0
-    tw165: twisted==16.5.0
+    tw187: twisted==18.7.0
+    tw1910: twisted==19.10.0
     twtrunk: https://github.com/twisted/twisted/archive/trunk.zip
-    {tw121,tw132,tw154,tw165,twtrunk}: pytest-twisted
-
-    ; asyncio dependencies
-    py26-asyncio: trollius>=2.0
-    py26-asyncio: ordereddict
-    py27-asyncio: trollius>=2.0
-    pypy-asyncio: trollius>=2.0
-    py33-asyncio: asyncio>=3.4.3
+    {tw187,tw1910,twtrunk}: pytest-twisted==1.10
 
 changedir=test
 
@@ -42,7 +34,6 @@
 # -s: show output immediately
 # -v: one line per test, instead of one dot
 
-
 [testenv:flake8]
 deps =
     flake8
@@ -50,4 +41,4 @@
 changedir=.
 
 commands =
-    flake8 --max-line-length=119 txaio/ test/
+    flake8 --max-line-length=119 txaio/ test/ 
--exclude=test/_asyncio_test_utils.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/txaio/__init__.py 
new/txaio-20.1.1/txaio/__init__.py
--- old/txaio-2.9.0/txaio/__init__.py   2017-04-15 16:17:17.000000000 +0200
+++ new/txaio-20.1.1/txaio/__init__.py  2020-01-21 10:06:33.000000000 +0100
@@ -74,6 +74,7 @@
     'is_future',                # True for Deferreds in tx and Futures, 
@coroutines in asyncio
     'reject',                   # errback a Future
     'resolve',                  # callback a Future
+    'cancel',                   # cancel a Future
     'add_callbacks',            # add callback and/or errback
     'gather',                   # return a Future waiting for several other 
Futures
     'is_called',                # True if the Future has a result
@@ -96,6 +97,8 @@
     'ILogger',                  # API for logging
 
     'sleep',                    # little helper for inline sleeping
+    'time_ns',                  # helper: current time (UTC) in ns
+    'perf_counter_ns',          # helper: current performance counter in ns
 )
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/txaio/_unframework.py 
new/txaio-20.1.1/txaio/_unframework.py
--- old/txaio-2.9.0/txaio/_unframework.py       2017-04-15 16:17:17.000000000 
+0200
+++ new/txaio-20.1.1/txaio/_unframework.py      2020-01-21 10:06:33.000000000 
+0100
@@ -54,6 +54,7 @@
 as_future = _throw_usage_error
 is_future = _throw_usage_error
 reject = _throw_usage_error
+cancel = _throw_usage_error
 resolve = _throw_usage_error
 add_callbacks = _throw_usage_error
 gather = _throw_usage_error
@@ -78,3 +79,5 @@
 ILogger = _throw_usage_error
 
 sleep = _throw_usage_error
+time_ns = _throw_usage_error
+perf_counter_ns = _throw_usage_error
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/txaio/_util.py 
new/txaio-20.1.1/txaio/_util.py
--- old/txaio-2.9.0/txaio/_util.py      1970-01-01 01:00:00.000000000 +0100
+++ new/txaio-20.1.1/txaio/_util.py     2020-01-21 10:06:33.000000000 +0100
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# The MIT License (MIT)
+#
+# Copyright (c) Crossbar.io Technologies GmbH
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+###############################################################################
+
+import sys
+import time
+
+
+if sys.version_info >= (3, 7):
+
+    time_ns = time.time_ns
+    perf_counter_ns = time.perf_counter_ns
+
+else:
+
+    def time_ns():
+        """
+        Shim for standard library time.time_ns for Python < 3.7.
+        """
+        return int(time.time() * 1000000000.)
+
+    def perf_counter_ns():
+        """
+        Shim for standard library time.perf_counter for Python < 3.7.
+        """
+        return int(time.perf_counter() * 1000000000.)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/txaio/_version.py 
new/txaio-20.1.1/txaio/_version.py
--- old/txaio-2.9.0/txaio/_version.py   2018-03-02 08:06:14.000000000 +0100
+++ new/txaio-20.1.1/txaio/_version.py  2020-01-21 10:06:33.000000000 +0100
@@ -1 +1 @@
-__version__ = u'2.9.0'
+__version__ = u'20.1.1'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/txaio/aio.py 
new/txaio-20.1.1/txaio/aio.py
--- old/txaio-2.9.0/txaio/aio.py        2018-03-02 08:04:45.000000000 +0100
+++ new/txaio-20.1.1/txaio/aio.py       2020-01-21 10:06:33.000000000 +0100
@@ -33,12 +33,14 @@
 import functools
 import traceback
 import logging
+import inspect
 
 from datetime import datetime
 
 from txaio.interfaces import IFailedFuture, ILogger, log_levels
 from txaio._iotype import guess_stream_needs_encoding
 from txaio._common import _BatchedTimer
+from txaio import _util
 from txaio import _Config
 
 import six
@@ -258,7 +260,21 @@
 
 
 def make_logger():
-    logger = _TxaioLogWrapper(logging.getLogger())
+    # we want the namespace to be the calling context of "make_logger"
+    # otherwise the root logger will be returned
+    cf = inspect.currentframe().f_back
+    if "self" in cf.f_locals:
+        # We're probably in a class init or method
+        cls = cf.f_locals["self"].__class__
+        namespace = '{0}.{1}'.format(cls.__module__, cls.__name__)
+    else:
+        namespace = cf.f_globals["__name__"]
+        if cf.f_code.co_name != "<module>":
+            # If it's not the module, and not in a class instance, add the code
+            # object's name.
+            namespace = namespace + "." + cf.f_code.co_name
+
+    logger = _TxaioLogWrapper(logging.getLogger(name=namespace))
     # remember this so we can set their levels properly once
     # start_logging is actually called
     _loggers.add(logger)
@@ -374,7 +390,7 @@
         except Exception:
             return u"Failed to format failure traceback for '{0}'".format(fail)
 
-    def create_future(self, result=_unspecified, error=_unspecified):
+    def create_future(self, result=_unspecified, error=_unspecified, 
canceller=_unspecified):
         if result is not _unspecified and error is not _unspecified:
             raise ValueError("Cannot have both result and error.")
 
@@ -383,6 +399,19 @@
             resolve(f, result)
         elif error is not _unspecified:
             reject(f, error)
+
+        # Twisted's only API for cancelling is to pass a
+        # single-argument callable to the Deferred constructor, so
+        # txaio apes that here for asyncio. The argument is the Future
+        # that has been cancelled.
+        if canceller is not _unspecified:
+            def done(f):
+                try:
+                    f.exception()
+                except asyncio.CancelledError:
+                    canceller(f)
+            f.add_done_callback(done)
+
         return f
 
     def create_future_success(self, result):
@@ -461,6 +490,9 @@
                 raise RuntimeError("reject requires an IFailedFuture or 
Exception")
         future.set_exception(error.value)
 
+    def cancel(self, future):
+        future.cancel()
+
     def create_failure(self, exception=None):
         """
         This returns an object implementing IFailedFuture.
@@ -535,7 +567,10 @@
 is_called = _default_api.is_called
 resolve = _default_api.resolve
 reject = _default_api.reject
+cancel = _default_api.cancel
 create_failure = _default_api.create_failure
 add_callbacks = _default_api.add_callbacks
 gather = _default_api.gather
 sleep = _default_api.sleep
+time_ns = _util.time_ns
+perf_counter_ns = _util.perf_counter_ns
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/txaio/tx.py new/txaio-20.1.1/txaio/tx.py
--- old/txaio-2.9.0/txaio/tx.py 2017-04-15 16:42:18.000000000 +0200
+++ new/txaio-20.1.1/txaio/tx.py        2020-01-21 10:06:33.000000000 +0100
@@ -44,9 +44,19 @@
 from txaio._iotype import guess_stream_needs_encoding
 from txaio import _Config
 from txaio._common import _BatchedTimer
+from txaio import _util
 
 import six
 
+PY3_CORO = False
+if six.PY3:
+    try:
+        from twisted.internet.defer import ensureDeferred
+        from asyncio import iscoroutinefunction
+        PY3_CORO = True
+    except ImportError:
+        pass
+
 using_twisted = True
 using_asyncio = False
 
@@ -396,11 +406,11 @@
         except Exception:
             return u"Failed to format failure traceback for '{0}'".format(fail)
 
-    def create_future(self, result=_unspecified, error=_unspecified):
+    def create_future(self, result=_unspecified, error=_unspecified, 
canceller=None):
         if result is not _unspecified and error is not _unspecified:
             raise ValueError("Cannot have both result and error.")
 
-        f = Deferred()
+        f = Deferred(canceller=canceller)
         if result is not _unspecified:
             resolve(f, result)
         elif error is not _unspecified:
@@ -414,6 +424,9 @@
         return fail(create_failure(error))
 
     def as_future(self, fun, *args, **kwargs):
+        # Twisted doesn't automagically deal with coroutines on Py3
+        if PY3_CORO and iscoroutinefunction(fun):
+            return ensureDeferred(fun(*args, **kwargs))
         return maybeDeferred(fun, *args, **kwargs)
 
     def is_future(self, obj):
@@ -465,6 +478,9 @@
                 raise RuntimeError("reject requires a Failure or Exception")
         future.errback(error)
 
+    def cancel(self, future):
+        future.cancel()
+
     def create_failure(self, exception=None):
         """
         Create a Failure instance.
@@ -567,7 +583,10 @@
 is_called = _default_api.is_called
 resolve = _default_api.resolve
 reject = _default_api.reject
+cancel = _default_api.cancel
 create_failure = _default_api.create_failure
 add_callbacks = _default_api.add_callbacks
 gather = _default_api.gather
 sleep = _default_api.sleep
+time_ns = _util.time_ns
+perf_counter_ns = _util.perf_counter_ns
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/txaio.egg-info/PKG-INFO 
new/txaio-20.1.1/txaio.egg-info/PKG-INFO
--- old/txaio-2.9.0/txaio.egg-info/PKG-INFO     2018-03-02 08:47:02.000000000 
+0100
+++ new/txaio-20.1.1/txaio.egg-info/PKG-INFO    2020-01-21 10:09:51.000000000 
+0100
@@ -1,12 +1,11 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: txaio
-Version: 2.9.0
+Version: 20.1.1
 Summary: Compatibility API between asyncio/Twisted/Trollius
 Home-page: https://github.com/crossbario/txaio
 Author: Crossbar.io Technologies GmbH
 Author-email: [email protected]
 License: MIT License
-Description-Content-Type: UNKNOWN
 Description: txaio
         =====
         
@@ -31,20 +30,12 @@
         Platform support
         ----------------
         
-        **txaio** runs on CPython 2.7/3.3+ and PyPy 2/3, on top of Twisted or 
asyncio. Specifically, **txaio** is tested on the following platforms:
+        **txaio** runs on CPython 3.5+ and PyPy 3, on top of *Twisted* or 
*asyncio*. Specifically, **txaio** is tested on the following platforms:
         
-        **Python 2:**
+        * CPython 3.5, 3.7 and 3.8 on Twisted 18.7, 19.10, trunk and on 
asyncio (stdlib)
+        * PyPy 3 on Twisted 18.7, 19.10, trunk and on asyncio (stdlib)
         
-        * CPython 2.7 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 
2.0
-        * PyPy 2 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
-        
-        **Python 3:**
-        
-        * CPython 3.3 on Twisted 15.4, 16.5, trunk and on Trollius 2.0
-        * CPython 3.4 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-        * CPython 3.5 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-        * CPython 3.6 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
-        * PyPy 3 on Twisted 15.4, 16.5, trunk and on asyncio (stdlib)
+        > Note: txaio up to version 18.8.1 also supported Python 2.7 and 
Python 3.4. Beginning with release v20.1.1, txaio only supports Python 3.5+.
         
         
         How it works
@@ -99,14 +90,17 @@
 Classifier: Intended Audience :: Developers
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-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: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Software Development :: Libraries
 Classifier: Topic :: Software Development :: Libraries :: Application 
Frameworks
+Requires-Python: >=3.5
+Provides-Extra: twisted
+Provides-Extra: all
+Provides-Extra: dev
+Provides-Extra: asyncio
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/txaio.egg-info/SOURCES.txt 
new/txaio-20.1.1/txaio.egg-info/SOURCES.txt
--- old/txaio-2.9.0/txaio.egg-info/SOURCES.txt  2018-03-02 08:47:02.000000000 
+0100
+++ new/txaio-20.1.1/txaio.egg-info/SOURCES.txt 2020-01-21 10:09:51.000000000 
+0100
@@ -17,12 +17,14 @@
 examples/log_interop_stdlib.py
 examples/log_interop_twisted.py
 examples/multiloop.py
+test/_asyncio_test_utils.py
 test/conftest.py
 test/test_as_future.py
 test/test_batched_timers_aio.py
 test/test_batched_timers_tx.py
 test/test_call_later.py
 test/test_callback.py
+test/test_cancel.py
 test/test_create.py
 test/test_errback.py
 test/test_gather.py
@@ -31,11 +33,13 @@
 test/test_legacy_logging.py
 test/test_logging.py
 test/test_packaging.py
+test/test_util.py
 test/util.py
 txaio/__init__.py
 txaio/_common.py
 txaio/_iotype.py
 txaio/_unframework.py
+txaio/_util.py
 txaio/_version.py
 txaio/aio.py
 txaio/interfaces.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/txaio-2.9.0/txaio.egg-info/requires.txt 
new/txaio-20.1.1/txaio.egg-info/requires.txt
--- old/txaio-2.9.0/txaio.egg-info/requires.txt 2018-03-02 08:47:02.000000000 
+0100
+++ new/txaio-20.1.1/txaio.egg-info/requires.txt        2020-01-21 
10:09:51.000000000 +0100
@@ -7,6 +7,7 @@
 [asyncio]
 
 [dev]
+wheel
 pytest>=2.6.4
 pytest-cov>=1.8.1
 pep8>=1.6.2


Reply via email to