Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pytest-twisted for openSUSE:Factory checked in at 2024-10-24 15:44:54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pytest-twisted (Old) and /work/SRC/openSUSE:Factory/.python-pytest-twisted.new.2020 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytest-twisted" Thu Oct 24 15:44:54 2024 rev:10 rq:1217995 version:1.14.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pytest-twisted/python-pytest-twisted.changes 2024-08-19 23:44:44.772234474 +0200 +++ /work/SRC/openSUSE:Factory/.python-pytest-twisted.new.2020/python-pytest-twisted.changes 2024-10-24 15:45:41.787408110 +0200 @@ -1,0 +2,7 @@ +Thu Oct 24 09:31:43 UTC 2024 - Dirk Müller <dmuel...@suse.com> + +- update to 1.14.3: + * update for deprecation of `twisted.internet.defer.returnValue + * refactor project layout + +------------------------------------------------------------------- Old: ---- pytest-twisted-1.14.2-gh.tar.gz New: ---- pytest-twisted-1.14.3-gh.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pytest-twisted.spec ++++++ --- /var/tmp/diff_new_pack.ySC6lC/_old 2024-10-24 15:45:42.247427231 +0200 +++ /var/tmp/diff_new_pack.ySC6lC/_new 2024-10-24 15:45:42.247427231 +0200 @@ -17,7 +17,7 @@ Name: python-pytest-twisted -Version: 1.14.2 +Version: 1.14.3 Release: 0 Summary: Pytest Plugin for Twisted License: BSD-2-Clause @@ -63,7 +63,6 @@ %files %{python_files} %license LICENSE %doc README.rst -%{python_sitelib}/pytest_twisted.py -%pycache_only %{python_sitelib}/__pycache__/pytest_twisted*pyc +%{python_sitelib}/pytest_twisted %{python_sitelib}/pytest_twisted-%{version}.dist-info ++++++ pytest-twisted-1.14.2-gh.tar.gz -> pytest-twisted-1.14.3-gh.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-twisted-1.14.2/.flake8 new/pytest-twisted-1.14.3/.flake8 --- old/pytest-twisted-1.14.2/.flake8 1970-01-01 01:00:00.000000000 +0100 +++ new/pytest-twisted-1.14.3/.flake8 2024-08-22 14:04:04.000000000 +0200 @@ -0,0 +1,6 @@ +[flake8] +ignore = + N802 + +per-file-ignores = + src/pytest_twisted/__init__.py: F401 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-twisted-1.14.2/README.rst new/pytest-twisted-1.14.3/README.rst --- old/pytest-twisted-1.14.2/README.rst 2024-07-10 18:54:06.000000000 +0200 +++ new/pytest-twisted-1.14.3/README.rst 2024-08-22 14:04:04.000000000 +0200 @@ -7,8 +7,8 @@ |PyPI| |Pythons| |Travis| |AppVeyor| |Actions| |Black| :Authors: Ralf Schmitt, Kyle Altendorf, Victor Titor -:Version: 1.14.2 -:Date: 2024-07-10 +:Version: 1.14.3 +:Date: 2024-08-21 :Download: https://pypi.python.org/pypi/pytest-twisted#downloads :Code: https://github.com/pytest-dev/pytest-twisted diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-twisted-1.14.2/pytest_twisted.py new/pytest-twisted-1.14.3/pytest_twisted.py --- old/pytest-twisted-1.14.2/pytest_twisted.py 2024-07-10 18:54:06.000000000 +0200 +++ new/pytest-twisted-1.14.3/pytest_twisted.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,535 +0,0 @@ -import functools -import inspect -import itertools -import signal -import sys -import threading -import warnings - -import decorator -import greenlet -import pytest - -from twisted.internet import defer, error -from twisted.internet.threads import blockingCallFromThread -from twisted.python import failure - - -class WrongReactorAlreadyInstalledError(Exception): - pass - - -class UnrecognizedCoroutineMarkError(Exception): - @classmethod - def from_mark(cls, mark): - return cls( - 'Coroutine wrapper mark not recognized: {}'.format(repr(mark)), - ) - - -class AsyncGeneratorFixtureDidNotStopError(Exception): - @classmethod - def from_generator(cls, generator): - return cls( - 'async fixture did not stop: {}'.format(generator), - ) - - -class AsyncFixtureUnsupportedScopeError(Exception): - @classmethod - def from_scope(cls, scope): - return cls( - 'Unsupported scope {0!r} used for async fixture'.format(scope) - ) - - -class _config: - external_reactor = False - - -class _instances: - gr_twisted = None - reactor = None - - -def _deprecate(deprecated, recommended): - def decorator(f): - @functools.wraps(f) - def wrapper(*args, **kwargs): - warnings.warn( - '{deprecated} has been deprecated, use {recommended}'.format( - deprecated=deprecated, - recommended=recommended, - ), - DeprecationWarning, - stacklevel=2, - ) - return f(*args, **kwargs) - - return wrapper - - return decorator - - -def blockon(d): - if _config.external_reactor: - return block_from_thread(d) - - return blockon_default(d) - - -def blockon_default(d): - current = greenlet.getcurrent() - assert ( - current is not _instances.gr_twisted - ), "blockon cannot be called from the twisted greenlet" - result = [] - - def cb(r): - result.append(r) - if greenlet.getcurrent() is not current: - current.switch(result) - - d.addCallbacks(cb, cb) - if not result: - _result = _instances.gr_twisted.switch() - assert _result is result, "illegal switch in blockon" - - if isinstance(result[0], failure.Failure): - result[0].raiseException() - - return result[0] - - -def block_from_thread(d): - return blockingCallFromThread(_instances.reactor, lambda x: x, d) - - -def decorator_apply(dec, func): - """ - Decorate a function by preserving the signature even if dec - is not a signature-preserving decorator. - - https://github.com/micheles/decorator/blob/55a68b5ef1951614c5c37a6d201b1f3b804dbce6/docs/documentation.md#dealing-with-third-party-decorators - """ - return decorator.FunctionMaker.create( - func, 'return decfunc(%(signature)s)', - dict(decfunc=dec(func)), __wrapped__=func) - - -class DecoratorArgumentsError(Exception): - pass - - -def repr_args_kwargs(*args, **kwargs): - arguments = ', '.join(itertools.chain( - (repr(x) for x in args), - ('{}={}'.format(k, repr(v)) for k, v in kwargs.items()) - )) - - return '({})'.format(arguments) - - -def _positional_not_allowed_exception(*args, **kwargs): - arguments = repr_args_kwargs(*args, **kwargs) - - return DecoratorArgumentsError( - 'Positional decorator arguments not allowed: {}'.format(arguments), - ) - - -def _optional_arguments(): - def decorator_decorator(d): - # TODO: this should get the signature of d minus the f or something - def decorator_wrapper(*args, **decorator_arguments): - """this is decorator_wrapper""" - if len(args) > 1: - raise _positional_not_allowed_exception() - - if len(args) == 1: - maybe_f = args[0] - - if len(decorator_arguments) > 0 or not callable(maybe_f): - raise _positional_not_allowed_exception() - - f = maybe_f - return d(f) - - # TODO: this should get the signature of d minus the kwargs - def decorator_closure_on_arguments(f): - return d(f, **decorator_arguments) - - return decorator_closure_on_arguments - - return decorator_wrapper - - return decorator_decorator - - -@_optional_arguments() -def inlineCallbacks(f): - """ - Mark as inline callbacks test for pytest-twisted processing and apply - @inlineCallbacks. - - Unlike @ensureDeferred, @inlineCallbacks can be applied here because it - does not call nor schedule the test function. Further, @inlineCallbacks - must be applied here otherwise pytest identifies the test as a 'yield test' - for which they dropped support in 4.0 and now they skip. - """ - decorated = decorator_apply(defer.inlineCallbacks, f) - _set_mark(o=decorated, mark='inline_callbacks_test') - - return decorated - - -@_optional_arguments() -def ensureDeferred(f): - """ - Mark as async test for pytest-twisted processing. - - Unlike @inlineCallbacks, @ensureDeferred must not be applied here since it - would call and schedule the test function. - """ - _set_mark(o=f, mark='async_test') - - return f - - -def init_twisted_greenlet(): - if _instances.reactor is None or _instances.gr_twisted: - return - - if not _instances.reactor.running: - if not isinstance(threading.current_thread(), threading._MainThread): - warnings.warn( - ( - 'Will not attempt to block Twisted signal configuration' - ' since we are not running in the main thread. See' - ' https://github.com/pytest-dev/pytest-twisted/issues/153.' - ), - RuntimeWarning, - ) - elif signal.getsignal(signal.SIGINT) == signal.default_int_handler: - signal.signal( - signal.SIGINT, - functools.partial(signal.default_int_handler), - ) - _instances.gr_twisted = greenlet.greenlet(_instances.reactor.run) - # give me better tracebacks: - failure.Failure.cleanFailure = lambda self: None - else: - _config.external_reactor = True - - -def stop_twisted_greenlet(): - if _instances.gr_twisted: - try: - _instances.reactor.stop() - except error.ReactorNotRunning: - # Sometimes the reactor is stopped before we get here. For - # example, this can happen in response to a SIGINT in some cases. - pass - _instances.gr_twisted.switch() - - -def _get_mark(o, default=None): - """Get the pytest-twisted test or fixture mark.""" - return getattr(o, _mark_attribute_name, default) - - -def _set_mark(o, mark): - """Set the pytest-twisted test or fixture mark.""" - setattr(o, _mark_attribute_name, mark) - - -def _marked_async_fixture(mark): - @functools.wraps(pytest.fixture) - @_optional_arguments() - def fixture(f, *args, **kwargs): - try: - scope = args[0] - except IndexError: - scope = kwargs.get('scope', 'function') - - if scope not in ['function', 'module']: - # TODO: handle... - # - class - # - package - # - session - # - dynamic - # - # https://docs.pytest.org/en/latest/reference.html#pytest-fixture-api - # then remove this and update docs, or maybe keep it around - # in case new options come in without support? - # - # https://github.com/pytest-dev/pytest-twisted/issues/56 - raise AsyncFixtureUnsupportedScopeError.from_scope(scope=scope) - - _set_mark(f, mark) - result = pytest.fixture(*args, **kwargs)(f) - - return result - - return fixture - - -_mark_attribute_name = '_pytest_twisted_mark' -async_fixture = _marked_async_fixture('async_fixture') -async_yield_fixture = _marked_async_fixture('async_yield_fixture') - - -def pytest_fixture_setup(fixturedef, request): - """Interface pytest to async for async and async yield fixtures.""" - # TODO: what about _adding_ inlineCallbacks fixture support? - maybe_mark = _get_mark(fixturedef.func) - if maybe_mark is None: - return None - - mark = maybe_mark - - _run_inline_callbacks( - _async_pytest_fixture_setup, - fixturedef, - request, - mark, - ) - - return not None - - -def _create_async_yield_fixture_finalizer(coroutine): - def finalizer(): - _run_inline_callbacks( - _tear_it_down, - defer.ensureDeferred(coroutine.__anext__()), - ) - - return finalizer - - -@defer.inlineCallbacks -def _async_pytest_fixture_setup(fixturedef, request, mark): - """Setup an async or async yield fixture.""" - fixture_function = fixturedef.func - - kwargs = { - name: request.getfixturevalue(name) - for name in fixturedef.argnames - } - - if mark == 'async_fixture': - arg_value = yield defer.ensureDeferred( - fixture_function(**kwargs) - ) - elif mark == 'async_yield_fixture': - coroutine = fixture_function(**kwargs) - - request.addfinalizer( - _create_async_yield_fixture_finalizer(coroutine=coroutine), - ) - - arg_value = yield defer.ensureDeferred(coroutine.__anext__()) - else: - raise UnrecognizedCoroutineMarkError.from_mark(mark=mark) - - fixturedef.cached_result = (arg_value, fixturedef.cache_key(request), None) - - defer.returnValue(arg_value) - - -@defer.inlineCallbacks -def _tear_it_down(deferred): - """Tear down a specific async yield fixture.""" - try: - yield deferred - except StopAsyncIteration: - return - - # TODO: six.raise_from() - raise AsyncGeneratorFixtureDidNotStopError.from_generator( - generator=deferred, - ) - - -def _run_inline_callbacks(f, *args): - """Interface into Twisted greenlet to run and wait for a deferred.""" - if _instances.gr_twisted is not None: - if _instances.gr_twisted.dead: - raise RuntimeError("twisted reactor has stopped") - - def in_reactor(d, f, *args): - return defer.maybeDeferred(f, *args).chainDeferred(d) - - d = defer.Deferred() - _instances.reactor.callLater(0.0, in_reactor, d, f, *args) - blockon_default(d) - else: - if not _instances.reactor.running: - raise RuntimeError("twisted reactor is not running") - blockingCallFromThread(_instances.reactor, f, *args) - - -def pytest_pyfunc_call(pyfuncitem): - """Interface to async test call handler.""" - # TODO: only handle 'our' tests? what is the point of handling others? - # well, because our interface allowed people to return deferreds - # from arbitrary tests so we kinda have to keep this up for now - maybe_hypothesis = getattr(pyfuncitem.obj, "hypothesis", None) - if maybe_hypothesis is None: - _run_inline_callbacks( - _async_pytest_pyfunc_call, - pyfuncitem, - pyfuncitem.obj, - {} - ) - result = not None - else: - hypothesis = maybe_hypothesis - f = hypothesis.inner_test - - def inner_test(**kwargs): - return _run_inline_callbacks( - _async_pytest_pyfunc_call, - pyfuncitem, - f, - kwargs, - ) - - pyfuncitem.obj.hypothesis.inner_test = inner_test - result = None - - return result - - -@defer.inlineCallbacks -def _async_pytest_pyfunc_call(pyfuncitem, f, kwargs): - """Run test function.""" - fixture_kwargs = { - name: value - for name, value in pyfuncitem.funcargs.items() - if name in pyfuncitem._fixtureinfo.argnames - } - kwargs.update(fixture_kwargs) - - maybe_mark = _get_mark(f) - if maybe_mark == 'async_test': - result = yield defer.ensureDeferred(f(**kwargs)) - elif maybe_mark == 'inline_callbacks_test': - result = yield f(**kwargs) - else: - # TODO: maybe deprecate this - result = yield f(**kwargs) - - defer.returnValue(result) - - -@pytest.fixture(scope="session", autouse=True) -def twisted_greenlet(): - """Provide the twisted greenlet in fixture form.""" - return _instances.gr_twisted - - -def init_default_reactor(): - """Install the default Twisted reactor.""" - import twisted.internet.default - - module = inspect.getmodule(twisted.internet.default.install) - - module_name = module.__name__.split(".")[-1] - reactor_type_name, = (x for x in dir(module) if x.lower() == module_name) - reactor_type = getattr(module, reactor_type_name) - - _install_reactor( - reactor_installer=twisted.internet.default.install, - reactor_type=reactor_type, - ) - - -def init_qt5_reactor(): - """Install the qt5reactor... reactor.""" - import qt5reactor - - _install_reactor( - reactor_installer=qt5reactor.install, reactor_type=qt5reactor.QtReactor - ) - - -def init_asyncio_reactor(): - """Install the Twisted reactor for asyncio.""" - from twisted.internet import asyncioreactor - - _install_reactor( - reactor_installer=asyncioreactor.install, - reactor_type=asyncioreactor.AsyncioSelectorReactor, - ) - - -reactor_installers = { - "default": init_default_reactor, - "qt5reactor": init_qt5_reactor, - "asyncio": init_asyncio_reactor, -} - - -def _install_reactor(reactor_installer, reactor_type): - """Install the specified reactor and create the greenlet.""" - try: - reactor_installer() - except error.ReactorAlreadyInstalledError: - import twisted.internet.reactor - - if not isinstance(twisted.internet.reactor, reactor_type): - raise WrongReactorAlreadyInstalledError( - "expected {} but found {}".format( - reactor_type, type(twisted.internet.reactor) - ) - ) - - import twisted.internet.reactor - - _instances.reactor = twisted.internet.reactor - init_twisted_greenlet() - - -def pytest_addoption(parser): - """Add options into the pytest CLI.""" - group = parser.getgroup("twisted") - group.addoption( - "--reactor", - default="default", - choices=tuple(reactor_installers.keys()), - ) - - -def pytest_configure(config): - """Identify and install chosen reactor.""" - pytest.inlineCallbacks = _deprecate( - deprecated='pytest.inlineCallbacks', - recommended='pytest_twisted.inlineCallbacks', - )(inlineCallbacks) - pytest.blockon = _deprecate( - deprecated='pytest.blockon', - recommended='pytest_twisted.blockon', - )(blockon) - - reactor_installers[config.getoption("reactor")]() - - -def pytest_unconfigure(config): - """Stop the reactor greenlet.""" - stop_twisted_greenlet() - - -def _use_asyncio_selector_if_required(config): - """Set asyncio selector event loop policy if needed.""" - # https://twistedmatrix.com/trac/ticket/9766 - # https://github.com/pytest-dev/pytest-twisted/issues/80 - - is_asyncio = config.getoption("reactor", "default") == "asyncio" - - if is_asyncio and sys.platform == 'win32' and sys.version_info >= (3, 8): - import asyncio - - selector_policy = asyncio.WindowsSelectorEventLoopPolicy() - asyncio.set_event_loop_policy(selector_policy) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-twisted-1.14.2/setup.py new/pytest-twisted-1.14.3/setup.py --- old/pytest-twisted-1.14.2/setup.py 2024-07-10 18:54:06.000000000 +0200 +++ new/pytest-twisted-1.14.3/setup.py 2024-08-22 14:04:04.000000000 +0200 @@ -1,18 +1,19 @@ -from setuptools import setup +import setuptools with open("README.rst") as f: long_description = f.read() -setup( +setuptools.setup( name="pytest-twisted", - version="1.14.2", + version="1.14.3", description="A twisted plugin for pytest.", long_description=long_description, long_description_content_type="text/x-rst", author="Ralf Schmitt, Kyle Altendorf, Victor Titor", author_email="s...@fstab.net", url="https://github.com/pytest-dev/pytest-twisted", - py_modules=["pytest_twisted"], + packages=setuptools.find_packages('src'), + package_dir={'': 'src'}, python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*', install_requires=["greenlet", "pytest>=2.3", "decorator"], extras_require={ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-twisted-1.14.2/src/pytest_twisted/__init__.py new/pytest-twisted-1.14.3/src/pytest_twisted/__init__.py --- old/pytest-twisted-1.14.2/src/pytest_twisted/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pytest-twisted-1.14.3/src/pytest_twisted/__init__.py 2024-08-22 14:04:04.000000000 +0200 @@ -0,0 +1,491 @@ +import functools +import inspect +import itertools +import signal +import sys +import threading +import warnings + +import decorator +import greenlet +import pytest + +from twisted.internet import defer, error +from twisted.internet.threads import blockingCallFromThread +from twisted.python import failure + +if sys.version_info[0] == 3: + from pytest_twisted.three import ( + _async_pytest_fixture_setup, + _async_pytest_pyfunc_call, + ) +elif sys.version_info[0] == 2: + from pytest_twisted.two import _async_pytest_pyfunc_call + + +class WrongReactorAlreadyInstalledError(Exception): + pass + + +class UnrecognizedCoroutineMarkError(Exception): + @classmethod + def from_mark(cls, mark): + return cls( + 'Coroutine wrapper mark not recognized: {}'.format(repr(mark)), + ) + + +class AsyncGeneratorFixtureDidNotStopError(Exception): + @classmethod + def from_generator(cls, generator): + return cls( + 'async fixture did not stop: {}'.format(generator), + ) + + +class AsyncFixtureUnsupportedScopeError(Exception): + @classmethod + def from_scope(cls, scope): + return cls( + 'Unsupported scope {0!r} used for async fixture'.format(scope) + ) + + +class _config: + external_reactor = False + + +class _instances: + gr_twisted = None + reactor = None + + +def _deprecate(deprecated, recommended): + def decorator(f): + @functools.wraps(f) + def wrapper(*args, **kwargs): + warnings.warn( + '{deprecated} has been deprecated, use {recommended}'.format( + deprecated=deprecated, + recommended=recommended, + ), + DeprecationWarning, + stacklevel=2, + ) + return f(*args, **kwargs) + + return wrapper + + return decorator + + +def blockon(d): + if _config.external_reactor: + return block_from_thread(d) + + return blockon_default(d) + + +def blockon_default(d): + current = greenlet.getcurrent() + assert ( + current is not _instances.gr_twisted + ), "blockon cannot be called from the twisted greenlet" + result = [] + + def cb(r): + result.append(r) + if greenlet.getcurrent() is not current: + current.switch(result) + + d.addCallbacks(cb, cb) + if not result: + _result = _instances.gr_twisted.switch() + assert _result is result, "illegal switch in blockon" + + if isinstance(result[0], failure.Failure): + result[0].raiseException() + + return result[0] + + +def block_from_thread(d): + return blockingCallFromThread(_instances.reactor, lambda x: x, d) + + +def decorator_apply(dec, func): + """ + Decorate a function by preserving the signature even if dec + is not a signature-preserving decorator. + + https://github.com/micheles/decorator/blob/55a68b5ef1951614c5c37a6d201b1f3b804dbce6/docs/documentation.md#dealing-with-third-party-decorators + """ + return decorator.FunctionMaker.create( + func, 'return decfunc(%(signature)s)', + dict(decfunc=dec(func)), __wrapped__=func) + + +class DecoratorArgumentsError(Exception): + pass + + +def repr_args_kwargs(*args, **kwargs): + arguments = ', '.join(itertools.chain( + (repr(x) for x in args), + ('{}={}'.format(k, repr(v)) for k, v in kwargs.items()) + )) + + return '({})'.format(arguments) + + +def _positional_not_allowed_exception(*args, **kwargs): + arguments = repr_args_kwargs(*args, **kwargs) + + return DecoratorArgumentsError( + 'Positional decorator arguments not allowed: {}'.format(arguments), + ) + + +def _optional_arguments(): + def decorator_decorator(d): + # TODO: this should get the signature of d minus the f or something + def decorator_wrapper(*args, **decorator_arguments): + """this is decorator_wrapper""" + if len(args) > 1: + raise _positional_not_allowed_exception() + + if len(args) == 1: + maybe_f = args[0] + + if len(decorator_arguments) > 0 or not callable(maybe_f): + raise _positional_not_allowed_exception() + + f = maybe_f + return d(f) + + # TODO: this should get the signature of d minus the kwargs + def decorator_closure_on_arguments(f): + return d(f, **decorator_arguments) + + return decorator_closure_on_arguments + + return decorator_wrapper + + return decorator_decorator + + +@_optional_arguments() +def inlineCallbacks(f): + """ + Mark as inline callbacks test for pytest-twisted processing and apply + @inlineCallbacks. + + Unlike @ensureDeferred, @inlineCallbacks can be applied here because it + does not call nor schedule the test function. Further, @inlineCallbacks + must be applied here otherwise pytest identifies the test as a 'yield test' + for which they dropped support in 4.0 and now they skip. + """ + decorated = decorator_apply(defer.inlineCallbacks, f) + _set_mark(o=decorated, mark='inline_callbacks_test') + + return decorated + + +@_optional_arguments() +def ensureDeferred(f): + """ + Mark as async test for pytest-twisted processing. + + Unlike @inlineCallbacks, @ensureDeferred must not be applied here since it + would call and schedule the test function. + """ + _set_mark(o=f, mark='async_test') + + return f + + +def init_twisted_greenlet(): + if _instances.reactor is None or _instances.gr_twisted: + return + + if not _instances.reactor.running: + if not isinstance(threading.current_thread(), threading._MainThread): + warnings.warn( + ( + 'Will not attempt to block Twisted signal configuration' + ' since we are not running in the main thread. See' + ' https://github.com/pytest-dev/pytest-twisted/issues/153.' + ), + RuntimeWarning, + ) + elif signal.getsignal(signal.SIGINT) == signal.default_int_handler: + signal.signal( + signal.SIGINT, + functools.partial(signal.default_int_handler), + ) + _instances.gr_twisted = greenlet.greenlet(_instances.reactor.run) + # give me better tracebacks: + failure.Failure.cleanFailure = lambda self: None + else: + _config.external_reactor = True + + +def stop_twisted_greenlet(): + if _instances.gr_twisted: + try: + _instances.reactor.stop() + except error.ReactorNotRunning: + # Sometimes the reactor is stopped before we get here. For + # example, this can happen in response to a SIGINT in some cases. + pass + _instances.gr_twisted.switch() + + +def _get_mark(o, default=None): + """Get the pytest-twisted test or fixture mark.""" + return getattr(o, _mark_attribute_name, default) + + +def _set_mark(o, mark): + """Set the pytest-twisted test or fixture mark.""" + setattr(o, _mark_attribute_name, mark) + + +def _marked_async_fixture(mark): + @functools.wraps(pytest.fixture) + @_optional_arguments() + def fixture(f, *args, **kwargs): + try: + scope = args[0] + except IndexError: + scope = kwargs.get('scope', 'function') + + if scope not in ['function', 'module']: + # TODO: handle... + # - class + # - package + # - session + # - dynamic + # + # https://docs.pytest.org/en/latest/reference.html#pytest-fixture-api + # then remove this and update docs, or maybe keep it around + # in case new options come in without support? + # + # https://github.com/pytest-dev/pytest-twisted/issues/56 + raise AsyncFixtureUnsupportedScopeError.from_scope(scope=scope) + + _set_mark(f, mark) + result = pytest.fixture(*args, **kwargs)(f) + + return result + + return fixture + + +_mark_attribute_name = '_pytest_twisted_mark' +async_fixture = _marked_async_fixture('async_fixture') +async_yield_fixture = _marked_async_fixture('async_yield_fixture') + + +def pytest_fixture_setup(fixturedef, request): + """Interface pytest to async for async and async yield fixtures.""" + # TODO: what about _adding_ inlineCallbacks fixture support? + maybe_mark = _get_mark(fixturedef.func) + if maybe_mark is None: + return None + + mark = maybe_mark + + _run_inline_callbacks( + _async_pytest_fixture_setup, + fixturedef, + request, + mark, + ) + + return not None + + +def _create_async_yield_fixture_finalizer(coroutine): + def finalizer(): + _run_inline_callbacks( + _tear_it_down, + defer.ensureDeferred(coroutine.__anext__()), + ) + + return finalizer + + +@defer.inlineCallbacks +def _tear_it_down(deferred): + """Tear down a specific async yield fixture.""" + try: + yield deferred + except StopAsyncIteration: + return + + # TODO: six.raise_from() + raise AsyncGeneratorFixtureDidNotStopError.from_generator( + generator=deferred, + ) + + +def _run_inline_callbacks(f, *args): + """Interface into Twisted greenlet to run and wait for a deferred.""" + if _instances.gr_twisted is not None: + if _instances.gr_twisted.dead: + raise RuntimeError("twisted reactor has stopped") + + def in_reactor(d, f, *args): + return defer.maybeDeferred(f, *args).chainDeferred(d) + + d = defer.Deferred() + _instances.reactor.callLater(0.0, in_reactor, d, f, *args) + blockon_default(d) + else: + if not _instances.reactor.running: + raise RuntimeError("twisted reactor is not running") + blockingCallFromThread(_instances.reactor, f, *args) + + +def pytest_pyfunc_call(pyfuncitem): + """Interface to async test call handler.""" + # TODO: only handle 'our' tests? what is the point of handling others? + # well, because our interface allowed people to return deferreds + # from arbitrary tests so we kinda have to keep this up for now + maybe_hypothesis = getattr(pyfuncitem.obj, "hypothesis", None) + if maybe_hypothesis is None: + _run_inline_callbacks( + _async_pytest_pyfunc_call, + pyfuncitem, + pyfuncitem.obj, + {} + ) + result = not None + else: + hypothesis = maybe_hypothesis + f = hypothesis.inner_test + + def inner_test(**kwargs): + return _run_inline_callbacks( + _async_pytest_pyfunc_call, + pyfuncitem, + f, + kwargs, + ) + + pyfuncitem.obj.hypothesis.inner_test = inner_test + result = None + + return result + + +@pytest.fixture(scope="session", autouse=True) +def twisted_greenlet(): + """Provide the twisted greenlet in fixture form.""" + return _instances.gr_twisted + + +def init_default_reactor(): + """Install the default Twisted reactor.""" + import twisted.internet.default + + module = inspect.getmodule(twisted.internet.default.install) + + module_name = module.__name__.split(".")[-1] + reactor_type_name, = (x for x in dir(module) if x.lower() == module_name) + reactor_type = getattr(module, reactor_type_name) + + _install_reactor( + reactor_installer=twisted.internet.default.install, + reactor_type=reactor_type, + ) + + +def init_qt5_reactor(): + """Install the qt5reactor... reactor.""" + import qt5reactor + + _install_reactor( + reactor_installer=qt5reactor.install, reactor_type=qt5reactor.QtReactor + ) + + +def init_asyncio_reactor(): + """Install the Twisted reactor for asyncio.""" + from twisted.internet import asyncioreactor + + _install_reactor( + reactor_installer=asyncioreactor.install, + reactor_type=asyncioreactor.AsyncioSelectorReactor, + ) + + +reactor_installers = { + "default": init_default_reactor, + "qt5reactor": init_qt5_reactor, + "asyncio": init_asyncio_reactor, +} + + +def _install_reactor(reactor_installer, reactor_type): + """Install the specified reactor and create the greenlet.""" + try: + reactor_installer() + except error.ReactorAlreadyInstalledError: + import twisted.internet.reactor + + if not isinstance(twisted.internet.reactor, reactor_type): + raise WrongReactorAlreadyInstalledError( + "expected {} but found {}".format( + reactor_type, type(twisted.internet.reactor) + ) + ) + + import twisted.internet.reactor + + _instances.reactor = twisted.internet.reactor + init_twisted_greenlet() + + +def pytest_addoption(parser): + """Add options into the pytest CLI.""" + group = parser.getgroup("twisted") + group.addoption( + "--reactor", + default="default", + choices=tuple(reactor_installers.keys()), + ) + + +def pytest_configure(config): + """Identify and install chosen reactor.""" + pytest.inlineCallbacks = _deprecate( + deprecated='pytest.inlineCallbacks', + recommended='pytest_twisted.inlineCallbacks', + )(inlineCallbacks) + pytest.blockon = _deprecate( + deprecated='pytest.blockon', + recommended='pytest_twisted.blockon', + )(blockon) + + reactor_installers[config.getoption("reactor")]() + + +def pytest_unconfigure(config): + """Stop the reactor greenlet.""" + stop_twisted_greenlet() + + +def _use_asyncio_selector_if_required(config): + """Set asyncio selector event loop policy if needed.""" + # https://twistedmatrix.com/trac/ticket/9766 + # https://github.com/pytest-dev/pytest-twisted/issues/80 + + is_asyncio = config.getoption("reactor", "default") == "asyncio" + + if is_asyncio and sys.platform == 'win32' and sys.version_info >= (3, 8): + import asyncio + + selector_policy = asyncio.WindowsSelectorEventLoopPolicy() + asyncio.set_event_loop_policy(selector_policy) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-twisted-1.14.2/src/pytest_twisted/three.py new/pytest-twisted-1.14.3/src/pytest_twisted/three.py --- old/pytest-twisted-1.14.2/src/pytest_twisted/three.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pytest-twisted-1.14.3/src/pytest_twisted/three.py 2024-08-22 14:04:04.000000000 +0200 @@ -0,0 +1,60 @@ +from twisted.internet import defer + + +@defer.inlineCallbacks +def _async_pytest_fixture_setup(fixturedef, request, mark): + """Setup an async or async yield fixture.""" + from pytest_twisted import ( + UnrecognizedCoroutineMarkError, + _create_async_yield_fixture_finalizer, + ) + + fixture_function = fixturedef.func + + kwargs = { + name: request.getfixturevalue(name) + for name in fixturedef.argnames + } + + if mark == 'async_fixture': + arg_value = yield defer.ensureDeferred( + fixture_function(**kwargs) + ) + elif mark == 'async_yield_fixture': + coroutine = fixture_function(**kwargs) + + request.addfinalizer( + _create_async_yield_fixture_finalizer(coroutine=coroutine), + ) + + arg_value = yield defer.ensureDeferred(coroutine.__anext__()) + else: + raise UnrecognizedCoroutineMarkError.from_mark(mark=mark) + + fixturedef.cached_result = (arg_value, fixturedef.cache_key(request), None) + + return arg_value + + +@defer.inlineCallbacks +def _async_pytest_pyfunc_call(pyfuncitem, f, kwargs): + """Run test function.""" + from pytest_twisted import _get_mark + + fixture_kwargs = { + name: value + for name, value in pyfuncitem.funcargs.items() + if name in pyfuncitem._fixtureinfo.argnames + } + kwargs.update(fixture_kwargs) + + maybe_mark = _get_mark(f) + if maybe_mark == 'async_test': + result = yield defer.ensureDeferred(f(**kwargs)) + elif maybe_mark == 'inline_callbacks_test': + result = yield f(**kwargs) + else: + # TODO: maybe deprecate this + result = yield f(**kwargs) + + return result diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-twisted-1.14.2/src/pytest_twisted/two.py new/pytest-twisted-1.14.3/src/pytest_twisted/two.py --- old/pytest-twisted-1.14.2/src/pytest_twisted/two.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pytest-twisted-1.14.3/src/pytest_twisted/two.py 2024-08-22 14:04:04.000000000 +0200 @@ -0,0 +1,25 @@ +from twisted.internet import defer + + +@defer.inlineCallbacks +def _async_pytest_pyfunc_call(pyfuncitem, f, kwargs): + """Run test function.""" + from pytest_twisted import _get_mark + + fixture_kwargs = { + name: value + for name, value in pyfuncitem.funcargs.items() + if name in pyfuncitem._fixtureinfo.argnames + } + kwargs.update(fixture_kwargs) + + maybe_mark = _get_mark(f) + if maybe_mark == 'async_test': + result = yield defer.ensureDeferred(f(**kwargs)) + elif maybe_mark == 'inline_callbacks_test': + result = yield f(**kwargs) + else: + # TODO: maybe deprecate this + result = yield f(**kwargs) + + defer.returnValue(result) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-twisted-1.14.2/tox.ini new/pytest-twisted-1.14.3/tox.ini --- old/pytest-twisted-1.14.2/tox.ini 2024-07-10 18:54:06.000000000 +0200 +++ new/pytest-twisted-1.14.3/tox.ini 2024-08-22 14:04:04.000000000 +0200 @@ -30,7 +30,4 @@ [testenv:linting] deps=flake8 -commands=flake8 setup.py pytest_twisted.py testing - -[flake8] -ignore=N802 +commands=flake8 setup.py src/pytest_twisted testing