Hello community, here is the log from the commit of package python-pyee for openSUSE:Factory checked in at 2020-03-31 17:14:16 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pyee (Old) and /work/SRC/openSUSE:Factory/.python-pyee.new.3160 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyee" Tue Mar 31 17:14:16 2020 rev:6 rq:789777 version:7.0.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pyee/python-pyee.changes 2019-04-26 22:54:30.813312683 +0200 +++ /work/SRC/openSUSE:Factory/.python-pyee.new.3160/python-pyee.changes 2020-03-31 17:14:31.219599976 +0200 @@ -1,0 +2,13 @@ +Thu Mar 26 14:04:57 UTC 2020 - [email protected] + +- version update to 7.0.1 + - Some tweaks to the docs + - Added a ``TrioEventEmitter`` class for intended use with trio + - ``AsyncIOEventEmitter`` now correctly handles cancellations + - Add a new experimental ``pyee.uplift`` API for adding new functionality to + existing event emitters +- deleted patches + - fix-build-requirements.patch (upstreamed) +- Add patch python-pyee-import-asyncio.exceptions.patch + +------------------------------------------------------------------- Old: ---- fix-build-requirements.patch pyee-6.0.0.tar.gz New: ---- pyee-7.0.1.tar.gz python-pyee-import-asyncio.exceptions.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pyee.spec ++++++ --- /var/tmp/diff_new_pack.n0bt24/_old 2020-03-31 17:14:32.075600520 +0200 +++ /var/tmp/diff_new_pack.n0bt24/_new 2020-03-31 17:14:32.075600520 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-pyee # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,9 +17,8 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} -%bcond_without test Name: python-pyee -Version: 6.0.0 +Version: 7.0.1 Release: 0 Summary: A port of node.js's EventEmitter to python License: MIT @@ -27,8 +26,9 @@ URL: https://github.com/jfhbrook/pyee Source: https://files.pythonhosted.org/packages/source/p/pyee/pyee-%{version}.tar.gz Source99: https://raw.githubusercontent.com/jfhbrook/pyee/master/LICENSE -# PATCH-FIX-UPSTREAM fix-build-requirements.patch -Patch0: fix-build-requirements.patch +# test_async.py: import asyncio.exception.TimeoutError +# https://github.com/jfhbrook/pyee/issues/68 +Patch0: python-pyee-import-asyncio.exceptions.patch BuildRequires: %{python_module PyHamcrest} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module vcversioner} @@ -36,12 +36,11 @@ BuildRequires: python-rpm-macros BuildArch: noarch # SECTION test requirements -%if %{with test} BuildRequires: %{python_module Twisted} BuildRequires: %{python_module mock} BuildRequires: %{python_module pytest-runner} BuildRequires: python3-pytest-asyncio -%endif +BuildRequires: python3-pytest-trio # /SECTION %python_subpackages @@ -62,15 +61,13 @@ %python_install %python_expand %fdupes %{buildroot}%{$python_sitelib} -%if %{with test} && 0%{?suse_version} >= 1500 %check -# Only run tests for python3 +# Only run tests for python3 (see test requirements) %{python_expand # if [ "${python_flavor}" == "python3" ]; then python3 setup.py test fi } -%endif %files %{python_files} %doc README.rst ++++++ pyee-6.0.0.tar.gz -> pyee-7.0.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/CHANGELOG.rst new/pyee-7.0.1/CHANGELOG.rst --- old/pyee-6.0.0/CHANGELOG.rst 2019-04-13 19:47:29.000000000 +0200 +++ new/pyee-7.0.1/CHANGELOG.rst 2020-01-30 21:42:15.000000000 +0100 @@ -1,5 +1,16 @@ +2020/01/30 Version 7.0.1 +------------------------ +- Some tweaks to the docs + +2020/01/30 Version 7.0.0 +------------------------ +- Added a ``TrioEventEmitter`` class for intended use with trio +- ``AsyncIOEventEmitter`` now correctly handles cancellations +- Add a new experimental ``pyee.uplift`` API for adding new functionality to + existing event emitters + 2019/04/11 Version 6.0.0 ------------------------ +------------------------ - Added a ``BaseEventEmitter`` class which is entirely synchronous and intended for simple use and for subclassing - Added an ``AsyncIOEventEmitter`` class for intended use with asyncio diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/PKG-INFO new/pyee-7.0.1/PKG-INFO --- old/pyee-6.0.0/PKG-INFO 2019-04-13 19:50:13.000000000 +0200 +++ new/pyee-7.0.1/PKG-INFO 2020-01-30 21:42:40.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pyee -Version: 6.0.0 +Version: 7.0.1 Summary: A port of node.js's EventEmitter to python. Home-page: https://github.com/jfhbrook/pyee Author: Joshua Holbrook diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/pyee/__init__.py new/pyee-7.0.1/pyee/__init__.py --- old/pyee-6.0.0/pyee/__init__.py 2019-04-13 19:47:29.000000000 +0200 +++ new/pyee-7.0.1/pyee/__init__.py 2020-01-30 21:23:11.000000000 +0100 @@ -33,7 +33,6 @@ BaseEventEmitter, PyeeException ) - from pyee._compat import CompatEventEmitter as EventEmitter __all__ = ['BaseEventEmitter', 'EventEmitter', 'PyeeException'] @@ -55,3 +54,9 @@ __all__.append('ExecutorEventEmitter') except ImportError: pass + +try: + from pyee._trio import TrioEventEmitter # noqa + __all__.append('TrioEventEmitter') +except (ImportError, SyntaxError): + pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/pyee/_asyncio.py new/pyee-7.0.1/pyee/_asyncio.py --- old/pyee-6.0.0/pyee/_asyncio.py 2019-04-13 19:47:29.000000000 +0200 +++ new/pyee-7.0.1/pyee/_asyncio.py 2020-01-30 21:22:49.000000000 +0100 @@ -54,6 +54,9 @@ if f: @f.add_done_callback def _callback(f): + if f.cancelled(): + return + exc = f.exception() if exc: self.emit('error', exc) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/pyee/_base.py new/pyee-7.0.1/pyee/_base.py --- old/pyee-6.0.0/pyee/_base.py 2019-04-13 19:47:29.000000000 +0200 +++ new/pyee-7.0.1/pyee/_base.py 2020-01-30 21:23:11.000000000 +0100 @@ -84,6 +84,15 @@ else: raise PyeeException("Uncaught, unspecified 'error' event.") + def _call_handlers(self, event, args, kwargs): + handled = False + + for f in list(self._events[event].values()): + self._emit_run(f, args, kwargs) + handled = True + + return handled + def emit(self, event, *args, **kwargs): """Emit ``event``, passing ``*args`` and ``**kwargs`` to each attached function. Returns ``True`` if any functions are attached to ``event``; @@ -96,11 +105,7 @@ Assuming ``data`` is an attached function, this will call ``data('00101001')'``. """ - handled = False - - for f in list(self._events[event].values()): - self._emit_run(f, args, kwargs) - handled = True + handled = self._call_handlers(event, args, kwargs) if not handled: self._emit_handle_potential_error(event, args[0] if args else None) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/pyee/_compat.py new/pyee-7.0.1/pyee/_compat.py --- old/pyee-6.0.0/pyee/_compat.py 2019-04-13 19:47:29.000000000 +0200 +++ new/pyee-7.0.1/pyee/_compat.py 2020-01-30 21:41:33.000000000 +0100 @@ -12,7 +12,7 @@ """An EventEmitter exposed for compatibility with prior versions of pyee. This functionality is deprecated; you should instead use either ``AsyncIOEventEmitter``, ``TwistedEventEmitter``, ``ExecutorEventEmitter``, - or ``BaseEventEmitter``. + ``TrioEventEmitter`` or ``BaseEventEmitter``. This class is similar to the ``AsyncIOEventEmitter`` class, but also allows for overriding the scheduler function (``ensure_future`` by default as in @@ -36,7 +36,8 @@ 'pyee.EventEmitter is deprecated and will be removed in a future ' 'major version; you should instead use either ' 'pyee.AsyncIOEventEmitter, pyee.TwistedEventEmitter, ' - 'pyee.ExecutorEventEmitter or pyee.BaseEventEmitter.' + 'pyee.ExecutorEventEmitter, pyee.TrioEventEmitter, ' + 'or pyee.BaseEventEmitter.' )) super(CompatEventEmitter, self).__init__() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/pyee/_trio.py new/pyee-7.0.1/pyee/_trio.py --- old/pyee-6.0.0/pyee/_trio.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyee-7.0.1/pyee/_trio.py 2020-01-30 21:16:37.000000000 +0100 @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- + +from contextlib import asynccontextmanager +import trio +from pyee._base import BaseEventEmitter, PyeeException + +__all__ = ['TrioEventEmitter'] + + +class TrioEventEmitter(BaseEventEmitter): + """An event emitter class which can run trio tasks in a trio nursery. + + By default, this class will lazily create both a nursery manager (the + object returned from ``trio.open_nursery()`` and a nursery (the object + yielded by using the nursery manager as an async context manager). It is + also possible to supply an existing nursery manager via the ``manager`` + argument, or an existing nursery via the ``nursery`` argument. + + Instances of TrioEventEmitter are themselves async context managers, so + that they may manage the lifecycle of the underlying trio nursery. For + example, typical usage of this library may look something like this:: + + async with TrioEventEmitter() as ee: + # Underlying nursery is instantiated and ready to go + @ee.on('data') + async def handler(data): + print(data) + + ee.emit('event') + + # Underlying nursery and manager have been cleaned up + + Unlike the case with the BaseEventEmitter, all exceptions raised by event + handlers are automatically emitted on the ``error`` event. This is + important for trio coroutines specifically but is also handled for + synchronous functions for consistency. + + For trio coroutine event handlers, calling emit is non-blocking. In other + words, you should not attempt to await emit; the coroutine is scheduled + in a fire-and-forget fashion. + """ + def __init__(self, nursery=None, manager=None): + super(TrioEventEmitter, self).__init__() + if nursery: + if manager: + raise PyeeException( + 'You may either pass a nursery or a nursery manager ' + 'but not both' + ) + self._nursery = nursery + self._manager = None + elif manager: + self._nursery = None + self._manager = manager + else: + self._manager = trio.open_nursery() + + def _async_runner(self, f, args, kwargs): + async def runner(): + try: + await f(*args, **kwargs) + except Exception as exc: + self.emit('error', exc) + + return runner + + def _emit_run(self, f, args, kwargs): + self._nursery.start_soon(self._async_runner(f, args, kwargs)) + + @asynccontextmanager + async def context(self): + """Returns an async contextmanager which manages the underlying + nursery to the EventEmitter. The ``TrioEventEmitter``'s + async context management methods are implemented using this + function, but it may also be used directly for clarity. + """ + if getattr(self, '_nursery', None): + yield self._nursery + else: + async with self._manager as nursery: + self._nursery = nursery + yield self + + async def __aenter__(self): + self._context = self.context() + return await self._context.__aenter__() + + async def __aexit__(self, type, value, traceback): + rv = await self._context.__aexit__(type, value, traceback) + self._context = None + self._nursery = None + self._manager = None + return rv diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/pyee/uplift.py new/pyee-7.0.1/pyee/uplift.py --- old/pyee-6.0.0/pyee/uplift.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyee-7.0.1/pyee/uplift.py 2020-01-30 21:39:14.000000000 +0100 @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- + +from functools import wraps + + +def _wrap(left, right, error_handler, proxy_new_listener): + left_emit = left.emit + left_unwrap = getattr(left, 'unwrap', None) + + @wraps(left_emit) + def wrapped_emit(event, *args, **kwargs): + left_handled = left._call_handlers(event, args, kwargs) + + # Do it for the right side + if proxy_new_listener or event != 'new_listener': + right_handled = right._call_handlers(event, args, kwargs) + else: + right_handled = False + + handled = left_handled or right_handled + + # Use the error handling on ``error_handler`` (should either be + # ``left`` or ``right``) + if not handled: + error_handler._emit_handle_potential_error( + event, args[0] if args else None + ) + + return handled + + def unwrap(): + left.emit = left_emit + if left_unwrap: + left.unwrap = left_unwrap + else: + del left.unwrap + left.emit = left_emit + + if hasattr(right, 'unwrap'): + right.unwrap() + + left.emit = wrapped_emit + left.unwrap = unwrap + + +_PROXY_NEW_LISTENER_SETTINGS = dict( + forward=(False, True), + backward=(True, False), + both=(True, True), + neither=(False, False) +) + + +def uplift( + cls, underlying, + error_handling='new', proxy_new_listener='forward', + *args, **kwargs +): + """A helper to create instances of an event emitter ``cls`` that inherits + event behavior from an ``underlying`` event emitter instance. + + This is mostly helpful if you have a simple underlying event emitter + that you don't have direct control over, but you want to use that + event emitter in a new context - for example, you may want to ``uplift`` a + ``BaseEventEmitter`` supplied by a third party library into an + ``AsyncIOEventEmitter`` so that you may register async event handlers + in your ``asyncio`` app but still be able to receive events from the + underlying event emitter and call the underlying event emitter's existing + handlers. This trick will also often work for a deprecated + ``EventEmitter`` instance. + + When called, ``uplift`` instantiates a new instance of ``cls``, passing + along any unrecognized arguments, and overwrites the ``emit`` method on + the ``underlying`` event emitter to also emit events on the new event + emitter and vice versa. In both cases, they return whether the ``emit`` + method was handled by either emitter. Execution order prefers the event + emitter on which ``emit`` was called. + + ``uplift`` also adds an ``unwrap`` method to both instances, either of + which will unwrap both ``emit`` methods when called. + + The ``error_handling`` flag can be configured to control what happens to + unhandled errors: + + - 'new': Error handling for the new event emitter is always used and the + underlying library's non-event-based error handling is inert. + - 'underlying': Error handling on the underlying event emitter is always + used and the new event emitter can not implement non-event-based error + handling. + - 'neither': Error handling for the new event emitter is used if the + handler was registered on the new event emitter, and vice versa. + + Tuning this option can be useful depending on how the underlying event + emitter does error handling. The default is 'new'. + + The ``proxy_new_listener`` option can be configured to control how + ``new_listener`` events are treated: + + - 'forward': ``new_listener`` events are propagated from the underlying + - 'both': ``new_listener`` events are propagated as with other events. + - 'neither': ``new_listener`` events are only fired on their respective + event emitters. + event emitter to the new event emitter but not vice versa. + - 'backward': ``new_listener`` events are propagated from the new event + emitter to the underlying event emitter, but not vice versa. + + Tuning this option can be useful depending on how the ``new_listener`` + event is used by the underlying event emitter, if at all. The default is + 'forward', since ``underlying`` may not know how to handle certain + handlers, such as asyncio coroutines. + + Each event emitter tracks its own internal table of handlers. + ``remove_listener``, ``remove_all_listeners`` and ``listeners`` all + work independently. This means you will have to remember which event + emitter an event handler was added to! + + Note that both the new event emitter returned by ``cls`` and the + underlying event emitter should inherit from ``BaseEventEmitter``, or at + least implement the interface for the undocumented ``_call_handlers`` and + ``_emit_handle_potential_error`` methods. + """ + + new_proxy_new_listener, underlying_proxy_new_listener = ( + _PROXY_NEW_LISTENER_SETTINGS[ + proxy_new_listener + ] + ) + + new = cls(*args, **kwargs) + + uplift_error_handlers = dict( + new=(new, new), + underlying=(underlying, underlying), + neither=(new, underlying) + ) + + new_error_handler, underlying_error_handler = uplift_error_handlers[ + error_handling + ] + + _wrap( + new, underlying, + new_error_handler, + new_proxy_new_listener + ) + _wrap( + underlying, new, + underlying_error_handler, + underlying_proxy_new_listener + ) + + return new diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/pyee.egg-info/PKG-INFO new/pyee-7.0.1/pyee.egg-info/PKG-INFO --- old/pyee-6.0.0/pyee.egg-info/PKG-INFO 2019-04-13 19:50:13.000000000 +0200 +++ new/pyee-7.0.1/pyee.egg-info/PKG-INFO 2020-01-30 21:42:39.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pyee -Version: 6.0.0 +Version: 7.0.1 Summary: A port of node.js's EventEmitter to python. Home-page: https://github.com/jfhbrook/pyee Author: Joshua Holbrook diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/pyee.egg-info/SOURCES.txt new/pyee-7.0.1/pyee.egg-info/SOURCES.txt --- old/pyee-6.0.0/pyee.egg-info/SOURCES.txt 2019-04-13 19:50:13.000000000 +0200 +++ new/pyee-7.0.1/pyee.egg-info/SOURCES.txt 2020-01-30 21:42:39.000000000 +0100 @@ -11,7 +11,9 @@ pyee/_base.py pyee/_compat.py pyee/_executor.py +pyee/_trio.py pyee/_twisted.py +pyee/uplift.py pyee.egg-info/PKG-INFO pyee.egg-info/SOURCES.txt pyee.egg-info/dependency_links.txt @@ -20,4 +22,6 @@ tests/test_async.py tests/test_executor.py tests/test_sync.py -tests/test_twisted.py \ No newline at end of file +tests/test_trio.py +tests/test_twisted.py +tests/test_uplift.py \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/setup.py new/pyee-7.0.1/setup.py --- old/pyee-6.0.0/setup.py 2019-04-13 19:47:29.000000000 +0200 +++ new/pyee-7.0.1/setup.py 2020-01-30 21:16:37.000000000 +0100 @@ -16,9 +16,14 @@ setup_requires=[ 'pytest-runner', 'pytest-asyncio; python_version > "3.4"', + 'pytest-trio; python_version > "3.7"', 'vcversioner' ], - tests_require=['twisted', 'futures; python_version < "3.0"'], + tests_require=[ + 'twisted', + 'trio; python_version > "3.7"', + 'futures; python_version < "3.0"' + ], include_package_data=True, description="A port of node.js's EventEmitter to python.", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/tests/conftest.py new/pyee-7.0.1/tests/conftest.py --- old/pyee-6.0.0/tests/conftest.py 2019-04-06 07:07:14.000000000 +0200 +++ new/pyee-7.0.1/tests/conftest.py 2020-01-30 21:16:37.000000000 +0100 @@ -6,3 +6,6 @@ if not (v[0] >= 3 and v[1] >= 5): collect_ignore.append('test_async.py') + +if not (v[0] >= 3 and v[1] >= 7): + collect_ignore.append('test_trio.py') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/tests/test_async.py new/pyee-7.0.1/tests/test_async.py --- old/pyee-6.0.0/tests/test_async.py 2019-04-13 19:47:29.000000000 +0200 +++ new/pyee-7.0.1/tests/test_async.py 2020-01-30 21:22:49.000000000 +0100 @@ -4,7 +4,9 @@ import pytest_asyncio.plugin # noqa from asyncio import Future, wait_for +from concurrent.futures import TimeoutError from mock import Mock + from twisted.internet.defer import ensureDeferred, succeed from pyee import EventEmitter, AsyncIOEventEmitter, TwistedEventEmitter @@ -93,6 +95,33 @@ @pytest.mark.asyncio +async def test_asyncio_cancellation(event_loop): + """Test that AsyncIOEventEmitter can handle Future cancellations""" + + cancel_me = Future(loop=event_loop) + should_not_call = Future(loop=event_loop) + + ee = AsyncIOEventEmitter(loop=event_loop) + + @ee.on('event') + async def event_handler(): + cancel_me.cancel() + + @ee.on('error') + def handle_error(exc): + should_not_call.set_result(None) + + ee.emit('event') + + try: + await wait_for(should_not_call, 0.1) + except TimeoutError: + pass + else: + raise PyeeTestError() + + [email protected] async def test_sync_error(event_loop): """Test that regular functions have the same error handling as coroutines """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/tests/test_sync.py new/pyee-7.0.1/tests/test_sync.py --- old/pyee-6.0.0/tests/test_sync.py 2019-04-13 19:47:29.000000000 +0200 +++ new/pyee-7.0.1/tests/test_sync.py 2020-01-30 21:23:11.000000000 +0100 @@ -90,7 +90,7 @@ def event_handler(data): pass - call_me.assert_called_once() + call_me.assert_called_once_with('event', event_handler) def test_listener_removal(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/tests/test_trio.py new/pyee-7.0.1/tests/test_trio.py --- old/pyee-6.0.0/tests/test_trio.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyee-7.0.1/tests/test_trio.py 2020-01-30 21:16:37.000000000 +0100 @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- + +import pytest +import pytest_trio.plugin # noqa + +import trio + +from pyee import TrioEventEmitter + + +class PyeeTestError(Exception): + pass + + [email protected] +async def test_trio_emit(): + """Test that the trio event emitter can handle wrapping + coroutines + """ + + async with TrioEventEmitter() as ee: + + should_call = trio.Event() + + @ee.on('event') + async def event_handler(): + should_call.set() + + ee.emit('event') + + result = False + with trio.move_on_after(0.1): + await should_call.wait() + result = True + + assert result + + [email protected] +async def test_trio_once_emit(): + """Test that trio event emitters also wrap coroutines when + using once + """ + + async with TrioEventEmitter() as ee: + should_call = trio.Event() + + @ee.once('event') + async def event_handler(): + should_call.set() + + ee.emit('event') + + result = False + with trio.move_on_after(0.1): + await should_call.wait() + result = True + + assert result + + [email protected] +async def test_trio_error(): + """Test that trio event emitters can handle errors when + wrapping coroutines + """ + + async with TrioEventEmitter() as ee: + send, rcv = trio.open_memory_channel(1) + + @ee.on('event') + async def event_handler(): + raise PyeeTestError() + + @ee.on('error') + async def handle_error(exc): + async with send: + await send.send(exc) + + ee.emit('event') + + result = None + with trio.move_on_after(0.1): + async with rcv: + result = await rcv.__anext__() + + assert isinstance(result, PyeeTestError) + + [email protected] +async def test_sync_error(event_loop): + """Test that regular functions have the same error handling as coroutines + """ + + async with TrioEventEmitter() as ee: + send, rcv = trio.open_memory_channel(1) + + @ee.on('event') + def sync_handler(): + raise PyeeTestError() + + @ee.on('error') + async def handle_error(exc): + async with send: + await send.send(exc) + + ee.emit('event') + + result = None + with trio.move_on_after(0.1): + async with rcv: + result = await rcv.__anext__() + + assert isinstance(result, PyeeTestError) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/tests/test_uplift.py new/pyee-7.0.1/tests/test_uplift.py --- old/pyee-6.0.0/tests/test_uplift.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyee-7.0.1/tests/test_uplift.py 2020-01-30 21:23:11.000000000 +0100 @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- + +import pytest + +from mock import call, Mock +from pyee import BaseEventEmitter +from pyee.uplift import uplift + + +class UpliftedEventEmitter(BaseEventEmitter): + pass + + +def test_uplift_emit(): + call_me = Mock() + + base_ee = BaseEventEmitter() + + @base_ee.on('base_event') + def base_handler(): + call_me('base event on base emitter') + + @base_ee.on('shared_event') + def shared_base_handler(): + call_me('shared event on base emitter') + + uplifted_ee = uplift(UpliftedEventEmitter, base_ee) + + assert isinstance( + uplifted_ee, + UpliftedEventEmitter + ), 'Returns an uplifted emitter' + + @uplifted_ee.on('uplifted_event') + def uplifted_handler(): + call_me('uplifted event on uplifted emitter') + + @uplifted_ee.on('shared_event') + def shared_uplifted_handler(): + call_me('shared event on uplifted emitter') + + # Events on uplifted proxy correctly + assert uplifted_ee.emit('base_event') + assert uplifted_ee.emit('shared_event') + assert uplifted_ee.emit('uplifted_event') + + call_me.assert_has_calls([ + call('base event on base emitter'), + call('shared event on uplifted emitter'), + call('shared event on base emitter'), + call('uplifted event on uplifted emitter') + ]) + + call_me.reset_mock() + + # Events on underlying proxy correctly + assert base_ee.emit('base_event') + assert base_ee.emit('shared_event') + assert base_ee.emit('uplifted_event') + + call_me.assert_has_calls([ + call('base event on base emitter'), + call('shared event on base emitter'), + call('shared event on uplifted emitter'), + call('uplifted event on uplifted emitter') + ]) + + call_me.reset_mock() + + # Quick check for unwrap + uplifted_ee.unwrap() + + with pytest.raises(AttributeError): + getattr(uplifted_ee, 'unwrap') + + with pytest.raises(AttributeError): + getattr(base_ee, 'unwrap') + + assert not uplifted_ee.emit('base_event') + assert uplifted_ee.emit('shared_event') + assert uplifted_ee.emit('uplifted_event') + + assert base_ee.emit('base_event') + assert base_ee.emit('shared_event') + assert not base_ee.emit('uplifted_event') + + call_me.assert_has_calls([ + # No listener for base event on uplifted + call('shared event on uplifted emitter'), + call('uplifted event on uplifted emitter'), + call('base event on base emitter'), + call('shared event on base emitter') + # No listener for uplifted event on uplifted + ]) + + [email protected]('error_handling', [ + 'new', 'underlying', 'neither' +]) +def test_exception_handling(error_handling): + base_ee = BaseEventEmitter() + uplifted_ee = uplift( + UpliftedEventEmitter, base_ee, + error_handling=error_handling + ) + + # Exception handling alwyas prefers uplifted + base_error = Exception('base error') + uplifted_error = Exception('uplifted error') + + # Hold my beer + base_error_handler = Mock() + base_ee._emit_handle_potential_error = base_error_handler + + # Hold my other beer + uplifted_error_handler = Mock() + uplifted_ee._emit_handle_potential_error = uplifted_error_handler + + base_ee.emit('error', base_error) + uplifted_ee.emit('error', uplifted_error) + + if error_handling == 'new': + base_error_handler.assert_not_called() + uplifted_error_handler.assert_has_calls([ + call('error', base_error), + call('error', uplifted_error) + ]) + elif error_handling == 'underlying': + base_error_handler.assert_has_calls([ + call('error', base_error), + call('error', uplifted_error) + ]) + uplifted_error_handler.assert_not_called() + elif error_handling == 'neither': + base_error_handler.assert_called_once_with('error', base_error) + uplifted_error_handler.assert_called_once_with('error', uplifted_error) + else: + raise Exception('unrecognized setting') + + [email protected]('proxy_new_listener', [ + 'both', + 'neither', + 'forward', + 'backward' +]) +def test_proxy_new_listener(proxy_new_listener): + call_me = Mock() + + base_ee = BaseEventEmitter() + + uplifted_ee = uplift( + UpliftedEventEmitter, + base_ee, + proxy_new_listener=proxy_new_listener + ) + + @base_ee.on('new_listener') + def base_new_listener_handler(event, f): + assert event in ('event', 'new_listener') + call_me('base new listener handler', f) + + @uplifted_ee.on('new_listener') + def uplifted_new_listener_handler(event, f): + assert event in ('event', 'new_listener') + call_me('uplifted new listener handler', f) + + def fresh_base_handler(): + pass + + def fresh_uplifted_handler(): + pass + + base_ee.on('event', fresh_base_handler) + uplifted_ee.on('event', fresh_uplifted_handler) + + if proxy_new_listener == 'both': + call_me.assert_has_calls([ + call('base new listener handler', fresh_base_handler), + call('uplifted new listener handler', fresh_base_handler), + call('uplifted new listener handler', fresh_uplifted_handler), + call('base new listener handler', fresh_uplifted_handler) + ]) + elif proxy_new_listener == 'neither': + call_me.assert_has_calls([ + call('base new listener handler', fresh_base_handler), + call('uplifted new listener handler', fresh_uplifted_handler) + ]) + elif proxy_new_listener == 'forward': + call_me.assert_has_calls([ + call('base new listener handler', fresh_base_handler), + call('uplifted new listener handler', fresh_base_handler), + call('uplifted new listener handler', fresh_uplifted_handler) + ]) + elif proxy_new_listener == 'backward': + call_me.assert_has_calls([ + call('base new listener handler', fresh_base_handler), + call('uplifted new listener handler', fresh_uplifted_handler), + call('base new listener handler', fresh_uplifted_handler) + ]) + else: + raise Exception('unrecognized proxy_new_listener') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyee-6.0.0/version.txt new/pyee-7.0.1/version.txt --- old/pyee-6.0.0/version.txt 2019-04-13 19:50:13.000000000 +0200 +++ new/pyee-7.0.1/version.txt 2020-01-30 21:42:39.000000000 +0100 @@ -1 +1 @@ -6.0.0-0-gad0107d \ No newline at end of file +7.0.1-0-g358fb04 \ No newline at end of file ++++++ python-pyee-import-asyncio.exceptions.patch ++++++ Index: pyee-7.0.1/tests/test_async.py =================================================================== --- pyee-7.0.1.orig/tests/test_async.py 2020-01-30 21:22:49.000000000 +0100 +++ pyee-7.0.1/tests/test_async.py 2020-03-27 10:10:34.734403451 +0100 @@ -4,7 +4,7 @@ import pytest import pytest_asyncio.plugin # noqa from asyncio import Future, wait_for -from concurrent.futures import TimeoutError +from asyncio.exceptions import TimeoutError from mock import Mock from twisted.internet.defer import ensureDeferred, succeed
