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 <[email protected]>
+
+- 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
-
-
[email protected]
-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)
-
-
[email protected]
-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
-
-
[email protected]
-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)
-
-
[email protected](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="[email protected]",
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
+
+
[email protected]
+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
+
+
[email protected](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
+
+
[email protected]
+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
+
+
[email protected]
+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
+
+
[email protected]
+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