Merge branch 'main' into warn-when-a-mark-is-applied-to-a-fixture
This commit is contained in:
commit
2fd160110c
|
@ -50,6 +50,7 @@ coverage.xml
|
||||||
.project
|
.project
|
||||||
.settings
|
.settings
|
||||||
.vscode
|
.vscode
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
# generated by pip
|
# generated by pip
|
||||||
pip-wheel-metadata/
|
pip-wheel-metadata/
|
||||||
|
|
2
AUTHORS
2
AUTHORS
|
@ -154,6 +154,7 @@ Ian Bicking
|
||||||
Ian Lesperance
|
Ian Lesperance
|
||||||
Ilya Konstantinov
|
Ilya Konstantinov
|
||||||
Ionuț Turturică
|
Ionuț Turturică
|
||||||
|
Itxaso Aizpurua
|
||||||
Iwan Briquemont
|
Iwan Briquemont
|
||||||
Jaap Broekhuizen
|
Jaap Broekhuizen
|
||||||
Jakob van Santen
|
Jakob van Santen
|
||||||
|
@ -356,6 +357,7 @@ Victor Uriarte
|
||||||
Vidar T. Fauske
|
Vidar T. Fauske
|
||||||
Virgil Dupras
|
Virgil Dupras
|
||||||
Vitaly Lashmanov
|
Vitaly Lashmanov
|
||||||
|
Vivaan Verma
|
||||||
Vlad Dragos
|
Vlad Dragos
|
||||||
Vlad Radziuk
|
Vlad Radziuk
|
||||||
Vladyslav Rachek
|
Vladyslav Rachek
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
:class:`~pytest.PytestReturnNotNoneWarning` is now a subclass of :class:`~pytest.PytestRemovedIn8Warning`: the plan is to make returning non-``None`` from tests an error in the future.
|
|
@ -0,0 +1 @@
|
||||||
|
Update information on writing plugins to use ``pyproject.toml`` instead of ``setup.py``.
|
|
@ -0,0 +1 @@
|
||||||
|
Assertion failures with strings in NFC and NFD forms that normalize to the same string now have a dedicated error message detailing the issue, and their utf-8 representation is expresed instead.
|
|
@ -0,0 +1,10 @@
|
||||||
|
The functionality for running tests written for ``nose`` has been officially deprecated.
|
||||||
|
|
||||||
|
This includes:
|
||||||
|
|
||||||
|
* Plain ``setup`` and ``teardown`` functions and methods: this might catch users by surprise, as ``setup()`` and ``teardown()`` are not pytest idioms, but part of the ``nose`` support.
|
||||||
|
* Setup/teardown using the `@with_setup <with-setup-nose>`_ decorator.
|
||||||
|
|
||||||
|
For more details, consult the :ref:`deprecation docs <nose-deprecation>`.
|
||||||
|
|
||||||
|
.. _`with-setup-nose`: https://nose.readthedocs.io/en/latest/testing_tools.html?highlight=with_setup#nose.tools.with_setup
|
|
@ -18,6 +18,113 @@ Deprecated Features
|
||||||
Below is a complete list of all pytest features which are considered deprecated. Using those features will issue
|
Below is a complete list of all pytest features which are considered deprecated. Using those features will issue
|
||||||
:class:`~pytest.PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
|
:class:`~pytest.PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _nose-deprecation:
|
||||||
|
|
||||||
|
Support for tests written for nose
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. deprecated:: 7.2
|
||||||
|
|
||||||
|
Support for running tests written for `nose <https://nose.readthedocs.io/en/latest/>`__ is now deprecated.
|
||||||
|
|
||||||
|
``nose`` has been in maintenance mode-only for years, and maintaining the plugin is not trivial as it spills
|
||||||
|
over the code base (see :issue:`9886` for more details).
|
||||||
|
|
||||||
|
setup/teardown
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
One thing that might catch users by surprise is that plain ``setup`` and ``teardown`` methods are not pytest native,
|
||||||
|
they are in fact part of the ``nose`` support.
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class Test:
|
||||||
|
def setup(self):
|
||||||
|
self.resource = make_resource()
|
||||||
|
|
||||||
|
def teardown(self):
|
||||||
|
self.resource.close()
|
||||||
|
|
||||||
|
def test_foo(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def test_bar(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Native pytest support uses ``setup_method`` and ``teardown_method`` (see :ref:`xunit-method-setup`), so the above should be changed to:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class Test:
|
||||||
|
def setup_method(self):
|
||||||
|
self.resource = make_resource()
|
||||||
|
|
||||||
|
def teardown_method(self):
|
||||||
|
self.resource.close()
|
||||||
|
|
||||||
|
def test_foo(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def test_bar(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
This is easy to do in an entire code base by doing a simple find/replace.
|
||||||
|
|
||||||
|
@with_setup
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
Code using `@with_setup <with-setup-nose>`_ such as this:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from nose.tools import with_setup
|
||||||
|
|
||||||
|
|
||||||
|
def setup_some_resource():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_some_resource():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@with_setup(setup_some_resource, teardown_some_resource)
|
||||||
|
def test_foo():
|
||||||
|
...
|
||||||
|
|
||||||
|
Will also need to be ported to a supported pytest style. One way to do it is using a fixture:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
def setup_some_resource():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_some_resource():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def some_resource():
|
||||||
|
setup_some_resource()
|
||||||
|
yield
|
||||||
|
teardown_some_resource()
|
||||||
|
|
||||||
|
|
||||||
|
def test_foo(some_resource):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
.. _`with-setup-nose`: https://nose.readthedocs.io/en/latest/testing_tools.html?highlight=with_setup#nose.tools.with_setup
|
||||||
|
|
||||||
.. _instance-collector-deprecation:
|
.. _instance-collector-deprecation:
|
||||||
|
|
||||||
The ``pytest.Instance`` collector
|
The ``pytest.Instance`` collector
|
||||||
|
|
|
@ -5,6 +5,9 @@ How to run tests written for nose
|
||||||
|
|
||||||
``pytest`` has basic support for running tests written for nose_.
|
``pytest`` has basic support for running tests written for nose_.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
This functionality has been deprecated and is likely to be removed in ``pytest 8.x``.
|
||||||
|
|
||||||
.. _nosestyle:
|
.. _nosestyle:
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
|
|
|
@ -147,27 +147,33 @@ Making your plugin installable by others
|
||||||
|
|
||||||
If you want to make your plugin externally available, you
|
If you want to make your plugin externally available, you
|
||||||
may define a so-called entry point for your distribution so
|
may define a so-called entry point for your distribution so
|
||||||
that ``pytest`` finds your plugin module. Entry points are
|
that ``pytest`` finds your plugin module. Entry points are
|
||||||
a feature that is provided by :std:doc:`setuptools:index`. pytest looks up
|
a feature that is provided by :std:doc:`setuptools <setuptools:index>`.
|
||||||
the ``pytest11`` entrypoint to discover its
|
|
||||||
plugins and you can thus make your plugin available by defining
|
|
||||||
it in your setuptools-invocation:
|
|
||||||
|
|
||||||
.. sourcecode:: python
|
pytest looks up the ``pytest11`` entrypoint to discover its
|
||||||
|
plugins, thus you can make your plugin available by defining
|
||||||
|
it in your ``pyproject.toml`` file.
|
||||||
|
|
||||||
# sample ./setup.py file
|
.. sourcecode:: toml
|
||||||
from setuptools import setup
|
|
||||||
|
|
||||||
|
# sample ./pyproject.toml file
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
name_of_plugin = "myproject" # register plugin with this name
|
[project]
|
||||||
setup(
|
name = "myproject"
|
||||||
name="myproject",
|
classifiers = [
|
||||||
packages=["myproject"],
|
"Framework :: Pytest",
|
||||||
# the following makes a plugin available to pytest
|
]
|
||||||
entry_points={"pytest11": [f"{name_of_plugin} = myproject.pluginmodule"]},
|
|
||||||
# custom PyPI classifier for pytest plugins
|
[tool.setuptools]
|
||||||
classifiers=["Framework :: Pytest"],
|
packages = ["myproject"]
|
||||||
)
|
|
||||||
|
[project.entry_points]
|
||||||
|
pytest11 = [
|
||||||
|
"myproject = myproject.pluginmodule",
|
||||||
|
]
|
||||||
|
|
||||||
If a package is installed this way, ``pytest`` will load
|
If a package is installed this way, ``pytest`` will load
|
||||||
``myproject.pluginmodule`` as a plugin which can define
|
``myproject.pluginmodule`` as a plugin which can define
|
||||||
|
|
|
@ -63,6 +63,8 @@ and after all test methods of the class are called:
|
||||||
setup_class.
|
setup_class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
.. _xunit-method-setup:
|
||||||
|
|
||||||
Method and function level setup/teardown
|
Method and function level setup/teardown
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1136,6 +1136,9 @@ Custom warnings generated in some situations such as improper usage or deprecate
|
||||||
.. autoclass:: pytest.PytestReturnNotNoneWarning
|
.. autoclass:: pytest.PytestReturnNotNoneWarning
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.PytestRemovedIn8Warning
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
.. autoclass:: pytest.PytestUnhandledCoroutineWarning
|
.. autoclass:: pytest.PytestUnhandledCoroutineWarning
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ class SafeRepr(reprlib.Repr):
|
||||||
information on exceptions raised during the call.
|
information on exceptions raised during the call.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, maxsize: Optional[int]) -> None:
|
def __init__(self, maxsize: Optional[int], use_ascii: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
:param maxsize:
|
:param maxsize:
|
||||||
If not None, will truncate the resulting repr to that specific size, using ellipsis
|
If not None, will truncate the resulting repr to that specific size, using ellipsis
|
||||||
|
@ -54,10 +54,15 @@ class SafeRepr(reprlib.Repr):
|
||||||
# truncation.
|
# truncation.
|
||||||
self.maxstring = maxsize if maxsize is not None else 1_000_000_000
|
self.maxstring = maxsize if maxsize is not None else 1_000_000_000
|
||||||
self.maxsize = maxsize
|
self.maxsize = maxsize
|
||||||
|
self.use_ascii = use_ascii
|
||||||
|
|
||||||
def repr(self, x: object) -> str:
|
def repr(self, x: object) -> str:
|
||||||
try:
|
try:
|
||||||
s = super().repr(x)
|
if self.use_ascii:
|
||||||
|
s = ascii(x)
|
||||||
|
else:
|
||||||
|
s = super().repr(x)
|
||||||
|
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
raise
|
raise
|
||||||
except BaseException as exc:
|
except BaseException as exc:
|
||||||
|
@ -94,7 +99,9 @@ def safeformat(obj: object) -> str:
|
||||||
DEFAULT_REPR_MAX_SIZE = 240
|
DEFAULT_REPR_MAX_SIZE = 240
|
||||||
|
|
||||||
|
|
||||||
def saferepr(obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE) -> str:
|
def saferepr(
|
||||||
|
obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False
|
||||||
|
) -> str:
|
||||||
"""Return a size-limited safe repr-string for the given object.
|
"""Return a size-limited safe repr-string for the given object.
|
||||||
|
|
||||||
Failing __repr__ functions of user instances will be represented
|
Failing __repr__ functions of user instances will be represented
|
||||||
|
@ -104,10 +111,11 @@ def saferepr(obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE) -> str
|
||||||
This function is a wrapper around the Repr/reprlib functionality of the
|
This function is a wrapper around the Repr/reprlib functionality of the
|
||||||
stdlib.
|
stdlib.
|
||||||
"""
|
"""
|
||||||
return SafeRepr(maxsize).repr(obj)
|
|
||||||
|
return SafeRepr(maxsize, use_ascii).repr(obj)
|
||||||
|
|
||||||
|
|
||||||
def saferepr_unlimited(obj: object) -> str:
|
def saferepr_unlimited(obj: object, use_ascii: bool = True) -> str:
|
||||||
"""Return an unlimited-size safe repr-string for the given object.
|
"""Return an unlimited-size safe repr-string for the given object.
|
||||||
|
|
||||||
As with saferepr, failing __repr__ functions of user instances
|
As with saferepr, failing __repr__ functions of user instances
|
||||||
|
@ -119,6 +127,8 @@ def saferepr_unlimited(obj: object) -> str:
|
||||||
when maxsize=None, but that might affect some other code.
|
when maxsize=None, but that might affect some other code.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
if use_ascii:
|
||||||
|
return ascii(obj)
|
||||||
return repr(obj)
|
return repr(obj)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
return _format_repr_exception(exc, obj)
|
return _format_repr_exception(exc, obj)
|
||||||
|
|
|
@ -10,6 +10,7 @@ from typing import List
|
||||||
from typing import Mapping
|
from typing import Mapping
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
from unicodedata import normalize
|
||||||
|
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
from _pytest import outcomes
|
from _pytest import outcomes
|
||||||
|
@ -156,20 +157,32 @@ def has_default_eq(
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def assertrepr_compare(config, op: str, left: Any, right: Any) -> Optional[List[str]]:
|
def assertrepr_compare(
|
||||||
|
config, op: str, left: Any, right: Any, use_ascii: bool = False
|
||||||
|
) -> Optional[List[str]]:
|
||||||
"""Return specialised explanations for some operators/operands."""
|
"""Return specialised explanations for some operators/operands."""
|
||||||
verbose = config.getoption("verbose")
|
verbose = config.getoption("verbose")
|
||||||
|
|
||||||
|
# Strings which normalize equal are often hard to distinguish when printed; use ascii() to make this easier.
|
||||||
|
# See issue #3246.
|
||||||
|
use_ascii = (
|
||||||
|
isinstance(left, str)
|
||||||
|
and isinstance(right, str)
|
||||||
|
and normalize("NFD", left) == normalize("NFD", right)
|
||||||
|
)
|
||||||
|
|
||||||
if verbose > 1:
|
if verbose > 1:
|
||||||
left_repr = saferepr_unlimited(left)
|
left_repr = saferepr_unlimited(left, use_ascii=use_ascii)
|
||||||
right_repr = saferepr_unlimited(right)
|
right_repr = saferepr_unlimited(right, use_ascii=use_ascii)
|
||||||
else:
|
else:
|
||||||
# XXX: "15 chars indentation" is wrong
|
# XXX: "15 chars indentation" is wrong
|
||||||
# ("E AssertionError: assert "); should use term width.
|
# ("E AssertionError: assert "); should use term width.
|
||||||
maxsize = (
|
maxsize = (
|
||||||
80 - 15 - len(op) - 2
|
80 - 15 - len(op) - 2
|
||||||
) // 2 # 15 chars indentation, 1 space around op
|
) // 2 # 15 chars indentation, 1 space around op
|
||||||
left_repr = saferepr(left, maxsize=maxsize)
|
|
||||||
right_repr = saferepr(right, maxsize=maxsize)
|
left_repr = saferepr(left, maxsize=maxsize, use_ascii=use_ascii)
|
||||||
|
right_repr = saferepr(right, maxsize=maxsize, use_ascii=use_ascii)
|
||||||
|
|
||||||
summary = f"{left_repr} {op} {right_repr}"
|
summary = f"{left_repr} {op} {right_repr}"
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,21 @@ DEPRECATED_EXTERNAL_PLUGINS = {
|
||||||
"pytest_faulthandler",
|
"pytest_faulthandler",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NOSE_SUPPORT = UnformattedWarning(
|
||||||
|
PytestRemovedIn8Warning,
|
||||||
|
"Support for nose tests is deprecated and will be removed in a future release.\n"
|
||||||
|
"{nodeid} is using nose method: `{method}` ({stage})\n"
|
||||||
|
"See docs: https://docs.pytest.org/en/stable/deprecations.html#support-for-tests-written-for-nose",
|
||||||
|
)
|
||||||
|
|
||||||
|
NOSE_SUPPORT_METHOD = UnformattedWarning(
|
||||||
|
PytestRemovedIn8Warning,
|
||||||
|
"Support for nose tests is deprecated and will be removed in a future release.\n"
|
||||||
|
"{nodeid} is using nose-specific method: `{method}(self)`\n"
|
||||||
|
"To remove this warning, rename it to `{method}_method(self)`\n"
|
||||||
|
"See docs: https://docs.pytest.org/en/stable/deprecations.html#support-for-tests-written-for-nose",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# This can be* removed pytest 8, but it's harmless and common, so no rush to remove.
|
# This can be* removed pytest 8, but it's harmless and common, so no rush to remove.
|
||||||
# * If you're in the future: "could have been".
|
# * If you're in the future: "could have been".
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
"""Run testsuites written for nose."""
|
"""Run testsuites written for nose."""
|
||||||
|
import warnings
|
||||||
|
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
|
from _pytest.deprecated import NOSE_SUPPORT
|
||||||
from _pytest.fixtures import getfixturemarker
|
from _pytest.fixtures import getfixturemarker
|
||||||
from _pytest.nodes import Item
|
from _pytest.nodes import Item
|
||||||
from _pytest.python import Function
|
from _pytest.python import Function
|
||||||
|
@ -18,8 +21,8 @@ def pytest_runtest_setup(item: Item) -> None:
|
||||||
# see https://github.com/python/mypy/issues/2608
|
# see https://github.com/python/mypy/issues/2608
|
||||||
func = item
|
func = item
|
||||||
|
|
||||||
call_optional(func.obj, "setup")
|
call_optional(func.obj, "setup", func.nodeid)
|
||||||
func.addfinalizer(lambda: call_optional(func.obj, "teardown"))
|
func.addfinalizer(lambda: call_optional(func.obj, "teardown", func.nodeid))
|
||||||
|
|
||||||
# NOTE: Module- and class-level fixtures are handled in python.py
|
# NOTE: Module- and class-level fixtures are handled in python.py
|
||||||
# with `pluginmanager.has_plugin("nose")` checks.
|
# with `pluginmanager.has_plugin("nose")` checks.
|
||||||
|
@ -27,7 +30,7 @@ def pytest_runtest_setup(item: Item) -> None:
|
||||||
# it's not straightforward.
|
# it's not straightforward.
|
||||||
|
|
||||||
|
|
||||||
def call_optional(obj: object, name: str) -> bool:
|
def call_optional(obj: object, name: str, nodeid: str) -> bool:
|
||||||
method = getattr(obj, name, None)
|
method = getattr(obj, name, None)
|
||||||
if method is None:
|
if method is None:
|
||||||
return False
|
return False
|
||||||
|
@ -36,6 +39,11 @@ def call_optional(obj: object, name: str) -> bool:
|
||||||
return False
|
return False
|
||||||
if not callable(method):
|
if not callable(method):
|
||||||
return False
|
return False
|
||||||
|
# Warn about deprecation of this plugin.
|
||||||
|
method_name = getattr(method, "__name__", str(method))
|
||||||
|
warnings.warn(
|
||||||
|
NOSE_SUPPORT.format(nodeid=nodeid, method=method_name, stage=name), stacklevel=2
|
||||||
|
)
|
||||||
# If there are any problems allow the exception to raise rather than
|
# If there are any problems allow the exception to raise rather than
|
||||||
# silently ignoring it.
|
# silently ignoring it.
|
||||||
method()
|
method()
|
||||||
|
|
|
@ -59,6 +59,7 @@ from _pytest.config.argparsing import Parser
|
||||||
from _pytest.deprecated import check_ispytest
|
from _pytest.deprecated import check_ispytest
|
||||||
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
||||||
from _pytest.deprecated import INSTANCE_COLLECTOR
|
from _pytest.deprecated import INSTANCE_COLLECTOR
|
||||||
|
from _pytest.deprecated import NOSE_SUPPORT_METHOD
|
||||||
from _pytest.fixtures import FuncFixtureInfo
|
from _pytest.fixtures import FuncFixtureInfo
|
||||||
from _pytest.main import Session
|
from _pytest.main import Session
|
||||||
from _pytest.mark import MARK_GEN
|
from _pytest.mark import MARK_GEN
|
||||||
|
@ -872,19 +873,23 @@ class Class(PyCollector):
|
||||||
"""Inject a hidden autouse, function scoped fixture into the collected class object
|
"""Inject a hidden autouse, function scoped fixture into the collected class object
|
||||||
that invokes setup_method/teardown_method if either or both are available.
|
that invokes setup_method/teardown_method if either or both are available.
|
||||||
|
|
||||||
Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
|
Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with
|
||||||
other fixtures (#517).
|
other fixtures (#517).
|
||||||
"""
|
"""
|
||||||
has_nose = self.config.pluginmanager.has_plugin("nose")
|
has_nose = self.config.pluginmanager.has_plugin("nose")
|
||||||
setup_name = "setup_method"
|
setup_name = "setup_method"
|
||||||
setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))
|
setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))
|
||||||
|
emit_nose_setup_warning = False
|
||||||
if setup_method is None and has_nose:
|
if setup_method is None and has_nose:
|
||||||
setup_name = "setup"
|
setup_name = "setup"
|
||||||
|
emit_nose_setup_warning = True
|
||||||
setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))
|
setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))
|
||||||
teardown_name = "teardown_method"
|
teardown_name = "teardown_method"
|
||||||
teardown_method = getattr(self.obj, teardown_name, None)
|
teardown_method = getattr(self.obj, teardown_name, None)
|
||||||
|
emit_nose_teardown_warning = False
|
||||||
if teardown_method is None and has_nose:
|
if teardown_method is None and has_nose:
|
||||||
teardown_name = "teardown"
|
teardown_name = "teardown"
|
||||||
|
emit_nose_teardown_warning = True
|
||||||
teardown_method = getattr(self.obj, teardown_name, None)
|
teardown_method = getattr(self.obj, teardown_name, None)
|
||||||
if setup_method is None and teardown_method is None:
|
if setup_method is None and teardown_method is None:
|
||||||
return
|
return
|
||||||
|
@ -900,10 +905,24 @@ class Class(PyCollector):
|
||||||
if setup_method is not None:
|
if setup_method is not None:
|
||||||
func = getattr(self, setup_name)
|
func = getattr(self, setup_name)
|
||||||
_call_with_optional_argument(func, method)
|
_call_with_optional_argument(func, method)
|
||||||
|
if emit_nose_setup_warning:
|
||||||
|
warnings.warn(
|
||||||
|
NOSE_SUPPORT_METHOD.format(
|
||||||
|
nodeid=request.node.nodeid, method="setup"
|
||||||
|
),
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
yield
|
yield
|
||||||
if teardown_method is not None:
|
if teardown_method is not None:
|
||||||
func = getattr(self, teardown_name)
|
func = getattr(self, teardown_name)
|
||||||
_call_with_optional_argument(func, method)
|
_call_with_optional_argument(func, method)
|
||||||
|
if emit_nose_teardown_warning:
|
||||||
|
warnings.warn(
|
||||||
|
NOSE_SUPPORT_METHOD.format(
|
||||||
|
nodeid=request.node.nodeid, method="teardown"
|
||||||
|
),
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
|
||||||
self.obj.__pytest_setup_method = xunit_setup_method_fixture
|
self.obj.__pytest_setup_method = xunit_setup_method_fixture
|
||||||
|
|
||||||
|
|
|
@ -51,14 +51,13 @@ class PytestDeprecationWarning(PytestWarning, DeprecationWarning):
|
||||||
__module__ = "pytest"
|
__module__ = "pytest"
|
||||||
|
|
||||||
|
|
||||||
@final
|
|
||||||
class PytestRemovedIn8Warning(PytestDeprecationWarning):
|
class PytestRemovedIn8Warning(PytestDeprecationWarning):
|
||||||
"""Warning class for features that will be removed in pytest 8."""
|
"""Warning class for features that will be removed in pytest 8."""
|
||||||
|
|
||||||
__module__ = "pytest"
|
__module__ = "pytest"
|
||||||
|
|
||||||
|
|
||||||
class PytestReturnNotNoneWarning(PytestDeprecationWarning):
|
class PytestReturnNotNoneWarning(PytestRemovedIn8Warning):
|
||||||
"""Warning emitted when a test function is returning value other than None."""
|
"""Warning emitted when a test function is returning value other than None."""
|
||||||
|
|
||||||
__module__ = "pytest"
|
__module__ = "pytest"
|
||||||
|
|
|
@ -330,3 +330,62 @@ def test_fixture_disallowed_between_marks():
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
assert len(record) == 2 # one for each mark decorator
|
assert len(record) == 2 # one for each mark decorator
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.filterwarnings("default")
|
||||||
|
def test_nose_deprecated_with_setup(pytester: Pytester) -> None:
|
||||||
|
pytest.importorskip("nose")
|
||||||
|
pytester.makepyfile(
|
||||||
|
"""
|
||||||
|
from nose.tools import with_setup
|
||||||
|
|
||||||
|
def setup_fn_no_op():
|
||||||
|
...
|
||||||
|
|
||||||
|
def teardown_fn_no_op():
|
||||||
|
...
|
||||||
|
|
||||||
|
@with_setup(setup_fn_no_op, teardown_fn_no_op)
|
||||||
|
def test_omits_warnings():
|
||||||
|
...
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
output = pytester.runpytest()
|
||||||
|
message = [
|
||||||
|
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
||||||
|
"*test_nose_deprecated_with_setup.py::test_omits_warnings is using nose method: `setup_fn_no_op` (setup)",
|
||||||
|
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
||||||
|
"*test_nose_deprecated_with_setup.py::test_omits_warnings is using nose method: `teardown_fn_no_op` (teardown)",
|
||||||
|
]
|
||||||
|
output.stdout.fnmatch_lines(message)
|
||||||
|
output.assert_outcomes(passed=1)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.filterwarnings("default")
|
||||||
|
def test_nose_deprecated_setup_teardown(pytester: Pytester) -> None:
|
||||||
|
pytest.importorskip("nose")
|
||||||
|
pytester.makepyfile(
|
||||||
|
"""
|
||||||
|
class Test:
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def teardown(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
...
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
output = pytester.runpytest()
|
||||||
|
message = [
|
||||||
|
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
||||||
|
"*test_nose_deprecated_setup_teardown.py::Test::test is using nose-specific method: `setup(self)`",
|
||||||
|
"*To remove this warning, rename it to `setup_method(self)`",
|
||||||
|
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
||||||
|
"*test_nose_deprecated_setup_teardown.py::Test::test is using nose-specific method: `teardown(self)`",
|
||||||
|
"*To remove this warning, rename it to `teardown_method(self)`",
|
||||||
|
]
|
||||||
|
output.stdout.fnmatch_lines(message)
|
||||||
|
output.assert_outcomes(passed=1)
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
anyio[curio,trio]==3.6.1
|
anyio[curio,trio]==3.6.1
|
||||||
django==4.1.1
|
django==4.1.2
|
||||||
pytest-asyncio==0.19.0
|
pytest-asyncio==0.19.0
|
||||||
pytest-bdd==6.0.1
|
pytest-bdd==6.0.1
|
||||||
pytest-cov==4.0.0
|
pytest-cov==4.0.0
|
||||||
pytest-django==4.5.2
|
pytest-django==4.5.2
|
||||||
pytest-flakes==4.0.5
|
pytest-flakes==4.0.5
|
||||||
pytest-html==3.1.1
|
pytest-html==3.1.1
|
||||||
pytest-mock==3.9.0
|
pytest-mock==3.10.0
|
||||||
pytest-rerunfailures==10.2
|
pytest-rerunfailures==10.2
|
||||||
pytest-sugar==0.9.5
|
pytest-sugar==0.9.5
|
||||||
pytest-trio==0.7.0
|
pytest-trio==0.7.0
|
||||||
|
|
|
@ -776,6 +776,24 @@ class TestAssert_reprcompare:
|
||||||
msg = "\n".join(expl)
|
msg = "\n".join(expl)
|
||||||
assert msg
|
assert msg
|
||||||
|
|
||||||
|
def test_nfc_nfd_same_string(self) -> None:
|
||||||
|
# issue 3426
|
||||||
|
left = "hyv\xe4"
|
||||||
|
right = "hyva\u0308"
|
||||||
|
expl = callequal(left, right)
|
||||||
|
assert expl == [
|
||||||
|
r"'hyv\xe4' == 'hyva\u0308'",
|
||||||
|
f"- {str(right)}",
|
||||||
|
f"+ {str(left)}",
|
||||||
|
]
|
||||||
|
|
||||||
|
expl = callequal(left, right, verbose=2)
|
||||||
|
assert expl == [
|
||||||
|
r"'hyv\xe4' == 'hyva\u0308'",
|
||||||
|
f"- {str(right)}",
|
||||||
|
f"+ {str(left)}",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class TestAssert_reprcompare_dataclass:
|
class TestAssert_reprcompare_dataclass:
|
||||||
def test_dataclasses(self, pytester: Pytester) -> None:
|
def test_dataclasses(self, pytester: Pytester) -> None:
|
||||||
|
|
|
@ -37,7 +37,7 @@ def test_setup_func_with_setup_decorator() -> None:
|
||||||
def f(self):
|
def f(self):
|
||||||
values.append(1)
|
values.append(1)
|
||||||
|
|
||||||
call_optional(A(), "f")
|
call_optional(A(), "f", "A.f")
|
||||||
assert not values
|
assert not values
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ def test_setup_func_not_callable() -> None:
|
||||||
class A:
|
class A:
|
||||||
f = 1
|
f = 1
|
||||||
|
|
||||||
call_optional(A(), "f")
|
call_optional(A(), "f", "A.f")
|
||||||
|
|
||||||
|
|
||||||
def test_nose_setup_func(pytester: Pytester) -> None:
|
def test_nose_setup_func(pytester: Pytester) -> None:
|
||||||
|
|
Loading…
Reference in New Issue