[PKG-Openstack-devel] Bug#867037: python-txaio FTBFS: test_chained_callback[asyncio] failed

Gianfranco Costamagna locutusofborg at debian.org
Wed Jul 19 06:35:59 UTC 2017


control: tags -1 patch pending
> Some recent change in unstable makes python-txaio FTBFS:
> 

NMU and diffs from Ubuntu attached.
This new release fixed the Ubuntu build issues and should be ready also for Python 3.6

G.

-------------- next part --------------
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/changelog python-txaio-2.8.0/debian/changelog
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/changelog	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/changelog	2017-07-19 08:32:33.000000000 +0200
@@ -1,3 +1,18 @@
+python-txaio (2.8.0-0.1) unstable; urgency=medium
+
+  [ Gianfranco Costamagna ]
+  * Non-maintainer upload.
+  * Drop useless, conflicting usr/LICENSE file in both binary packages.
+  * Update copyright year and author
+  * Update watch file.
+
+  [ Michael Hudson-Doyle ]
+  * New upstream release (Closes: #867037).
+  * d/control: drop build-dependency/dependency on python3-trollius.
+  * d/patches/remove-privacy-breach-in-docs.patch: regenerate.
+
+ -- Gianfranco Costamagna <locutusofborg at debian.org>  Wed, 19 Jul 2017 08:32:33 +0200
+
 python-txaio (2.5.1+2016.10.03.git.623ef68776-1) unstable; urgency=medium
 
   * New upstream release based on commit 623ef68776:
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/control python-txaio-2.8.0/debian/control
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/control	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/control	2017-07-19 08:32:33.000000000 +0200
@@ -24,7 +24,6 @@
                      python3-mock (>= 1.3),
                      python3-pytest,
                      python3-six,
-                     python3-trollius,
                      python3-twisted,
                      python3-zope.interface,
 Standards-Version: 3.9.8
@@ -55,7 +54,6 @@
 Package: python3-txaio
 Architecture: all
 Depends: python3-six,
-         python3-trollius,
          python3-twisted,
          python3-zope.interface,
          ${misc:Depends},
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/copyright python-txaio-2.8.0/debian/copyright
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/copyright	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/copyright	2017-07-19 08:28:26.000000000 +0200
@@ -3,7 +3,7 @@
 Source: https://github.com/tavendo/txaio
 
 Files: *
-Copyright: (c) 2015, Tavendo GmbH <autobahnws at googlegroups.com>
+Copyright: (c) 2015 Crossbar.io Technologies GmbH
 License: Expat
 
 Files: debian/*
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/patches/remove-privacy-breach-in-docs.patch python-txaio-2.8.0/debian/patches/remove-privacy-breach-in-docs.patch
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/patches/remove-privacy-breach-in-docs.patch	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/patches/remove-privacy-breach-in-docs.patch	2017-06-30 01:18:36.000000000 +0200
@@ -3,31 +3,22 @@
 Forwarded: no
 Last-Update: 2016-07-13
 
---- python-txaio-2.5.1.orig/README.rst
-+++ python-txaio-2.5.1/README.rst
-@@ -58,25 +58,3 @@ Code like the following can then run on
-     # ...
-     txaio.resolve(f0, "value")
+--- a/README.rst
++++ b/README.rst
+@@ -60,16 +60,3 @@
      txaio.reject(f1, RuntimeError("it failed"))
+ 
+ Please refer to the `documentation <https://txaio.readthedocs.io/en/latest/>`_ for description and usage of the library features.
 -
 -
 -.. |Version| image:: https://img.shields.io/pypi/v/txaio.svg
 -   :target: https://pypi.python.org/pypi/txaio
 -
--.. |Downloads| image:: https://img.shields.io/pypi/dm/txaio.svg
--   :target: https://pypi.python.org/pypi/txaio
--
--.. |GitHub Stars| image:: https://img.shields.io/github/stars/crossbario/txaio.svg?style=social&label=Star
--   :target: https://github.com/crossbario/txaio
--
--.. |Master Branch| image:: https://img.shields.io/badge/branch-master-orange.svg
--   :target: https://travis-ci.org/crossbario/txaio.svg?branch=master
--
 -.. |Build Status| image:: https://travis-ci.org/crossbario/txaio.svg?branch=master
 -   :target: https://travis-ci.org/crossbario/txaio
 -
--.. |Coverage| image:: https://img.shields.io/codecov/c/github/crossbario/txaio/master.svg
+-.. |Coverage| image:: https://codecov.io/github/crossbario/txaio/coverage.svg?branch=master
 -   :target: https://codecov.io/github/crossbario/txaio
 -
--.. |Docs| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat
--   :target: http://txaio.readthedocs.org/en/latest/
+-.. |Docs| image:: https://readthedocs.org/projects/txaio/badge/?version=latest
+-   :target: https://txaio.readthedocs.io/en/latest/
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/rules python-txaio-2.8.0/debian/rules
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/rules	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/rules	2017-07-14 11:18:18.000000000 +0200
@@ -8,6 +8,8 @@
 
 override_dh_auto_install:
 	pkgos-dh_auto_install
+	# they are useless and conflicting files
+	find debian -name LICENSE -delete
 
 override_dh_auto_test:
 ifeq (,$(findstring nocheck, $(DEB_BUILD_OPTIONS)))
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/watch python-txaio-2.8.0/debian/watch
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/watch	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/watch	2017-07-19 08:31:26.000000000 +0200
@@ -1,3 +1,3 @@
 version=3
-http://pypi.python.org/packages/source/t/txaio txaio-(.*).tar.gz
-
+opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
+https://pypi.debian.net/txaio/txaio-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/conf.py python-txaio-2.8.0/docs/conf.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/conf.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/docs/conf.py	2016-11-06 20:23:19.000000000 +0100
@@ -95,12 +95,12 @@
 
 # General information about the project.
 project = u'txaio'
-author = u'Tavendo'
+author = u'Crossbar.io Project'
 this_year = u'{0}'.format(time.strftime('%Y'))
 if this_year != u'2015':
-    copyright = u'2015-{0}, Tavendo GmbH'.format(this_year)
+    copyright = u'2015-{0}, Crossbar.io Technologies GmbH'.format(this_year)
 else:
-    copyright = u'2015, Tavendo GmbH'
+    copyright = u'2015, Crossbar.io Technologies GmbH'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -271,87 +271,6 @@
 # Output file base name for HTML help builder.
 htmlhelp_basename = 'txaiodoc'
 
-# -- Options for LaTeX output ---------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-
-# Latex figure (float) alignment
-#'figure_align': 'htbp',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-#  author, documentclass [howto, manual, or own class]).
-latex_documents = [
-  (master_doc, 'txaio.tex', 'txaio Documentation',
-   'tavendo', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output ---------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    (master_doc, 'txaio', 'txaio Documentation',
-     [author], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output -------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-#  dir menu entry, description, category)
-texinfo_documents = [
-  (master_doc, 'txaio', 'txaio Documentation',
-   author, 'txaio', 'One line description of project.',
-   'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
-
 # http://sphinx-doc.org/ext/intersphinx.html
 intersphinx_mapping = {
    'py2': ('http://docs.python.org/2', None),
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/.gitignore python-txaio-2.8.0/docs/.gitignore
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/.gitignore	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/docs/.gitignore	1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-_build/
-
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/index.rst python-txaio-2.8.0/docs/index.rst
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/index.rst	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/docs/index.rst	2017-02-09 15:50:38.000000000 +0100
@@ -1,7 +1,7 @@
 txaio
 =====
 
-| |Version| |Downloads| |Build Status| |Coverage| |Docs|
+| |Version| |Build Status| |Coverage| |Docs|
 
 --------------
 
@@ -24,11 +24,11 @@
 
 **txaio** runs on CPython 2.7/3.3+ and PyPy 2, on top of Twisted or asyncio. Specifically, **txaio** is tested on the following platforms:
 
-* CPython 2.7 on Twisted 12.1, 13.2, 15.4, trunk and Trollius 2.0
-* CPython 3.3 on Twisted 15.4, trunk and Trollius 2.0
-* CPython 3.4 on Twisted 15.4, trunk and asyncio (stdlib)
-* CPython 3.5 on Twisted 15.4, trunk and asyncio (stdlib)
-* PyPy 2.5 on Twisted 12.1, 13.2, 15.4, trunk and Trollius 2.0
+* CPython 2.7 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
+* 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)
+* PyPy 2 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
 
 
 How it works
@@ -59,24 +59,17 @@
     txaio.resolve(f0, "value")
     txaio.reject(f1, RuntimeError("it failed"))
 
+Please refer to the `documentation <https://txaio.readthedocs.io/en/latest/>`_ for description and usage of the library features.
 
-.. |Version| image:: https://img.shields.io/pypi/v/txaio.svg
-   :target: https://pypi.python.org/pypi/txaio
 
-.. |Downloads| image:: https://img.shields.io/pypi/dm/txaio.svg
+.. |Version| image:: https://img.shields.io/pypi/v/txaio.svg
    :target: https://pypi.python.org/pypi/txaio
 
-.. |GitHub Stars| image:: https://img.shields.io/github/stars/crossbario/txaio.svg?style=social&label=Star
-   :target: https://github.com/crossbario/txaio
-
-.. |Master Branch| image:: https://img.shields.io/badge/branch-master-orange.svg
-   :target: https://travis-ci.org/crossbario/txaio.svg?branch=master
-
 .. |Build Status| image:: https://travis-ci.org/crossbario/txaio.svg?branch=master
    :target: https://travis-ci.org/crossbario/txaio
 
-.. |Coverage| image:: https://img.shields.io/codecov/c/github/crossbario/txaio/master.svg
+.. |Coverage| image:: https://codecov.io/github/crossbario/txaio/coverage.svg?branch=master
    :target: https://codecov.io/github/crossbario/txaio
 
-.. |Docs| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat
-   :target: http://txaio.readthedocs.org/en/latest/
+.. |Docs| image:: https://readthedocs.org/projects/txaio/badge/?version=latest
+   :target: https://txaio.readthedocs.io/en/latest/
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/overview.rst python-txaio-2.8.0/docs/overview.rst
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/overview.rst	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/docs/overview.rst	2016-11-06 20:23:19.000000000 +0100
@@ -7,8 +7,7 @@
 This library has been factored out of the `Autobahn|Python`_ WAMP client
 library. The ``ApplicationSession`` object from that project therefore
 serves as a good example of how to use this library in a complex
-use-case. See
-https://github.com/tavendo/AutobahnPython/blob/master/autobahn/wamp/protocol.py#L410
+use-case.
 
 We are releasing it in the hopes these utilities are useful on their
 own to other projects using event-based Python. Only authors of
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/programming-guide.rst python-txaio-2.8.0/docs/programming-guide.rst
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/programming-guide.rst	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/docs/programming-guide.rst	2017-05-01 11:04:39.000000000 +0200
@@ -4,6 +4,41 @@
 This section is a work in progress and suggestions are welcome.
 
 
+Explict Event Loops
+-------------------
+
+Twisted has a single, global reactor (for now). As such, txaio was built with a single, global (but configurable) event-loop. However, asyncio supports multiple event-loops.
+
+After version 2.7.0 it is possible to use txaio with multiple event-loops, and thereby offer asyncio users the chance to pass one. Of course, it's still not possible to use multiple event-loops at once with Twisted.
+
+To start using multiple event-loops with txaio, use :func:`txaio.with_config` to return a new "instance" of the txaio API with the given config (the only thing you can configure currently is the event-loop). On Twisted, it's an error if you try to use a different reactor.
+
+The object returned by :func:`txaio.with_config` is a drop-in replacement for every `txaio.*` call, so you can go from code like this::
+
+    import txaio
+    f = txaio.create_future()
+
+...and instead make your code do look like this::
+
+    import asyncio
+    import txaio
+    txa = txaio.with_config(loop=asyncio.new_event_loop())
+    f = txa.create_future()
+
+If you're doing this inside a class, you could use ``self._txa`` or similar instead. This gives you an easy path to opt-in to this multiple event-loop API:
+
+   - replace all ``txaio.*`` calls to use an object, like ``self._txa``.
+
+   - assign this to the txaio module (``self._txa = txaio``) or use
+     the new API right away (``self._txa = txaio.with_config()``)
+
+   - add a public API to your library to pass in an event loop
+
+   - when this is used, you set ``self._txa = txaio.with_config(loop=loop)``
+
+See the example in ``examples/multiloop.py``.
+
+
 Logging
 -------
 
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/releases.rst python-txaio-2.8.0/docs/releases.rst
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/releases.rst	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/docs/releases.rst	2017-06-08 18:36:41.000000000 +0200
@@ -1,6 +1,53 @@
 txio releases
 =============
 
+2.8.0
+-----
+
+- June 8, 2017
+- fix: asyncio - remove the hacks for "simulating" chained futures (no longer works - cpy36 has native code for future)
+- new: run CI on Python 3.5 and 3.6
+
+
+2.7.1
+-----
+
+- May 1, 2017
+- asyncio: example and docs for running multiple loops
+- asyncio: log exception tracebacks when they're available for error-message
+
+
+2.7.0
+-----
+
+- April 15, 2017
+- allow alternate asyncio loops
+- new future creation API for alternate loops
+
+
+2.6.1
+-----
+
+- February 9, 2017
+- added inline sleep helper (Twisted only for now)
+
+
+2.6.0
+-----
+
+- December 29, 2016
+- avoid giving negative times to `callLater` with batched timers (issue #81)
+
+
+2.5.2
+-----
+
+- November 6, 2016
+- fix pytest3/2
+- fix Sphinx 1.4+ doc building
+- Copyrights transferred from Tavendo to Crossbar.io Technologies
+
+
 2.5.1
 -----
 
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/spelling_wordlist.txt python-txaio-2.8.0/docs/spelling_wordlist.txt
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/docs/spelling_wordlist.txt	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/docs/spelling_wordlist.txt	1970-01-01 01:00:00.000000000 +0100
@@ -1,18 +0,0 @@
-txaio
-asyncio
-trollius
-deferred
-deferreds
-errback
-errbacks
-callback
-callbacks
-api
-cpython
-CPython
-arg
-args
-kwarg
-kwargs
-callables
-stdlib
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/examples/basic.py python-txaio-2.8.0/examples/basic.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/examples/basic.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/examples/basic.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/examples/log_interop_stdlib.py python-txaio-2.8.0/examples/log_interop_stdlib.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/examples/log_interop_stdlib.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/examples/log_interop_stdlib.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/examples/log_interop_twisted.py python-txaio-2.8.0/examples/log_interop_twisted.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/examples/log_interop_twisted.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/examples/log_interop_twisted.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/examples/multiloop.py python-txaio-2.8.0/examples/multiloop.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/examples/multiloop.py	1970-01-01 01:00:00.000000000 +0100
+++ python-txaio-2.8.0/examples/multiloop.py	2017-05-01 11:04:39.000000000 +0200
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# 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.
+#
+###############################################################################
+
+from __future__ import print_function
+
+# This example only works with asyncio
+
+import asyncio
+import pytest
+import txaio
+txaio.use_asyncio()
+
+
+class Thing(object):
+
+    def __init__(self, loop=None):
+        self._txa = txaio.with_config(loop=loop)
+
+    def do_thing(self):
+        f = self._txa.create_future()
+
+        def done():
+            self._txa.resolve(f, "done")
+        self._txa.call_later(1, done)
+        return f
+
+
+loop = asyncio.new_event_loop()
+thing0 = Thing()
+thing1 = Thing(loop=loop)
+
+asyncio.get_event_loop().run_until_complete(thing0.do_thing())
+# this will be error, mismatched loops:
+#asyncio.get_event_loop().run_until_complete(thing1.do_thing())
+loop.run_until_complete(thing1.do_thing())
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/.gitignore python-txaio-2.8.0/.gitignore
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/.gitignore	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/.gitignore	1970-01-01 01:00:00.000000000 +0100
@@ -1,59 +0,0 @@
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-env/
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-*.egg-info/
-.installed.cfg
-*.egg
-
-# PyInstaller
-#  Usually these files are written by a python script from a template
-#  before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-
-# Sphinx documentation
-docs/_build/
-docs/_spelling/
-
-# PyBuilder
-target/
-
-*~
-_trial_temp/
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/LICENSE python-txaio-2.8.0/LICENSE
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/LICENSE	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/LICENSE	2016-11-06 20:23:19.000000000 +0100
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) Tavendo GmbH
+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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/Makefile python-txaio-2.8.0/Makefile
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/Makefile	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/Makefile	2017-02-25 11:15:24.000000000 +0100
@@ -47,6 +47,11 @@
 	find . -name "*.pyc" -type f -exec rm -f {} \;
 	find . -name "*__pycache__" -type d -exec rm -rf {} \;
 
+# upload to our internal deployment system
+upload: clean
+	python setup.py bdist_wheel
+	aws s3 cp dist/*.whl s3://fabric-deploy/
+
 # publish to PyPI
 publish: clean
 	python setup.py sdist bdist_wheel
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/PKG-INFO python-txaio-2.8.0/PKG-INFO
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/PKG-INFO	1970-01-01 01:00:00.000000000 +0100
+++ python-txaio-2.8.0/PKG-INFO	2017-06-08 18:38:00.000000000 +0200
@@ -0,0 +1,104 @@
+Metadata-Version: 1.1
+Name: txaio
+Version: 2.8.0
+Summary: Compatibility API between asyncio/Twisted/Trollius
+Home-page: https://github.com/crossbario/txaio
+Author: Crossbar.io Technologies GmbH
+Author-email: autobahnws at googlegroups.com
+License: MIT License
+Description: txaio
+        =====
+        
+        | |Version| |Build Status| |Coverage| |Docs|
+        
+        --------------
+        
+        **txaio** is a helper library for writing code that runs unmodified on
+        both `Twisted <https://twistedmatrix.com/>`_ and `asyncio <https://docs.python.org/3/library/asyncio.html>`_ / `Trollius <http://trollius.readthedocs.org/en/latest/index.html>`_.
+        
+        This is like `six <http://pythonhosted.org/six/>`_, but for wrapping
+        over differences between Twisted and asyncio so one can write code
+        that runs unmodified on both (aka *source code compatibility*). In
+        other words: your *users* can choose if they want asyncio **or** Twisted
+        as a dependency.
+        
+        Note that, with this approach, user code **runs under the native event
+        loop of either Twisted or asyncio**. This is different from attaching
+        either one's event loop to the other using some event loop adapter.
+        
+        
+        Platform support
+        ----------------
+        
+        **txaio** runs on CPython 2.7/3.3+ and PyPy 2, on top of Twisted or asyncio. Specifically, **txaio** is tested on the following platforms:
+        
+        * CPython 2.7 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
+        * 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)
+        * PyPy 2 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
+        
+        
+        How it works
+        ------------
+        
+        Instead of directly importing, instantiating and using ``Deferred``
+        (for Twisted) or ``Future`` (for asyncio) objects, **txaio** provides
+        helper-functions to do that for you, as well as associated things like
+        adding callbacks or errbacks.
+        
+        This obviously changes the style of your code, but then you can choose
+        at runtime (or import time) which underlying event-loop to use. This
+        means you can write **one** code-base that can run on Twisted *or*
+        asyncio (without a Twisted dependency) as you or your users see fit.
+        
+        Code like the following can then run on *either* system:
+        
+        .. sourcecode:: python
+        
+            import txaio
+            txaio.use_twisted()  # or .use_asyncio()
+        
+            f0 = txaio.create_future()
+            f1 = txaio.as_future(some_func, 1, 2, key='word')
+            txaio.add_callbacks(f0, callback, errback)
+            txaio.add_callbacks(f1, callback, errback)
+            # ...
+            txaio.resolve(f0, "value")
+            txaio.reject(f1, RuntimeError("it failed"))
+        
+        Please refer to the `documentation <https://txaio.readthedocs.io/en/latest/>`_ for description and usage of the library features.
+        
+        
+        .. |Version| image:: https://img.shields.io/pypi/v/txaio.svg
+           :target: https://pypi.python.org/pypi/txaio
+        
+        .. |Build Status| image:: https://travis-ci.org/crossbario/txaio.svg?branch=master
+           :target: https://travis-ci.org/crossbario/txaio
+        
+        .. |Coverage| image:: https://codecov.io/github/crossbario/txaio/coverage.svg?branch=master
+           :target: https://codecov.io/github/crossbario/txaio
+        
+        .. |Docs| image:: https://readthedocs.org/projects/txaio/badge/?version=latest
+           :target: https://txaio.readthedocs.io/en/latest/
+        
+Keywords: asyncio twisted trollius coroutine
+Platform: Any
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Framework :: Twisted
+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 :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/README.rst python-txaio-2.8.0/README.rst
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/README.rst	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/README.rst	2017-02-09 15:50:38.000000000 +0100
@@ -1,7 +1,7 @@
 txaio
 =====
 
-| |Version| |Downloads| |Build Status| |Coverage| |Docs|
+| |Version| |Build Status| |Coverage| |Docs|
 
 --------------
 
@@ -24,11 +24,11 @@
 
 **txaio** runs on CPython 2.7/3.3+ and PyPy 2, on top of Twisted or asyncio. Specifically, **txaio** is tested on the following platforms:
 
-* CPython 2.7 on Twisted 12.1, 13.2, 15.4, trunk and Trollius 2.0
-* CPython 3.3 on Twisted 15.4, trunk and Trollius 2.0
-* CPython 3.4 on Twisted 15.4, trunk and asyncio (stdlib)
-* CPython 3.5 on Twisted 15.4, trunk and asyncio (stdlib)
-* PyPy 2.5 on Twisted 12.1, 13.2, 15.4, trunk and Trollius 2.0
+* CPython 2.7 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
+* 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)
+* PyPy 2 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
 
 
 How it works
@@ -59,24 +59,17 @@
     txaio.resolve(f0, "value")
     txaio.reject(f1, RuntimeError("it failed"))
 
+Please refer to the `documentation <https://txaio.readthedocs.io/en/latest/>`_ for description and usage of the library features.
 
-.. |Version| image:: https://img.shields.io/pypi/v/txaio.svg
-   :target: https://pypi.python.org/pypi/txaio
 
-.. |Downloads| image:: https://img.shields.io/pypi/dm/txaio.svg
+.. |Version| image:: https://img.shields.io/pypi/v/txaio.svg
    :target: https://pypi.python.org/pypi/txaio
 
-.. |GitHub Stars| image:: https://img.shields.io/github/stars/crossbario/txaio.svg?style=social&label=Star
-   :target: https://github.com/crossbario/txaio
-
-.. |Master Branch| image:: https://img.shields.io/badge/branch-master-orange.svg
-   :target: https://travis-ci.org/crossbario/txaio.svg?branch=master
-
 .. |Build Status| image:: https://travis-ci.org/crossbario/txaio.svg?branch=master
    :target: https://travis-ci.org/crossbario/txaio
 
-.. |Coverage| image:: https://img.shields.io/codecov/c/github/crossbario/txaio/master.svg
+.. |Coverage| image:: https://codecov.io/github/crossbario/txaio/coverage.svg?branch=master
    :target: https://codecov.io/github/crossbario/txaio
 
-.. |Docs| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat
-   :target: http://txaio.readthedocs.org/en/latest/
+.. |Docs| image:: https://readthedocs.org/projects/txaio/badge/?version=latest
+   :target: https://txaio.readthedocs.io/en/latest/
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/setup.cfg python-txaio-2.8.0/setup.cfg
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/setup.cfg	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/setup.cfg	2017-06-08 18:38:00.000000000 +0200
@@ -1,2 +1,7 @@
 [bdist_wheel]
-universal=1
+universal = 1
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/setup.py python-txaio-2.8.0/setup.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/setup.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/setup.py	2017-03-29 10:22:01.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
@@ -91,7 +91,8 @@
     version=__version__,
     description='Compatibility API between asyncio/Twisted/Trollius',
     long_description=docstr,
-    author='Tavendo GmbH',
+    license='MIT License',
+    author='Crossbar.io Technologies GmbH',
     author_email='autobahnws at googlegroups.com',
     url='https://github.com/crossbario/txaio',
     platforms=('Any'),
@@ -105,7 +106,18 @@
         'all': extras_require_all
     },
     packages=['txaio'],
-    zip_safe=False,
+
+    # this flag will make files from MANIFEST.in go into _source_ distributions only
+    include_package_data=True,
+
+    # in addition, the following will make the specified files go
+    # into source _and_ bdist distributions!
+    data_files=[('.', ['LICENSE'])],
+
+    # this package does not access its own source code or data files
+    # as normal operating system files
+    zip_safe=True,
+
     # http://pypi.python.org/pypi?%3Aaction=list_classifiers
     classifiers=[
         "License :: OSI Approved :: MIT License",
@@ -121,6 +133,7 @@
         "Programming Language :: Python :: 3.3",
         "Programming Language :: Python :: 3.4",
         "Programming Language :: Python :: 3.5",
+        "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: Implementation :: CPython",
         "Programming Language :: Python :: Implementation :: PyPy",
         "Topic :: Software Development :: Libraries",
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_as_future.py python-txaio-2.8.0/test/test_as_future.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_as_future.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/test_as_future.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_batched_timers_aio.py python-txaio-2.8.0/test/test_batched_timers_aio.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_batched_timers_aio.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/test_batched_timers_aio.py	2017-04-15 16:17:17.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
@@ -77,6 +77,33 @@
         assert calls[2] == (("third call", ), dict())
 
 
+def test_batched_successful_call_explicit_loop(framework_aio):
+    '''
+    batched calls really happen in batches
+    '''
+    # Trollius doesn't come with this, so won't work on py2
+    pytest.importorskip('asyncio.test_utils')
+    from asyncio.test_utils import TestLoop
+
+    def time_gen():
+        yield
+        yield
+    new_loop = TestLoop(time_gen)
+    calls = []
+
+    def foo(*args, **kw):
+        calls.append((args, kw))
+
+    txa = txaio.with_config(loop=new_loop)
+
+    batched = txa.make_batched_timer(5)
+
+    batched.call_later(1, foo, "first call")
+    new_loop.advance_time(2.0)
+    new_loop._run_once()
+    assert len(calls) == 1
+
+
 def test_batched_cancel(framework_aio):
     '''
     we can cancel uncalled call_laters
@@ -88,7 +115,6 @@
     def time_gen():
         yield
         yield
-        yield
     new_loop = TestLoop(time_gen)
     calls = []
 
@@ -123,7 +149,6 @@
     def time_gen():
         yield
         yield
-        yield
     new_loop = TestLoop(time_gen)
     calls = []
 
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_batched_timers_tx.py python-txaio-2.8.0/test/test_batched_timers_tx.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_batched_timers_tx.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/test_batched_timers_tx.py	2017-04-15 16:42:18.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
@@ -124,7 +124,7 @@
     laters = []
 
     class FakeClock(Clock):
-        def callLater(self, *args, **kw):
+        def callLater(self, *args, **kw):  # noqa
             laters.append((args, kw))
             Clock.callLater(self, *args, **kw)
     new_loop = FakeClock()
@@ -166,7 +166,7 @@
     laters = []
 
     class FakeClock(Clock):
-        def callLater(self, *args, **kw):
+        def callLater(self, *args, **kw):  # noqa
             laters.append((args, kw))
             Clock.callLater(self, *args, **kw)
     new_loop = FakeClock()
@@ -192,3 +192,24 @@
             assert False, "Should get exception"
         except RuntimeError as e:
             assert "processing call_later" in str(e)
+
+
+def test_batched_close_to_now(framework_tx):
+    '''
+    if our current time is fractional, and we make a call_later with a
+    tiny delay that's still within the same second, we'll produce a
+    negative call_later when adding a bucket; see issue #81
+    '''
+    from twisted.internet.task import Clock
+
+    class FakeClock(Clock):
+        def callLater(self, delay, *args, **kw):  # noqa
+            # 'real' reactors do this, but Clock doesn't assert on
+            # this.
+            assert delay >= 0
+            return Clock.callLater(self, delay, *args, **kw)
+
+    with replace_loop(FakeClock()) as clock:
+        clock.advance(0.5)
+        batched = txaio.make_batched_timer(1, chunk_size=2)
+        batched.call_later(0.1, lambda: None)
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_callback.py python-txaio-2.8.0/test/test_callback.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_callback.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/test_callback.py	2017-06-08 17:45:54.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
@@ -59,31 +59,6 @@
     assert results[0] == "it worked"
 
 
-def test_chained_callback(framework):
-    """
-    Chain two callbacks where the first one alters the value.
-    """
-    calls = []
-
-    def callback0(arg):
-        calls.append(arg)
-        return arg + " pray I do not alter it futher"
-
-    def callback1(arg):
-        calls.append(arg)
-
-    f = txaio.create_future()
-    txaio.add_callbacks(f, callback0, None)
-    txaio.add_callbacks(f, callback1, None)
-    txaio.resolve(f, "the deal")
-
-    run_once()
-
-    assert len(calls) == 2
-    assert calls[0] == "the deal"
-    assert calls[1] == "the deal pray I do not alter it futher"
-
-
 def test_immediate_result(framework):
     f = txaio.create_future_success("it worked")
     results = []
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_call_later.py python-txaio-2.8.0/test/test_call_later.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_call_later.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/test_call_later.py	2017-04-15 16:17:17.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
@@ -29,6 +29,7 @@
 import pytest
 import txaio
 from txaio.testutil import replace_loop
+from util import run_once
 
 
 def test_default_reactor(framework_tx):
@@ -64,6 +65,97 @@
         assert c[0] == 'call_soon'
 
 
+def test_create_future_explicit_loop(framework):
+    """
+    process events on alternate loop= for create_future later
+    """
+    pytest.importorskip('asyncio')
+    if txaio.using_twisted:
+        pytest.skip()
+
+    import asyncio
+
+    alt_loop = asyncio.new_event_loop()
+
+    txa = txaio.with_config(loop=alt_loop)
+    f = txa.create_future()
+
+    results = []
+    f.add_done_callback(lambda r: results.append(r.result()))
+
+    assert results == []
+    txaio.resolve(f, 'some result')
+
+    # run_once() runs the txaio.config.loop so we shouldn't get any
+    # results until we spin alt_loop
+    assert results == []
+    run_once()
+    assert results == []
+    with replace_loop(alt_loop):
+        run_once()
+    assert results == ['some result']
+
+
+def test_create_future_success_explicit_loop(framework):
+    """
+    process events on alternate loop= for create_future later
+    """
+    pytest.importorskip('asyncio')
+    if txaio.using_twisted:
+        pytest.skip()
+
+    import asyncio
+    alt_loop = asyncio.new_event_loop()
+    txa = txaio.with_config(loop=alt_loop)
+
+    f = txa.create_future_success('some result')
+
+    results = []
+    f.add_done_callback(lambda r: results.append(r.result()))
+
+    # run_once() runs the txaio.config.loop so we shouldn't get any
+    # results until we spin alt_loop
+    assert results == []
+    run_once()
+    assert results == []
+    with replace_loop(alt_loop):
+        run_once()
+    assert results == ['some result']
+
+
+def test_create_future_failure_explicit_loop(framework):
+    """
+    process events on alternate loop= for create_future later
+    """
+    pytest.importorskip('asyncio')
+    if txaio.using_twisted:
+        pytest.skip()
+
+    import asyncio
+    alt_loop = asyncio.new_event_loop()
+    the_exception = Exception('bad')
+    txa = txaio.with_config(loop=alt_loop)
+    f = txa.create_future_error(the_exception)
+
+    results = []
+
+    def boom(r):
+        try:
+            results.append(r.result())
+        except Exception as e:
+            results.append(e)
+    f.add_done_callback(boom)
+
+    # run_once() runs the txaio.config.loop so we shouldn't get any
+    # results until we spin alt_loop
+    assert results == []
+    run_once()
+    assert results == []
+    with replace_loop(alt_loop):
+        run_once()
+    assert results == [the_exception]
+
+
 def test_explicit_reactor_coroutine(framework):
     """
     If we set an event-loop, Futures + Tasks should use it.
@@ -122,7 +214,6 @@
         # even though we only do one call, I guess TestLoop needs
         # a "trailing" yield? "or something"
         when = yield 0
-        print("Hmmm", when)
     from asyncio.test_utils import TestLoop
     new_loop = TestLoop(time_gen)
 
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_errback.py python-txaio-2.8.0/test/test_errback.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_errback.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/test_errback.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_gather.py python-txaio-2.8.0/test/test_gather.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_gather.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/test_gather.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_is_future.py python-txaio-2.8.0/test/test_is_future.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_is_future.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/test_is_future.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_legacy_logging.py python-txaio-2.8.0/test/test_legacy_logging.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_legacy_logging.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/test_legacy_logging.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_logging.py python-txaio-2.8.0/test/test_logging.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_logging.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/test_logging.py	2017-05-01 10:52:15.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
@@ -141,6 +141,17 @@
     assert handler.messages[0].endswith(b"hilarious elephant")
 
 
+def test_legacy_error_with_traceback(handler, framework):
+    import logging
+
+    try:
+        raise RuntimeError("the bad stuff")
+    except Exception:
+        logging.error("bad stuff", exc_info=True)
+
+    assert 'RuntimeError: the bad stuff' in str(handler.messages)
+
+
 def test_trace(handler, framework):
     logger = txaio.make_logger()
     old_log = txaio.get_global_log_level()
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_packaging.py python-txaio-2.8.0/test/test_packaging.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/test_packaging.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/test_packaging.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/test/util.py python-txaio-2.8.0/test/util.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/test/util.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/test/util.py	2017-04-15 16:17:17.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
@@ -39,7 +39,7 @@
     try:
         import asyncio
         from asyncio.test_utils import run_once as _run_once
-        return _run_once(asyncio.get_event_loop())
+        return _run_once(txaio.config.loop or asyncio.get_event_loop())
 
     except ImportError:
         import trollius as asyncio
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/tox.ini python-txaio-2.8.0/tox.ini
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/tox.ini	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/tox.ini	2017-02-09 15:50:38.000000000 +0100
@@ -1,11 +1,12 @@
 [tox]
 envlist =
     flake8
-    py27-{tw121,tw132,tw154,tw162,twtrunk,asyncio}
-    pypy-{tw121,tw132,tw154,tw162,twtrunk,asyncio}
-    py33-{tw154,tw162,twtrunk,asyncio}
-    py34-{tw154,tw162,twtrunk,asyncio}
-    py35-{tw154,tw162,twtrunk,asyncio}
+    py27-{tw121,tw132,tw154,tw165,twtrunk,asyncio}
+    pypy-{tw121,tw132,tw154,tw165,twtrunk,asyncio}
+    py33-{tw154,tw165,twtrunk,asyncio}
+    py34-{tw154,tw165,twtrunk,asyncio}
+    py35-{tw154,tw165,twtrunk,asyncio}
+    py36-{tw154,tw165,twtrunk,asyncio}
 
 [testenv]
 deps =
@@ -18,9 +19,9 @@
     tw121: twisted==12.1.0
     tw132: twisted==13.2.0
     tw154: twisted==15.4.0
-    tw162: twisted==16.2.0
+    tw165: twisted==16.5.0
     twtrunk: https://github.com/twisted/twisted/archive/trunk.zip
-    {tw121,tw132,tw154,tw162,twtrunk}: pytest-twisted
+    {tw121,tw132,tw154,tw165,twtrunk}: pytest-twisted
 
     ; asyncio dependencies
     py26-asyncio: trollius>=2.0
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/.travis.yml python-txaio-2.8.0/.travis.yml
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/.travis.yml	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/.travis.yml	1970-01-01 01:00:00.000000000 +0100
@@ -1,73 +0,0 @@
-language: python
-sudo: false
-
-install:
-  - pip install tox codecov
-
-env:
-  - TOX_ENV=flake8
-
-  - TOX_ENV=py27-tw121
-  - TOX_ENV=py27-tw132
-  - TOX_ENV=py27-tw154
-  - TOX_ENV=py27-tw162
-  - TOX_ENV=py27-twtrunk
-  - TOX_ENV=py27-asyncio
-
-  - TOX_ENV=pypy-tw121
-  - TOX_ENV=pypy-tw132
-  - TOX_ENV=pypy-tw154
-  - TOX_ENV=pypy-tw162
-  - TOX_ENV=pypy-twtrunk
-  - TOX_ENV=pypy-asyncio
-
-  - TOX_ENV=py33-tw154
-  - TOX_ENV=py33-tw162
-  - TOX_ENV=py33-twtrunk
-  - TOX_ENV=py33-asyncio
-
-  - TOX_ENV=py34-tw154
-  - TOX_ENV=py34-tw162
-  - TOX_ENV=py34-twtrunk
-  - TOX_ENV=py34-asyncio
-
-  #- TOX_ENV=py35-tw154
-  #- TOX_ENV=py35-tw162
-  #- TOX_ENV=py35-twtrunk
-  #- TOX_ENV=py35-asyncio
-
-script:
-  - tox -c tox.ini -e $TOX_ENV
-
-after_success:
-  # since we use --parallel-mode to coverage inside Tox we use
-  # "coverage combine" so the filename is always ".coverage"
-  - cd test && coverage combine && codecov
-
-matrix:
-  fast_finish: true
-
-  # https://github.com/travis-ci/travis-ci/issues/4794#issuecomment-143758799
-  include:
-    - python: 3.5
-      env:
-      - TOX_ENV=py35-tw154
-
-    - python: 3.5
-      env:
-      - TOX_ENV=py35-tw162
-
-    - python: 3.5
-      env:
-      - TOX_ENV=py35-twtrunk
-
-    - python: 3.5
-      env:
-      - TOX_ENV=py35-asyncio
-
-notifications:
-   irc:
-      channels:
-         - "irc.freenode.org#autobahn"
-      use_notice: true
-      skip_join: true
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/aio.py python-txaio-2.8.0/txaio/aio.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/aio.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/txaio/aio.py	2017-06-08 17:45:54.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
@@ -55,17 +55,60 @@
     from trollius import iscoroutine
     from trollius import Future
 
+
 config = _Config()
-config.loop = asyncio.get_event_loop()
+
+
+def with_config(loop=None):
+    """
+    :return: an instance of the txaio API with the given
+        configuration. This won't affect anything using the 'gloabl'
+        config nor other instances created using this function.
+
+    If you need to customize txaio configuration separately (e.g. to
+    use multiple event-loops in asyncio), you can take code like this:
+
+        import txaio
+
+
+        class FunTimes(object):
+
+            def something_async(self):
+                return txaio.call_later(1, lambda: 'some result')
+
+    and instead do this:
+
+        import txaio
+
+
+        class FunTimes(object):
+            txaio = txaio
+
+            def something_async(self):
+                # this will run in the local/new event loop created in the constructor
+                return self.txaio.call_later(1, lambda: 'some result')
+
+        fun0 = FunTimes()
+        fun1 = FunTimes()
+        fun1.txaio = txaio.with_config(loop=asyncio.new_event_loop())
+
+    So `fun1` will run its futures on the newly-created event loop,
+    while `fun0` will work just as it did before this `with_config`
+    method was introduced (after 2.6.2).
+    """
+    cfg = _Config()
+    if loop is not None:
+        cfg.loop = loop
+    return _AsyncioApi(cfg)
+
+
+# logging should probably all be folded into _AsyncioApi as well
 _stderr, _stdout = sys.stderr, sys.stdout
 _loggers = weakref.WeakSet()  # weak-ref's of each logger we've created before start_logging()
 _log_level = 'info'  # re-set by start_logging
 _started_logging = False
 _categories = {}
 
-using_twisted = False
-using_asyncio = True
-
 
 def add_log_categories(categories):
     _categories.update(categories)
@@ -101,7 +144,8 @@
         return str(self.value)
 
 
-# API methods for txaio, exported via the top-level __init__.py
+# logging API methods
+
 
 def _log(logger, level, format=u'', **kwargs):
 
@@ -165,6 +209,10 @@
             dt = datetime.fromtimestamp(record.args.get('log_time', 0))
         else:
             message = record.getMessage()
+            if record.levelno == logging.ERROR and record.exc_info:
+                message += '\n'
+                for line in traceback.format_exception(*record.exc_info):
+                    message = message + line
             dt = datetime.fromtimestamp(record.created)
         msg = u'{0} {1}{2}'.format(
             dt.strftime("%Y-%m-%dT%H:%M:%S%z"),
@@ -226,201 +274,228 @@
         logger._set_log_level(level)
 
 
-def failure_message(fail):
+def set_global_log_level(level):
     """
-    :param fail: must be an IFailedFuture
-    returns a unicode error-message
+    Set the global log level on all loggers instantiated by txaio.
     """
-    try:
-        return u'{0}: {1}'.format(
-            fail._value.__class__.__name__,
-            str(fail._value),
-        )
-    except Exception:
-        return u'Failed to produce failure message for "{0}"'.format(fail)
+    for logger in _loggers:
+        logger._set_log_level(level)
+    global _log_level
+    _log_level = level
 
 
-def failure_traceback(fail):
-    """
-    :param fail: must be an IFailedFuture
-    returns a traceback instance
-    """
-    return fail._traceback
+def get_global_log_level():
+    return _log_level
 
 
-def failure_format_traceback(fail):
-    """
-    :param fail: must be an IFailedFuture
-    returns a string
-    """
-    try:
-        f = six.StringIO()
-        traceback.print_exception(
-            fail._type,
-            fail.value,
-            fail._traceback,
-            file=f,
-        )
-        return f.getvalue()
-    except Exception:
-        return u"Failed to format failure traceback for '{0}'".format(fail)
+# asyncio API methods; the module-level functions are (now, for
+# backwards-compat) exported from a default instance of this class
 
 
 _unspecified = object()
 
 
-def create_future(result=_unspecified, error=_unspecified):
-    if result is not _unspecified and error is not _unspecified:
-        raise ValueError("Cannot have both result and error.")
-
-    f = Future(loop=config.loop)
-    if result is not _unspecified:
-        resolve(f, result)
-    elif error is not _unspecified:
-        reject(f, error)
-    return f
+class _AsyncioApi(object):
+    using_twisted = False
+    using_asyncio = True
+
+    def __init__(self, config):
+        if config.loop is None:
+            config.loop = asyncio.get_event_loop()
+        self._config = config
 
+    def failure_message(self, fail):
+        """
+        :param fail: must be an IFailedFuture
+        returns a unicode error-message
+        """
+        try:
+            return u'{0}: {1}'.format(
+                fail._value.__class__.__name__,
+                str(fail._value),
+            )
+        except Exception:
+            return u'Failed to produce failure message for "{0}"'.format(fail)
 
-def create_future_success(result):
-    return create_future(result=result)
-
-
-def create_future_error(error=None):
-    f = create_future()
-    reject(f, error)
-    return f
-
-
-def as_future(fun, *args, **kwargs):
-    try:
-        res = fun(*args, **kwargs)
-    except Exception:
-        return create_future_error(create_failure())
-    else:
-        if isinstance(res, Future):
-            return res
-        elif iscoroutine(res):
-            return asyncio.Task(res, loop=config.loop)
-        else:
-            return create_future_success(res)
-
-
-def is_future(obj):
-    return iscoroutine(obj) or isinstance(obj, Future)
-
+    def failure_traceback(self, fail):
+        """
+        :param fail: must be an IFailedFuture
+        returns a traceback instance
+        """
+        return fail._traceback
 
-def call_later(delay, fun, *args, **kwargs):
-    # loop.call_later doesn't support kwargs
-    real_call = functools.partial(fun, *args, **kwargs)
-    return config.loop.call_later(delay, real_call)
+    def failure_format_traceback(self, fail):
+        """
+        :param fail: must be an IFailedFuture
+        returns a string
+        """
+        try:
+            f = six.StringIO()
+            traceback.print_exception(
+                fail._type,
+                fail.value,
+                fail._traceback,
+                file=f,
+            )
+            return f.getvalue()
+        except Exception:
+            return u"Failed to format failure traceback for '{0}'".format(fail)
 
+    def create_future(self, result=_unspecified, error=_unspecified):
+        if result is not _unspecified and error is not _unspecified:
+            raise ValueError("Cannot have both result and error.")
+
+        f = Future(loop=self._config.loop)
+        if result is not _unspecified:
+            resolve(f, result)
+        elif error is not _unspecified:
+            reject(f, error)
+        return f
 
-def make_batched_timer(bucket_seconds, chunk_size=100):
-    """
-    Creates and returns an object implementing
-    :class:`txaio.IBatchedTimer`.
+    def create_future_success(self, result):
+        return self.create_future(result=result)
 
-    :param bucket_seconds: the number of seconds in each bucket. That
-        is, a value of 5 means that any timeout within a 5 second
-        window will be in the same bucket, and get notified at the
-        same time. This is only accurate to "milliseconds".
-
-    :param chunk_size: when "doing" the callbacks in a particular
-        bucket, this controls how many we do at once before yielding to
-        the reactor.
-    """
+    def create_future_error(self, error=None):
+        f = self.create_future()
+        reject(f, error)
+        return f
 
-    def get_seconds():
-        return config.loop.time()
+    def as_future(self, fun, *args, **kwargs):
+        try:
+            res = fun(*args, **kwargs)
+        except Exception:
+            return create_future_error(create_failure())
+        else:
+            if isinstance(res, Future):
+                return res
+            elif iscoroutine(res):
+                return asyncio.Task(res, loop=self._config.loop)
+            else:
+                return create_future_success(res)
 
-    return _BatchedTimer(
-        bucket_seconds * 1000.0, chunk_size,
-        seconds_provider=get_seconds,
-        delayed_call_creator=call_later,
-    )
+    def is_future(self, obj):
+        return iscoroutine(obj) or isinstance(obj, Future)
 
+    def call_later(self, delay, fun, *args, **kwargs):
+        # loop.call_later doesn't support kwargs
+        real_call = functools.partial(fun, *args, **kwargs)
+        return self._config.loop.call_later(delay, real_call)
 
-def is_called(future):
-    return future.done()
+    def make_batched_timer(self, bucket_seconds, chunk_size=100):
+        """
+        Creates and returns an object implementing
+        :class:`txaio.IBatchedTimer`.
 
+        :param bucket_seconds: the number of seconds in each bucket. That
+            is, a value of 5 means that any timeout within a 5 second
+            window will be in the same bucket, and get notified at the
+            same time. This is only accurate to "milliseconds".
+
+        :param chunk_size: when "doing" the callbacks in a particular
+            bucket, this controls how many we do at once before yielding to
+            the reactor.
+        """
 
-def resolve(future, result=None):
-    future.set_result(result)
+        def get_seconds():
+            return self._config.loop.time()
 
+        return _BatchedTimer(
+            bucket_seconds * 1000.0, chunk_size,
+            seconds_provider=get_seconds,
+            delayed_call_creator=self.call_later,
+        )
 
-def reject(future, error=None):
-    if error is None:
-        error = create_failure()  # will be error if we're not in an "except"
-    elif isinstance(error, Exception):
-        error = FailedFuture(type(error), error, None)
-    else:
-        if not isinstance(error, IFailedFuture):
-            raise RuntimeError("reject requires an IFailedFuture or Exception")
-    future.set_exception(error.value)
+    def is_called(self, future):
+        return future.done()
 
+    def resolve(self, future, result=None):
+        future.set_result(result)
 
-def create_failure(exception=None):
-    """
-    This returns an object implementing IFailedFuture.
+    def reject(self, future, error=None):
+        if error is None:
+            error = create_failure()  # will be error if we're not in an "except"
+        elif isinstance(error, Exception):
+            error = FailedFuture(type(error), error, None)
+        else:
+            if not isinstance(error, IFailedFuture):
+                raise RuntimeError("reject requires an IFailedFuture or Exception")
+        future.set_exception(error.value)
 
-    If exception is None (the default) we MUST be called within an
-    "except" block (such that sys.exc_info() returns useful
-    information).
-    """
-    if exception:
-        return FailedFuture(type(exception), exception, None)
-    return FailedFuture(*sys.exc_info())
+    def create_failure(self, exception=None):
+        """
+        This returns an object implementing IFailedFuture.
 
+        If exception is None (the default) we MUST be called within an
+        "except" block (such that sys.exc_info() returns useful
+        information).
+        """
+        if exception:
+            return FailedFuture(type(exception), exception, None)
+        return FailedFuture(*sys.exc_info())
 
-def add_callbacks(future, callback, errback):
-    """
-    callback or errback may be None, but at least one must be
-    non-None.
+    def add_callbacks(self, future, callback, errback):
+        """
+        callback or errback may be None, but at least one must be
+        non-None.
+        """
+        def done(f):
+            try:
+                res = f.result()
+                if callback:
+                    callback(res)
+            except Exception:
+                if errback:
+                    errback(create_failure())
+        return future.add_done_callback(done)
 
-    XXX beware the "f._result" hack to get "chainable-callback" type
-    behavior.
-    """
-    def done(f):
-        try:
-            res = f.result()
-            if callback:
-                x = callback(res)
-                if x is not None:
-                    f._result = x
-        except Exception:
-            if errback:
-                errback(create_failure())
-    return future.add_done_callback(done)
+    def gather(self, futures, consume_exceptions=True):
+        """
+        This returns a Future that waits for all the Futures in the list
+        ``futures``
 
+        :param futures: a list of Futures (or coroutines?)
 
-def gather(futures, consume_exceptions=True):
-    """
-    This returns a Future that waits for all the Futures in the list
-    ``futures``
+        :param consume_exceptions: if True, any errors are eaten and
+        returned in the result list.
+        """
 
-    :param futures: a list of Futures (or coroutines?)
+        # from the asyncio docs: "If return_exceptions is True, exceptions
+        # in the tasks are treated the same as successful results, and
+        # gathered in the result list; otherwise, the first raised
+        # exception will be immediately propagated to the returned
+        # future."
+        return asyncio.gather(*futures, return_exceptions=consume_exceptions)
 
-    :param consume_exceptions: if True, any errors are eaten and
-    returned in the result list.
-    """
+    def sleep(self, delay):
+        """
+        Inline sleep for use in co-routines.
 
-    # from the asyncio docs: "If return_exceptions is True, exceptions
-    # in the tasks are treated the same as successful results, and
-    # gathered in the result list; otherwise, the first raised
-    # exception will be immediately propagated to the returned
-    # future."
-    return asyncio.gather(*futures, return_exceptions=consume_exceptions)
+        :param delay: Time to sleep in seconds.
+        :type delay: float
+        """
+        return asyncio.sleep(delay)
 
 
-def set_global_log_level(level):
-    """
-    Set the global log level on all loggers instantiated by txaio.
-    """
-    for logger in _loggers:
-        logger._set_log_level(level)
-    global _log_level
-    _log_level = level
+_default_api = _AsyncioApi(config)
 
 
-def get_global_log_level():
-    return _log_level
+using_twisted = _default_api.using_twisted
+using_asyncio = _default_api.using_asyncio
+sleep = _default_api.sleep
+failure_message = _default_api.failure_message
+failure_traceback = _default_api.failure_traceback
+failure_format_traceback = _default_api.failure_format_traceback
+create_future = _default_api.create_future
+create_future_success = _default_api.create_future_success
+create_future_error = _default_api.create_future_error
+as_future = _default_api.as_future
+is_future = _default_api.is_future
+call_later = _default_api.call_later
+make_batched_timer = _default_api.make_batched_timer
+is_called = _default_api.is_called
+resolve = _default_api.resolve
+reject = _default_api.reject
+create_failure = _default_api.create_failure
+add_callbacks = _default_api.add_callbacks
+gather = _default_api.gather
+sleep = _default_api.sleep
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/_common.py python-txaio-2.8.0/txaio/_common.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/_common.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/txaio/_common.py	2017-04-15 16:17:17.000000000 +0200
@@ -38,27 +38,37 @@
     """
 
     def __init__(self, bucket_milliseconds, chunk_size,
-                 seconds_provider, delayed_call_creator):
+                 seconds_provider, delayed_call_creator, loop=None):
+        if bucket_milliseconds <= 0.0:
+            raise ValueError(
+                "bucket_milliseconds must be > 0.0"
+            )
         self._bucket_milliseconds = float(bucket_milliseconds)
         self._chunk_size = chunk_size
         self._get_seconds = seconds_provider
         self._create_delayed_call = delayed_call_creator
         self._buckets = dict()  # real seconds -> (IDelayedCall, list)
+        self._loop = loop
 
     def call_later(self, delay, func, *args, **kwargs):
         """
         IBatchedTimer API
         """
         # "quantize" the delay to the nearest bucket
-        real_time = int(self._get_seconds() + delay) * 1000
+        now = self._get_seconds()
+        real_time = int(now + delay) * 1000
         real_time -= int(real_time % self._bucket_milliseconds)
         call = _BatchedCall(self, real_time, lambda: func(*args, **kwargs))
         try:
             self._buckets[real_time][1].append(call)
         except KeyError:
             # new bucket; need to add "actual" underlying IDelayedCall
+            diff = (real_time / 1000.0) - now
+            # we need to clamp this because if we quantized to the
+            # nearest second, but that second is actually (slightly)
+            # less than the current time 'diff' will be negative.
             delayed_call = self._create_delayed_call(
-                (real_time / 1000.0) - self._get_seconds(),
+                max(0.0, diff),
                 self._notify_bucket, real_time,
             )
             self._buckets[real_time] = (delayed_call, [call])
@@ -96,7 +106,9 @@
         # ceil()ing because we want the number of chunks, and a
         # partial chunk is still a chunk
         delay_ms = self._bucket_milliseconds / math.ceil(float(len(calls)) / self._chunk_size)
-        notify_one_chunk(calls, self._chunk_size, delay_ms)
+        # I can't imagine any scenario in which chunk_delay_ms is
+        # actually less than zero, but just being safe here
+        notify_one_chunk(calls, self._chunk_size, max(0.0, delay_ms))
 
     def _remove_call(self, real_time, call):
         """
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/__init__.py python-txaio-2.8.0/txaio/__init__.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/__init__.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/txaio/__init__.py	2017-04-15 16:17:17.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
@@ -36,7 +36,7 @@
 # see aio.py for asyncio/trollius implementation
 
 
-class _Config:
+class _Config(object):
     """
     This holds all valid configuration options, accessed as
     class-level variables. For example, if you were using asyncio:
@@ -58,6 +58,7 @@
 
 
 __all__ = (
+    'with_config',              # allow mutliple custom configurations at once
     'using_twisted',            # True if we're using Twisted
     'using_asyncio',            # True if we're using asyncio
     'use_twisted',              # sets the library to use Twisted, or exception
@@ -65,34 +66,36 @@
 
     'config',                   # the config instance, access via attributes
 
-    'create_future',   # create a Future (can be already resolved/errored)
+    'create_future',            # create a Future (can be already resolved/errored)
     'create_future_success',
     'create_future_error',
-    'create_failure',  # return an object implementing IFailedFuture
-    'as_future',       # call a method, and always return a Future
-    'is_future',       # True for Deferreds in tx and Futures, @coroutines in asyncio
-    'reject',          # errback a Future
-    'resolve',         # callback 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
-
-    'call_later',      # call the callback after the given delay seconds
-
-    'failure_message',    # a printable error-message from a IFailedFuture
-    'failure_traceback',  # returns a traceback instance from an IFailedFuture
-    'failure_format_traceback',  # a string, the formatted traceback
-
-    'make_batched_timer',  # create BatchedTimer/IBatchedTimer instances
-
-    'make_logger',     # creates an object implementing ILogger
-    'start_logging',   # initializes logging (may grab stdin at this point)
-    'set_global_log_level',  # Set the global log level
-    'get_global_log_level',  # Get the global log level
+    'create_failure',           # return an object implementing IFailedFuture
+    'as_future',                # call a method, and always return a Future
+    'is_future',                # True for Deferreds in tx and Futures, @coroutines in asyncio
+    'reject',                   # errback a Future
+    'resolve',                  # callback 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
+
+    'call_later',               # call the callback after the given delay seconds
+
+    'failure_message',          # a printable error-message from a IFailedFuture
+    'failure_traceback',        # returns a traceback instance from an IFailedFuture
+    'failure_format_traceback',     # a string, the formatted traceback
+
+    'make_batched_timer',       # create BatchedTimer/IBatchedTimer instances
+
+    'make_logger',              # creates an object implementing ILogger
+    'start_logging',            # initializes logging (may grab stdin at this point)
+    'set_global_log_level',     # Set the global log level
+    'get_global_log_level',     # Get the global log level
     'add_log_categories',
 
-    'IFailedFuture',             # describes API for arg to errback()s
-    'ILogger',                   # API for logging
+    'IFailedFuture',            # describes API for arg to errback()s
+    'ILogger',                  # API for logging
+
+    'sleep',                    # little helper for inline sleeping
 )
 
 
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/interfaces.py python-txaio-2.8.0/txaio/interfaces.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/interfaces.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/txaio/interfaces.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/_iotype.py python-txaio-2.8.0/txaio/_iotype.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/_iotype.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/txaio/_iotype.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/testutil.py python-txaio-2.8.0/txaio/testutil.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/testutil.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/txaio/testutil.py	2016-11-06 20:23:19.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/tx.py python-txaio-2.8.0/txaio/tx.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/tx.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/txaio/tx.py	2017-04-15 16:42:18.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
@@ -104,7 +104,7 @@
         trace = 'trace'
 
         @classmethod
-        def lookupByName(cls, name):
+        def lookupByName(cls, name):  # noqa
             return getattr(cls, name)
 
     class _Logger(ILogger):
@@ -130,6 +130,18 @@
     _categories.update(categories)
 
 
+def with_config(loop=None):
+    global config
+    if loop is not None:
+        if config.loop is not None and config.loop is not loop:
+            raise RuntimeError(
+                "Twisted has only a single, global reactor. You passed in "
+                "a reactor different from the one already configured "
+                "in txaio.config.loop"
+            )
+    return _TxApi(config)
+
+
 # NOTE: beware that twisted.logger._logger.Logger copies itself via an
 # overriden __get__ method when used as recommended as a class
 # descriptor.  So, we override __get__ to just return ``self`` which
@@ -344,183 +356,184 @@
         log.startLogging(out)
 
 
-def failure_message(fail):
-    """
-    :param fail: must be an IFailedFuture
-    returns a unicode error-message
-    """
-    try:
-        return u'{0}: {1}'.format(
-            fail.value.__class__.__name__,
-            fail.getErrorMessage(),
-        )
-    except Exception:
-        return 'Failed to produce failure message for "{0}"'.format(fail)
-
-
-def failure_traceback(fail):
-    """
-    :param fail: must be an IFailedFuture
-    returns a traceback instance
-    """
-    return fail.tb
-
-
-def failure_format_traceback(fail):
-    """
-    :param fail: must be an IFailedFuture
-    returns a string
-    """
-    try:
-        f = six.StringIO()
-        fail.printTraceback(file=f)
-        return f.getvalue()
-    except Exception:
-        return u"Failed to format failure traceback for '{0}'".format(fail)
-
-
 _unspecified = object()
 
 
-def create_future(result=_unspecified, error=_unspecified):
-    if result is not _unspecified and error is not _unspecified:
-        raise ValueError("Cannot have both result and error.")
-
-    f = Deferred()
-    if result is not _unspecified:
-        resolve(f, result)
-    elif error is not _unspecified:
-        reject(f, error)
-    return f
-
-
-# maybe delete, just use create_future()
-def create_future_success(result):
-    return succeed(result)
-
-
-# maybe delete, just use create_future()
-def create_future_error(error=None):
-    return fail(create_failure(error))
+class _TxApi(object):
 
+    def __init__(self, config):
+        self._config = config
 
-def as_future(fun, *args, **kwargs):
-    return maybeDeferred(fun, *args, **kwargs)
-
-
-def is_future(obj):
-    return isinstance(obj, Deferred)
-
+    def failure_message(self, fail):
+        """
+        :param fail: must be an IFailedFuture
+        returns a unicode error-message
+        """
+        try:
+            return u'{0}: {1}'.format(
+                fail.value.__class__.__name__,
+                fail.getErrorMessage(),
+            )
+        except Exception:
+            return 'Failed to produce failure message for "{0}"'.format(fail)
 
-def call_later(delay, fun, *args, **kwargs):
-    return IReactorTime(_get_loop()).callLater(delay, fun, *args, **kwargs)
+    def failure_traceback(self, fail):
+        """
+        :param fail: must be an IFailedFuture
+        returns a traceback instance
+        """
+        return fail.tb
 
+    def failure_format_traceback(self, fail):
+        """
+        :param fail: must be an IFailedFuture
+        returns a string
+        """
+        try:
+            f = six.StringIO()
+            fail.printTraceback(file=f)
+            return f.getvalue()
+        except Exception:
+            return u"Failed to format failure traceback for '{0}'".format(fail)
+
+    def create_future(self, result=_unspecified, error=_unspecified):
+        if result is not _unspecified and error is not _unspecified:
+            raise ValueError("Cannot have both result and error.")
+
+        f = Deferred()
+        if result is not _unspecified:
+            resolve(f, result)
+        elif error is not _unspecified:
+            reject(f, error)
+        return f
+
+    def create_future_success(self, result):
+        return succeed(result)
+
+    def create_future_error(self, error=None):
+        return fail(create_failure(error))
+
+    def as_future(self, fun, *args, **kwargs):
+        return maybeDeferred(fun, *args, **kwargs)
 
-def make_batched_timer(bucket_seconds, chunk_size=100):
-    """
-    Creates and returns an object implementing
-    :class:`txaio.IBatchedTimer`.
+    def is_future(self, obj):
+        return isinstance(obj, Deferred)
 
-    :param bucket_seconds: the number of seconds in each bucket. That
-        is, a value of 5 means that any timeout within a 5 second
-        window will be in the same bucket, and get notified at the
-        same time. This is only accurate to "milliseconds".
-
-    :param chunk_size: when "doing" the callbacks in a particular
-        bucket, this controls how many we do at once before yielding to
-        the reactor.
-    """
+    def call_later(self, delay, fun, *args, **kwargs):
+        return IReactorTime(self._get_loop()).callLater(delay, fun, *args, **kwargs)
 
-    def get_seconds():
-        return _get_loop().seconds()
+    def make_batched_timer(self, bucket_seconds, chunk_size=100):
+        """
+        Creates and returns an object implementing
+        :class:`txaio.IBatchedTimer`.
 
-    def create_delayed_call(delay, fun, *args, **kwargs):
-        return _get_loop().callLater(delay, fun, *args, **kwargs)
+        :param bucket_seconds: the number of seconds in each bucket. That
+            is, a value of 5 means that any timeout within a 5 second
+            window will be in the same bucket, and get notified at the
+            same time. This is only accurate to "milliseconds".
+
+        :param chunk_size: when "doing" the callbacks in a particular
+            bucket, this controls how many we do at once before yielding to
+            the reactor.
+        """
 
-    return _BatchedTimer(
-        bucket_seconds * 1000.0, chunk_size,
-        seconds_provider=get_seconds,
-        delayed_call_creator=create_delayed_call,
-    )
+        def get_seconds():
+            return self._get_loop().seconds()
 
+        def create_delayed_call(delay, fun, *args, **kwargs):
+            return self._get_loop().callLater(delay, fun, *args, **kwargs)
 
-def is_called(future):
-    return future.called
+        return _BatchedTimer(
+            bucket_seconds * 1000.0, chunk_size,
+            seconds_provider=get_seconds,
+            delayed_call_creator=create_delayed_call,
+        )
 
+    def is_called(self, future):
+        return future.called
 
-def resolve(future, result=None):
-    future.callback(result)
+    def resolve(self, future, result=None):
+        future.callback(result)
 
+    def reject(self, future, error=None):
+        if error is None:
+            error = create_failure()
+        elif isinstance(error, Exception):
+            error = Failure(error)
+        else:
+            if not isinstance(error, Failure):
+                raise RuntimeError("reject requires a Failure or Exception")
+        future.errback(error)
 
-def reject(future, error=None):
-    if error is None:
-        error = create_failure()
-    elif isinstance(error, Exception):
-        error = Failure(error)
-    else:
-        if not isinstance(error, Failure):
-            raise RuntimeError("reject requires a Failure or Exception")
-    future.errback(error)
+    def create_failure(self, exception=None):
+        """
+        Create a Failure instance.
 
+        if ``exception`` is None (the default), we MUST be inside an
+        "except" block. This encapsulates the exception into an object
+        that implements IFailedFuture
+        """
+        if exception:
+            return Failure(exception)
+        return Failure()
 
-def create_failure(exception=None):
-    """
-    Create a Failure instance.
+    def add_callbacks(self, future, callback, errback):
+        """
+        callback or errback may be None, but at least one must be
+        non-None.
+        """
+        assert future is not None
+        if callback is None:
+            assert errback is not None
+            future.addErrback(errback)
+        else:
+            # Twisted allows errback to be None here
+            future.addCallbacks(callback, errback)
+        return future
+
+    def gather(self, futures, consume_exceptions=True):
+        def completed(res):
+            rtn = []
+            for (ok, value) in res:
+                rtn.append(value)
+                if not ok and not consume_exceptions:
+                    value.raiseException()
+            return rtn
+
+        # XXX if consume_exceptions is False in asyncio.gather(), it will
+        # abort on the first raised exception -- should we set
+        # fireOnOneErrback=True (if consume_exceptions=False?) -- but then
+        # we'll have to wrap the errback() to extract the "real" failure
+        # from the FirstError that gets thrown if you set that ...
+
+        dl = DeferredList(list(futures), consumeErrors=consume_exceptions)
+        # we unpack the (ok, value) tuples into just a list of values, so
+        # that the callback() gets the same value in asyncio and Twisted.
+        add_callbacks(dl, completed, None)
+        return dl
 
-    if ``exception`` is None (the default), we MUST be inside an
-    "except" block. This encapsulates the exception into an object
-    that implements IFailedFuture
-    """
-    if exception:
-        return Failure(exception)
-    return Failure()
+    def sleep(self, delay):
+        """
+        Inline sleep for use in co-routines.
 
+        :param delay: Time to sleep in seconds.
+        :type delay: float
+        """
+        d = Deferred()
+        self._get_loop().callLater(delay, d.callback, None)
+        return d
 
-def add_callbacks(future, callback, errback):
-    """
-    callback or errback may be None, but at least one must be
-    non-None.
-    """
-    assert future is not None
-    if callback is None:
-        assert errback is not None
-        future.addErrback(errback)
-    else:
-        # Twisted allows errback to be None here
-        future.addCallbacks(callback, errback)
-    return future
-
-
-def gather(futures, consume_exceptions=True):
-    def completed(res):
-        rtn = []
-        for (ok, value) in res:
-            rtn.append(value)
-            if not ok and not consume_exceptions:
-                value.raiseException()
-        return rtn
-
-    # XXX if consume_exceptions is False in asyncio.gather(), it will
-    # abort on the first raised exception -- should we set
-    # fireOnOneErrback=True (if consume_exceptions=False?) -- but then
-    # we'll have to wrap the errback() to extract the "real" failure
-    # from the FirstError that gets thrown if you set that ...
-
-    dl = DeferredList(list(futures), consumeErrors=consume_exceptions)
-    # we unpack the (ok, value) tuples into just a list of values, so
-    # that the callback() gets the same value in asyncio and Twisted.
-    add_callbacks(dl, completed, None)
-    return dl
-
-
-# methods internal to this implementation
-
-
-def _get_loop():
-    if config.loop is None:
-        from twisted.internet import reactor
-        config.loop = reactor
-    return config.loop
+    def _get_loop(self):
+        """
+        internal helper
+        """
+        # we import and assign the default here (and not, e.g., when
+        # making Config) so as to delay importing reactor as long as
+        # possible in case someone is installing a custom one.
+        if self._config.loop is None:
+            from twisted.internet import reactor
+            self._config.loop = reactor
+        return self._config.loop
 
 
 def set_global_log_level(level):
@@ -536,3 +549,25 @@
 
 def get_global_log_level():
     return _log_level
+
+
+_default_api = _TxApi(config)
+
+
+failure_message = _default_api.failure_message
+failure_traceback = _default_api.failure_traceback
+failure_format_traceback = _default_api.failure_format_traceback
+create_future = _default_api.create_future
+create_future_success = _default_api.create_future_success
+create_future_error = _default_api.create_future_error
+as_future = _default_api.as_future
+is_future = _default_api.is_future
+call_later = _default_api.call_later
+make_batched_timer = _default_api.make_batched_timer
+is_called = _default_api.is_called
+resolve = _default_api.resolve
+reject = _default_api.reject
+create_failure = _default_api.create_failure
+add_callbacks = _default_api.add_callbacks
+gather = _default_api.gather
+sleep = _default_api.sleep
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/_unframework.py python-txaio-2.8.0/txaio/_unframework.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/_unframework.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/txaio/_unframework.py	2017-04-15 16:17:17.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (c) Tavendo GmbH
+# 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
@@ -44,7 +44,9 @@
         "with .use_twisted() or .use_asyncio()"
     )
 
+
 # all the txaio API methods just raise the error
+with_config = _throw_usage_error
 create_future = _throw_usage_error
 create_future_success = _throw_usage_error
 create_future_error = _throw_usage_error
@@ -74,3 +76,5 @@
 
 IFailedFuture = _throw_usage_error
 ILogger = _throw_usage_error
+
+sleep = _throw_usage_error
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/_version.py python-txaio-2.8.0/txaio/_version.py
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio/_version.py	2016-10-03 20:14:16.000000000 +0200
+++ python-txaio-2.8.0/txaio/_version.py	2017-06-08 18:36:41.000000000 +0200
@@ -1 +1 @@
-__version__ = u'2.5.1'
+__version__ = u'2.8.0'
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/dependency_links.txt python-txaio-2.8.0/txaio.egg-info/dependency_links.txt
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/dependency_links.txt	1970-01-01 01:00:00.000000000 +0100
+++ python-txaio-2.8.0/txaio.egg-info/dependency_links.txt	2017-06-08 18:38:00.000000000 +0200
@@ -0,0 +1 @@
+
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/PKG-INFO python-txaio-2.8.0/txaio.egg-info/PKG-INFO
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/PKG-INFO	1970-01-01 01:00:00.000000000 +0100
+++ python-txaio-2.8.0/txaio.egg-info/PKG-INFO	2017-06-08 18:38:00.000000000 +0200
@@ -0,0 +1,104 @@
+Metadata-Version: 1.1
+Name: txaio
+Version: 2.8.0
+Summary: Compatibility API between asyncio/Twisted/Trollius
+Home-page: https://github.com/crossbario/txaio
+Author: Crossbar.io Technologies GmbH
+Author-email: autobahnws at googlegroups.com
+License: MIT License
+Description: txaio
+        =====
+        
+        | |Version| |Build Status| |Coverage| |Docs|
+        
+        --------------
+        
+        **txaio** is a helper library for writing code that runs unmodified on
+        both `Twisted <https://twistedmatrix.com/>`_ and `asyncio <https://docs.python.org/3/library/asyncio.html>`_ / `Trollius <http://trollius.readthedocs.org/en/latest/index.html>`_.
+        
+        This is like `six <http://pythonhosted.org/six/>`_, but for wrapping
+        over differences between Twisted and asyncio so one can write code
+        that runs unmodified on both (aka *source code compatibility*). In
+        other words: your *users* can choose if they want asyncio **or** Twisted
+        as a dependency.
+        
+        Note that, with this approach, user code **runs under the native event
+        loop of either Twisted or asyncio**. This is different from attaching
+        either one's event loop to the other using some event loop adapter.
+        
+        
+        Platform support
+        ----------------
+        
+        **txaio** runs on CPython 2.7/3.3+ and PyPy 2, on top of Twisted or asyncio. Specifically, **txaio** is tested on the following platforms:
+        
+        * CPython 2.7 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
+        * 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)
+        * PyPy 2 on Twisted 12.1, 13.2, 15.4, 16.5, trunk and on Trollius 2.0
+        
+        
+        How it works
+        ------------
+        
+        Instead of directly importing, instantiating and using ``Deferred``
+        (for Twisted) or ``Future`` (for asyncio) objects, **txaio** provides
+        helper-functions to do that for you, as well as associated things like
+        adding callbacks or errbacks.
+        
+        This obviously changes the style of your code, but then you can choose
+        at runtime (or import time) which underlying event-loop to use. This
+        means you can write **one** code-base that can run on Twisted *or*
+        asyncio (without a Twisted dependency) as you or your users see fit.
+        
+        Code like the following can then run on *either* system:
+        
+        .. sourcecode:: python
+        
+            import txaio
+            txaio.use_twisted()  # or .use_asyncio()
+        
+            f0 = txaio.create_future()
+            f1 = txaio.as_future(some_func, 1, 2, key='word')
+            txaio.add_callbacks(f0, callback, errback)
+            txaio.add_callbacks(f1, callback, errback)
+            # ...
+            txaio.resolve(f0, "value")
+            txaio.reject(f1, RuntimeError("it failed"))
+        
+        Please refer to the `documentation <https://txaio.readthedocs.io/en/latest/>`_ for description and usage of the library features.
+        
+        
+        .. |Version| image:: https://img.shields.io/pypi/v/txaio.svg
+           :target: https://pypi.python.org/pypi/txaio
+        
+        .. |Build Status| image:: https://travis-ci.org/crossbario/txaio.svg?branch=master
+           :target: https://travis-ci.org/crossbario/txaio
+        
+        .. |Coverage| image:: https://codecov.io/github/crossbario/txaio/coverage.svg?branch=master
+           :target: https://codecov.io/github/crossbario/txaio
+        
+        .. |Docs| image:: https://readthedocs.org/projects/txaio/badge/?version=latest
+           :target: https://txaio.readthedocs.io/en/latest/
+        
+Keywords: asyncio twisted trollius coroutine
+Platform: Any
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Framework :: Twisted
+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 :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/requires.txt python-txaio-2.8.0/txaio.egg-info/requires.txt
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/requires.txt	1970-01-01 01:00:00.000000000 +0100
+++ python-txaio-2.8.0/txaio.egg-info/requires.txt	2017-06-08 18:38:00.000000000 +0200
@@ -0,0 +1,23 @@
+six
+
+[all]
+zope.interface>=3.6
+twisted>=12.1.0
+
+[asyncio]
+
+[dev]
+pytest>=2.6.4
+pytest-cov>=1.8.1
+pep8>=1.6.2
+sphinx>=1.2.3
+pyenchant>=1.6.6
+sphinxcontrib-spelling>=2.1.2
+sphinx_rtd_theme>=0.1.9
+tox>=2.1.1
+mock==1.3.0
+twine>=1.6.5
+
+[twisted]
+zope.interface>=3.6
+twisted>=12.1.0
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/SOURCES.txt python-txaio-2.8.0/txaio.egg-info/SOURCES.txt
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/SOURCES.txt	1970-01-01 01:00:00.000000000 +0100
+++ python-txaio-2.8.0/txaio.egg-info/SOURCES.txt	2017-06-08 18:38:00.000000000 +0200
@@ -0,0 +1,49 @@
+LICENSE
+MANIFEST.in
+Makefile
+README.rst
+setup.cfg
+setup.py
+tox.ini
+docs/Makefile
+docs/api.rst
+docs/conf.py
+docs/contents.rst
+docs/index.rst
+docs/overview.rst
+docs/programming-guide.rst
+docs/releases.rst
+examples/basic.py
+examples/log_interop_stdlib.py
+examples/log_interop_twisted.py
+examples/multiloop.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_create.py
+test/test_errback.py
+test/test_gather.py
+test/test_imports.py
+test/test_is_future.py
+test/test_legacy_logging.py
+test/test_logging.py
+test/test_packaging.py
+test/util.py
+txaio/__init__.py
+txaio/_common.py
+txaio/_iotype.py
+txaio/_unframework.py
+txaio/_version.py
+txaio/aio.py
+txaio/interfaces.py
+txaio/testutil.py
+txaio/tx.py
+txaio.egg-info/PKG-INFO
+txaio.egg-info/SOURCES.txt
+txaio.egg-info/dependency_links.txt
+txaio.egg-info/requires.txt
+txaio.egg-info/top_level.txt
+txaio.egg-info/zip-safe
\ No newline at end of file
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/top_level.txt python-txaio-2.8.0/txaio.egg-info/top_level.txt
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/top_level.txt	1970-01-01 01:00:00.000000000 +0100
+++ python-txaio-2.8.0/txaio.egg-info/top_level.txt	2017-06-08 18:38:00.000000000 +0200
@@ -0,0 +1 @@
+txaio
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/zip-safe python-txaio-2.8.0/txaio.egg-info/zip-safe
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/txaio.egg-info/zip-safe	1970-01-01 01:00:00.000000000 +0100
+++ python-txaio-2.8.0/txaio.egg-info/zip-safe	2017-06-08 18:38:00.000000000 +0200
@@ -0,0 +1 @@
+
-------------- next part --------------
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/changelog python-txaio-2.8.0/debian/changelog
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/changelog	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/changelog	2017-07-19 08:32:33.000000000 +0200
@@ -1,3 +1,18 @@
+python-txaio (2.8.0-0.1) unstable; urgency=medium
+
+  [ Gianfranco Costamagna ]
+  * Non-maintainer upload.
+  * Drop useless, conflicting usr/LICENSE file in both binary packages.
+  * Update copyright year and author
+  * Update watch file.
+
+  [ Michael Hudson-Doyle ]
+  * New upstream release (Closes: #867037).
+  * d/control: drop build-dependency/dependency on python3-trollius.
+  * d/patches/remove-privacy-breach-in-docs.patch: regenerate.
+
+ -- Gianfranco Costamagna <locutusofborg at debian.org>  Wed, 19 Jul 2017 08:32:33 +0200
+
 python-txaio (2.5.1+2016.10.03.git.623ef68776-1) unstable; urgency=medium
 
   * New upstream release based on commit 623ef68776:
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/control python-txaio-2.8.0/debian/control
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/control	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/control	2017-07-19 08:32:33.000000000 +0200
@@ -24,7 +24,6 @@
                      python3-mock (>= 1.3),
                      python3-pytest,
                      python3-six,
-                     python3-trollius,
                      python3-twisted,
                      python3-zope.interface,
 Standards-Version: 3.9.8
@@ -55,7 +54,6 @@
 Package: python3-txaio
 Architecture: all
 Depends: python3-six,
-         python3-trollius,
          python3-twisted,
          python3-zope.interface,
          ${misc:Depends},
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/copyright python-txaio-2.8.0/debian/copyright
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/copyright	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/copyright	2017-07-19 08:28:26.000000000 +0200
@@ -3,7 +3,7 @@
 Source: https://github.com/tavendo/txaio
 
 Files: *
-Copyright: (c) 2015, Tavendo GmbH <autobahnws at googlegroups.com>
+Copyright: (c) 2015 Crossbar.io Technologies GmbH
 License: Expat
 
 Files: debian/*
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/patches/remove-privacy-breach-in-docs.patch python-txaio-2.8.0/debian/patches/remove-privacy-breach-in-docs.patch
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/patches/remove-privacy-breach-in-docs.patch	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/patches/remove-privacy-breach-in-docs.patch	2017-06-30 01:18:36.000000000 +0200
@@ -3,31 +3,22 @@
 Forwarded: no
 Last-Update: 2016-07-13
 
---- python-txaio-2.5.1.orig/README.rst
-+++ python-txaio-2.5.1/README.rst
-@@ -58,25 +58,3 @@ Code like the following can then run on
-     # ...
-     txaio.resolve(f0, "value")
+--- a/README.rst
++++ b/README.rst
+@@ -60,16 +60,3 @@
      txaio.reject(f1, RuntimeError("it failed"))
+ 
+ Please refer to the `documentation <https://txaio.readthedocs.io/en/latest/>`_ for description and usage of the library features.
 -
 -
 -.. |Version| image:: https://img.shields.io/pypi/v/txaio.svg
 -   :target: https://pypi.python.org/pypi/txaio
 -
--.. |Downloads| image:: https://img.shields.io/pypi/dm/txaio.svg
--   :target: https://pypi.python.org/pypi/txaio
--
--.. |GitHub Stars| image:: https://img.shields.io/github/stars/crossbario/txaio.svg?style=social&label=Star
--   :target: https://github.com/crossbario/txaio
--
--.. |Master Branch| image:: https://img.shields.io/badge/branch-master-orange.svg
--   :target: https://travis-ci.org/crossbario/txaio.svg?branch=master
--
 -.. |Build Status| image:: https://travis-ci.org/crossbario/txaio.svg?branch=master
 -   :target: https://travis-ci.org/crossbario/txaio
 -
--.. |Coverage| image:: https://img.shields.io/codecov/c/github/crossbario/txaio/master.svg
+-.. |Coverage| image:: https://codecov.io/github/crossbario/txaio/coverage.svg?branch=master
 -   :target: https://codecov.io/github/crossbario/txaio
 -
--.. |Docs| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat
--   :target: http://txaio.readthedocs.org/en/latest/
+-.. |Docs| image:: https://readthedocs.org/projects/txaio/badge/?version=latest
+-   :target: https://txaio.readthedocs.io/en/latest/
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/rules python-txaio-2.8.0/debian/rules
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/rules	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/rules	2017-07-14 11:18:18.000000000 +0200
@@ -8,6 +8,8 @@
 
 override_dh_auto_install:
 	pkgos-dh_auto_install
+	# they are useless and conflicting files
+	find debian -name LICENSE -delete
 
 override_dh_auto_test:
 ifeq (,$(findstring nocheck, $(DEB_BUILD_OPTIONS)))
diff -Nru python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/watch python-txaio-2.8.0/debian/watch
--- python-txaio-2.5.1+2016.10.03.git.623ef68776/debian/watch	2016-10-13 19:51:07.000000000 +0200
+++ python-txaio-2.8.0/debian/watch	2017-07-19 08:31:26.000000000 +0200
@@ -1,3 +1,3 @@
 version=3
-http://pypi.python.org/packages/source/t/txaio txaio-(.*).tar.gz
-
+opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
+https://pypi.debian.net/txaio/txaio-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: OpenPGP digital signature
URL: <http://lists.alioth.debian.org/pipermail/openstack-devel/attachments/20170719/e3740d59/attachment-0001.sig>


More information about the Openstack-devel mailing list