Merge pull request #7660 from nicoddemus/deprecated-features
This commit is contained in:
commit
d69abff2c7
|
@ -0,0 +1,18 @@
|
||||||
|
As per our policy, the following features have been deprecated in the 5.X series and are now
|
||||||
|
removed:
|
||||||
|
|
||||||
|
* The ``funcargnames`` read-only property of ``FixtureRequest``, ``Metafunc``, and ``Function`` classes. Use ``fixturenames`` attribute.
|
||||||
|
|
||||||
|
* ``@pytest.fixture`` no longer supports positional arguments, pass all arguments by keyword instead.
|
||||||
|
|
||||||
|
* Direct construction of ``Node`` subclasses now raise an error, use ``from_parent`` instead.
|
||||||
|
|
||||||
|
* The default value for ``junit_family`` has changed to ``xunit2``. If you require the old format, add ``junit_family=xunit1`` to your configuration file.
|
||||||
|
|
||||||
|
* The ``TerminalReporter`` no longer has a ``writer`` attribute. Plugin authors may use the public functions of the ``TerminalReporter`` instead of accessing the ``TerminalWriter`` object directly.
|
||||||
|
|
||||||
|
* The ``--result-log`` option has been removed. Users are recommended to use the `pytest-reportlog <https://github.com/pytest-dev/pytest-reportlog>`__ plugin instead.
|
||||||
|
|
||||||
|
|
||||||
|
For more information consult
|
||||||
|
`Deprecations and Removals <https://docs.pytest.org/en/stable/deprecations.html>`__ in the docs.
|
|
@ -30,11 +30,19 @@ This hook has an `item` parameter which cannot be serialized by ``pytest-xdist``
|
||||||
Use the ``pytest_warning_recored`` hook instead, which replaces the ``item`` parameter
|
Use the ``pytest_warning_recored`` hook instead, which replaces the ``item`` parameter
|
||||||
by a ``nodeid`` parameter.
|
by a ``nodeid`` parameter.
|
||||||
|
|
||||||
|
The ``pytest.collect`` module
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. deprecated:: 6.0
|
||||||
|
|
||||||
|
The ``pytest.collect`` module is no longer part of the public API, all its names
|
||||||
|
should now be imported from ``pytest`` directly instead.
|
||||||
|
|
||||||
|
|
||||||
The ``pytest._fillfuncargs`` function
|
The ``pytest._fillfuncargs`` function
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. deprecated:: 5.5
|
.. deprecated:: 6.0
|
||||||
|
|
||||||
This function was kept for backward compatibility with an older plugin.
|
This function was kept for backward compatibility with an older plugin.
|
||||||
|
|
||||||
|
@ -43,6 +51,11 @@ it, use `function._request._fillfixtures()` instead, though note this is not
|
||||||
a public API and may break in the future.
|
a public API and may break in the future.
|
||||||
|
|
||||||
|
|
||||||
|
Removed Features
|
||||||
|
----------------
|
||||||
|
|
||||||
|
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
||||||
|
an appropriate period of deprecation has passed.
|
||||||
|
|
||||||
``--no-print-logs`` command-line option
|
``--no-print-logs`` command-line option
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -59,38 +72,46 @@ display captured output when tests fail: ``no``, ``stdout``, ``stderr``, ``log``
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Node Construction changed to ``Node.from_parent``
|
Result log (``--result-log``)
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. deprecated:: 5.4
|
.. deprecated:: 4.0
|
||||||
|
.. versionremoved:: 6.0
|
||||||
|
|
||||||
The construction of nodes now should use the named constructor ``from_parent``.
|
The ``--result-log`` option produces a stream of test reports which can be
|
||||||
This limitation in api surface intends to enable better/simpler refactoring of the collection tree.
|
analysed at runtime, but it uses a custom format which requires users to implement their own
|
||||||
|
parser.
|
||||||
|
|
||||||
This means that instead of :code:`MyItem(name="foo", parent=collector, obj=42)`
|
The `pytest-reportlog <https://github.com/pytest-dev/pytest-reportlog>`__ plugin provides a ``--report-log`` option, a more standard and extensible alternative, producing
|
||||||
one now has to invoke :code:`MyItem.from_parent(collector, name="foo")`.
|
one JSON object per-line, and should cover the same use cases. Please try it out and provide feedback.
|
||||||
|
|
||||||
Plugins that wish to support older versions of pytest and suppress the warning can use
|
The ``pytest-reportlog`` plugin might even be merged into the core
|
||||||
`hasattr` to check if `from_parent` exists in that version:
|
at some point, depending on the plans for the plugins and number of users using it.
|
||||||
|
|
||||||
.. code-block:: python
|
``pytest_collect_directory`` hook
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
def pytest_pycollect_makeitem(collector, name, obj):
|
.. versionremoved:: 6.0
|
||||||
if hasattr(MyItem, "from_parent"):
|
|
||||||
item = MyItem.from_parent(collector, name="foo")
|
|
||||||
item.obj = 42
|
|
||||||
return item
|
|
||||||
else:
|
|
||||||
return MyItem(name="foo", parent=collector, obj=42)
|
|
||||||
|
|
||||||
Note that ``from_parent`` should only be called with keyword arguments for the parameters.
|
The ``pytest_collect_directory`` has not worked properly for years (it was called
|
||||||
|
but the results were ignored). Users may consider using :func:`pytest_collection_modifyitems <_pytest.hookspec.pytest_collection_modifyitems>` instead.
|
||||||
|
|
||||||
|
TerminalReporter.writer
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionremoved:: 6.0
|
||||||
|
|
||||||
|
The ``TerminalReporter.writer`` attribute has been deprecated and should no longer be used. This
|
||||||
|
was inadvertently exposed as part of the public API of that plugin and ties it too much
|
||||||
|
with ``py.io.TerminalWriter``.
|
||||||
|
|
||||||
|
Plugins that used ``TerminalReporter.writer`` directly should instead use ``TerminalReporter``
|
||||||
|
methods that provide the same functionality.
|
||||||
|
|
||||||
``junit_family`` default value change to "xunit2"
|
``junit_family`` default value change to "xunit2"
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. deprecated:: 5.2
|
.. versionchanged:: 6.0
|
||||||
|
|
||||||
The default value of ``junit_family`` option will change to ``xunit2`` in pytest 6.0, which
|
The default value of ``junit_family`` option will change to ``xunit2`` in pytest 6.0, which
|
||||||
is an update of the old ``xunit1`` format and is supported by default in modern tools
|
is an update of the old ``xunit1`` format and is supported by default in modern tools
|
||||||
|
@ -126,11 +147,44 @@ Services known to support the ``xunit2`` format:
|
||||||
* `Jenkins <https://www.jenkins.io/>`__ with the `JUnit <https://plugins.jenkins.io/junit>`__ plugin.
|
* `Jenkins <https://www.jenkins.io/>`__ with the `JUnit <https://plugins.jenkins.io/junit>`__ plugin.
|
||||||
* `Azure Pipelines <https://azure.microsoft.com/en-us/services/devops/pipelines>`__.
|
* `Azure Pipelines <https://azure.microsoft.com/en-us/services/devops/pipelines>`__.
|
||||||
|
|
||||||
|
Node Construction changed to ``Node.from_parent``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionchanged:: 6.0
|
||||||
|
|
||||||
|
The construction of nodes now should use the named constructor ``from_parent``.
|
||||||
|
This limitation in api surface intends to enable better/simpler refactoring of the collection tree.
|
||||||
|
|
||||||
|
This means that instead of :code:`MyItem(name="foo", parent=collector, obj=42)`
|
||||||
|
one now has to invoke :code:`MyItem.from_parent(collector, name="foo")`.
|
||||||
|
|
||||||
|
Plugins that wish to support older versions of pytest and suppress the warning can use
|
||||||
|
`hasattr` to check if `from_parent` exists in that version:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def pytest_pycollect_makeitem(collector, name, obj):
|
||||||
|
if hasattr(MyItem, "from_parent"):
|
||||||
|
item = MyItem.from_parent(collector, name="foo")
|
||||||
|
item.obj = 42
|
||||||
|
return item
|
||||||
|
else:
|
||||||
|
return MyItem(name="foo", parent=collector, obj=42)
|
||||||
|
|
||||||
|
Note that ``from_parent`` should only be called with keyword arguments for the parameters.
|
||||||
|
|
||||||
|
|
||||||
|
``pytest.fixture`` arguments are keyword only
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionremoved:: 6.0
|
||||||
|
|
||||||
|
Passing arguments to pytest.fixture() as positional arguments has been removed - pass them by keyword instead.
|
||||||
|
|
||||||
``funcargnames`` alias for ``fixturenames``
|
``funcargnames`` alias for ``fixturenames``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. deprecated:: 5.0
|
.. versionremoved:: 6.0
|
||||||
|
|
||||||
The ``FixtureRequest``, ``Metafunc``, and ``Function`` classes track the names of
|
The ``FixtureRequest``, ``Metafunc``, and ``Function`` classes track the names of
|
||||||
their associated fixtures, with the aptly-named ``fixturenames`` attribute.
|
their associated fixtures, with the aptly-named ``fixturenames`` attribute.
|
||||||
|
@ -141,42 +195,6 @@ in places where we or plugin authors must distinguish between fixture names and
|
||||||
names supplied by non-fixture things such as ``pytest.mark.parametrize``.
|
names supplied by non-fixture things such as ``pytest.mark.parametrize``.
|
||||||
|
|
||||||
|
|
||||||
Result log (``--result-log``)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. deprecated:: 4.0
|
|
||||||
|
|
||||||
The ``--result-log`` option produces a stream of test reports which can be
|
|
||||||
analysed at runtime, but it uses a custom format which requires users to implement their own
|
|
||||||
parser.
|
|
||||||
|
|
||||||
The `pytest-reportlog <https://github.com/pytest-dev/pytest-reportlog>`__ plugin provides a ``--report-log`` option, a more standard and extensible alternative, producing
|
|
||||||
one JSON object per-line, and should cover the same use cases. Please try it out and provide feedback.
|
|
||||||
|
|
||||||
The plan is remove the ``--result-log`` option in pytest 6.0 if ``pytest-reportlog`` proves satisfactory
|
|
||||||
to all users and is deemed stable. The ``pytest-reportlog`` plugin might even be merged into the core
|
|
||||||
at some point, depending on the plans for the plugins and number of users using it.
|
|
||||||
|
|
||||||
TerminalReporter.writer
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. deprecated:: 5.4
|
|
||||||
|
|
||||||
The ``TerminalReporter.writer`` attribute has been deprecated and should no longer be used. This
|
|
||||||
was inadvertently exposed as part of the public API of that plugin and ties it too much
|
|
||||||
with ``py.io.TerminalWriter``.
|
|
||||||
|
|
||||||
Plugins that used ``TerminalReporter.writer`` directly should instead use ``TerminalReporter``
|
|
||||||
methods that provide the same functionality.
|
|
||||||
|
|
||||||
|
|
||||||
Removed Features
|
|
||||||
----------------
|
|
||||||
|
|
||||||
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
|
||||||
an appropriate period of deprecation has passed.
|
|
||||||
|
|
||||||
|
|
||||||
``pytest.config`` global
|
``pytest.config`` global
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -656,7 +656,6 @@ Collection hooks
|
||||||
|
|
||||||
.. autofunction:: pytest_collection
|
.. autofunction:: pytest_collection
|
||||||
.. autofunction:: pytest_ignore_collect
|
.. autofunction:: pytest_ignore_collect
|
||||||
.. autofunction:: pytest_collect_directory
|
|
||||||
.. autofunction:: pytest_collect_file
|
.. autofunction:: pytest_collect_file
|
||||||
.. autofunction:: pytest_pycollect_makemodule
|
.. autofunction:: pytest_pycollect_makemodule
|
||||||
|
|
||||||
|
|
|
@ -239,7 +239,6 @@ default_plugins = essential_plugins + (
|
||||||
"nose",
|
"nose",
|
||||||
"assertion",
|
"assertion",
|
||||||
"junitxml",
|
"junitxml",
|
||||||
"resultlog",
|
|
||||||
"doctest",
|
"doctest",
|
||||||
"cacheprovider",
|
"cacheprovider",
|
||||||
"freeze_support",
|
"freeze_support",
|
||||||
|
|
|
@ -19,45 +19,12 @@ DEPRECATED_EXTERNAL_PLUGINS = {
|
||||||
"pytest_faulthandler",
|
"pytest_faulthandler",
|
||||||
}
|
}
|
||||||
|
|
||||||
FUNCARGNAMES = PytestDeprecationWarning(
|
|
||||||
"The `funcargnames` attribute was an alias for `fixturenames`, "
|
|
||||||
"since pytest 2.3 - use the newer attribute instead."
|
|
||||||
)
|
|
||||||
|
|
||||||
FILLFUNCARGS = PytestDeprecationWarning(
|
FILLFUNCARGS = PytestDeprecationWarning(
|
||||||
"The `_fillfuncargs` function is deprecated, use "
|
"The `_fillfuncargs` function is deprecated, use "
|
||||||
"function._request._fillfixtures() instead if you cannot avoid reaching into internals."
|
"function._request._fillfixtures() instead if you cannot avoid reaching into internals."
|
||||||
)
|
)
|
||||||
|
|
||||||
RESULT_LOG = PytestDeprecationWarning(
|
|
||||||
"--result-log is deprecated, please try the new pytest-reportlog plugin.\n"
|
|
||||||
"See https://docs.pytest.org/en/stable/deprecations.html#result-log-result-log for more information."
|
|
||||||
)
|
|
||||||
|
|
||||||
FIXTURE_POSITIONAL_ARGUMENTS = PytestDeprecationWarning(
|
|
||||||
"Passing arguments to pytest.fixture() as positional arguments is deprecated - pass them "
|
|
||||||
"as a keyword argument instead."
|
|
||||||
)
|
|
||||||
|
|
||||||
NODE_USE_FROM_PARENT = UnformattedWarning(
|
|
||||||
PytestDeprecationWarning,
|
|
||||||
"Direct construction of {name} has been deprecated, please use {name}.from_parent.\n"
|
|
||||||
"See "
|
|
||||||
"https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent"
|
|
||||||
" for more details.",
|
|
||||||
)
|
|
||||||
|
|
||||||
JUNIT_XML_DEFAULT_FAMILY = PytestDeprecationWarning(
|
|
||||||
"The 'junit_family' default value will change to 'xunit2' in pytest 6.0. See:\n"
|
|
||||||
" https://docs.pytest.org/en/stable/deprecations.html#junit-family-default-value-change-to-xunit2\n"
|
|
||||||
"for more information."
|
|
||||||
)
|
|
||||||
|
|
||||||
COLLECT_DIRECTORY_HOOK = PytestDeprecationWarning(
|
|
||||||
"The pytest_collect_directory hook is not working.\n"
|
|
||||||
"Please use collect_ignore in conftests or pytest_collection_modifyitems."
|
|
||||||
)
|
|
||||||
|
|
||||||
PYTEST_COLLECT_MODULE = UnformattedWarning(
|
PYTEST_COLLECT_MODULE = UnformattedWarning(
|
||||||
PytestDeprecationWarning,
|
PytestDeprecationWarning,
|
||||||
"pytest.collect.{name} was moved to pytest.{name}\n"
|
"pytest.collect.{name} was moved to pytest.{name}\n"
|
||||||
|
@ -65,12 +32,6 @@ PYTEST_COLLECT_MODULE = UnformattedWarning(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
TERMINALWRITER_WRITER = PytestDeprecationWarning(
|
|
||||||
"The TerminalReporter.writer attribute is deprecated, use TerminalReporter._tw instead at your own risk.\n"
|
|
||||||
"See https://docs.pytest.org/en/stable/deprecations.html#terminalreporter-writer for more information."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
MINUS_K_DASH = PytestDeprecationWarning(
|
MINUS_K_DASH = PytestDeprecationWarning(
|
||||||
"The `-k '-expr'` syntax to -k is deprecated.\nUse `-k 'not expr'` instead."
|
"The `-k '-expr'` syntax to -k is deprecated.\nUse `-k 'not expr'` instead."
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,6 @@ import functools
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
|
@ -46,8 +45,6 @@ from _pytest.compat import TYPE_CHECKING
|
||||||
from _pytest.config import _PluggyPlugin
|
from _pytest.config import _PluggyPlugin
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
|
|
||||||
from _pytest.deprecated import FUNCARGNAMES
|
|
||||||
from _pytest.mark import ParameterSet
|
from _pytest.mark import ParameterSet
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
from _pytest.outcomes import TEST_OUTCOME
|
from _pytest.outcomes import TEST_OUTCOME
|
||||||
|
@ -457,12 +454,6 @@ class FixtureRequest:
|
||||||
result.extend(set(self._fixture_defs).difference(result))
|
result.extend(set(self._fixture_defs).difference(result))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@property
|
|
||||||
def funcargnames(self) -> List[str]:
|
|
||||||
"""Alias attribute for ``fixturenames`` for pre-2.3 compatibility."""
|
|
||||||
warnings.warn(FUNCARGNAMES, stacklevel=2)
|
|
||||||
return self.fixturenames
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def node(self):
|
def node(self):
|
||||||
"""Underlying collection node (depends on current request scope)."""
|
"""Underlying collection node (depends on current request scope)."""
|
||||||
|
@ -1253,7 +1244,7 @@ def fixture( # noqa: F811
|
||||||
|
|
||||||
def fixture( # noqa: F811
|
def fixture( # noqa: F811
|
||||||
fixture_function: Optional[_FixtureFunction] = None,
|
fixture_function: Optional[_FixtureFunction] = None,
|
||||||
*args: Any,
|
*,
|
||||||
scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function",
|
scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function",
|
||||||
params: Optional[Iterable[object]] = None,
|
params: Optional[Iterable[object]] = None,
|
||||||
autouse: bool = False,
|
autouse: bool = False,
|
||||||
|
@ -1315,53 +1306,6 @@ def fixture( # noqa: F811
|
||||||
name the decorated function ``fixture_<fixturename>`` and then use
|
name the decorated function ``fixture_<fixturename>`` and then use
|
||||||
``@pytest.fixture(name='<fixturename>')``.
|
``@pytest.fixture(name='<fixturename>')``.
|
||||||
"""
|
"""
|
||||||
# Positional arguments backward compatibility.
|
|
||||||
# If a kwarg is equal to its default, assume it was not explicitly
|
|
||||||
# passed, i.e. not duplicated. The more correct way is to use a
|
|
||||||
# **kwargs and check `in`, but that obfuscates the function signature.
|
|
||||||
if isinstance(fixture_function, str):
|
|
||||||
# It's actually the first positional argument, scope.
|
|
||||||
args = (fixture_function, *args) # type: ignore[unreachable]
|
|
||||||
fixture_function = None
|
|
||||||
duplicated_args = []
|
|
||||||
if len(args) > 0:
|
|
||||||
if scope == "function":
|
|
||||||
scope = args[0]
|
|
||||||
else:
|
|
||||||
duplicated_args.append("scope")
|
|
||||||
if len(args) > 1:
|
|
||||||
if params is None:
|
|
||||||
params = args[1]
|
|
||||||
else:
|
|
||||||
duplicated_args.append("params")
|
|
||||||
if len(args) > 2:
|
|
||||||
if autouse is False:
|
|
||||||
autouse = args[2]
|
|
||||||
else:
|
|
||||||
duplicated_args.append("autouse")
|
|
||||||
if len(args) > 3:
|
|
||||||
if ids is None:
|
|
||||||
ids = args[3]
|
|
||||||
else:
|
|
||||||
duplicated_args.append("ids")
|
|
||||||
if len(args) > 4:
|
|
||||||
if name is None:
|
|
||||||
name = args[4]
|
|
||||||
else:
|
|
||||||
duplicated_args.append("name")
|
|
||||||
if len(args) > 5:
|
|
||||||
raise TypeError(
|
|
||||||
"fixture() takes 5 positional arguments but {} were given".format(len(args))
|
|
||||||
)
|
|
||||||
if duplicated_args:
|
|
||||||
raise TypeError(
|
|
||||||
"The fixture arguments are defined as positional and keyword: {}. "
|
|
||||||
"Use only keyword arguments.".format(", ".join(duplicated_args))
|
|
||||||
)
|
|
||||||
if args:
|
|
||||||
warnings.warn(FIXTURE_POSITIONAL_ARGUMENTS, stacklevel=2)
|
|
||||||
# End backward compatiblity.
|
|
||||||
|
|
||||||
fixture_marker = FixtureFunctionMarker(
|
fixture_marker = FixtureFunctionMarker(
|
||||||
scope=scope, params=params, autouse=autouse, ids=ids, name=name,
|
scope=scope, params=params, autouse=autouse, ids=ids, name=name,
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,7 +12,6 @@ from typing import Union
|
||||||
import py.path
|
import py.path
|
||||||
from pluggy import HookspecMarker
|
from pluggy import HookspecMarker
|
||||||
|
|
||||||
from .deprecated import COLLECT_DIRECTORY_HOOK
|
|
||||||
from _pytest.compat import TYPE_CHECKING
|
from _pytest.compat import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -262,16 +261,6 @@ def pytest_ignore_collect(path: py.path.local, config: "Config") -> Optional[boo
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@hookspec(firstresult=True, warn_on_impl=COLLECT_DIRECTORY_HOOK)
|
|
||||||
def pytest_collect_directory(path: py.path.local, parent) -> Optional[object]:
|
|
||||||
"""Called before traversing a directory for collection files.
|
|
||||||
|
|
||||||
Stops at first non-None result, see :ref:`firstresult`.
|
|
||||||
|
|
||||||
:param py.path.local path: The path to analyze.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_collect_file(path: py.path.local, parent) -> "Optional[Collector]":
|
def pytest_collect_file(path: py.path.local, parent) -> "Optional[Collector]":
|
||||||
"""Return collection Node or None for the given path.
|
"""Return collection Node or None for the given path.
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ from typing import Tuple
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest import deprecated
|
|
||||||
from _pytest import nodes
|
from _pytest import nodes
|
||||||
from _pytest import timing
|
from _pytest import timing
|
||||||
from _pytest._code.code import ExceptionRepr
|
from _pytest._code.code import ExceptionRepr
|
||||||
|
@ -33,7 +32,6 @@ from _pytest.fixtures import FixtureRequest
|
||||||
from _pytest.reports import TestReport
|
from _pytest.reports import TestReport
|
||||||
from _pytest.store import StoreKey
|
from _pytest.store import StoreKey
|
||||||
from _pytest.terminal import TerminalReporter
|
from _pytest.terminal import TerminalReporter
|
||||||
from _pytest.warnings import _issue_warning_captured
|
|
||||||
|
|
||||||
|
|
||||||
xml_key = StoreKey["LogXML"]()
|
xml_key = StoreKey["LogXML"]()
|
||||||
|
@ -413,7 +411,9 @@ def pytest_addoption(parser: Parser) -> None:
|
||||||
default="total",
|
default="total",
|
||||||
) # choices=['total', 'call'])
|
) # choices=['total', 'call'])
|
||||||
parser.addini(
|
parser.addini(
|
||||||
"junit_family", "Emit XML for schema: one of legacy|xunit1|xunit2", default=None
|
"junit_family",
|
||||||
|
"Emit XML for schema: one of legacy|xunit1|xunit2",
|
||||||
|
default="xunit2",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -422,9 +422,6 @@ def pytest_configure(config: Config) -> None:
|
||||||
# Prevent opening xmllog on worker nodes (xdist).
|
# Prevent opening xmllog on worker nodes (xdist).
|
||||||
if xmlpath and not hasattr(config, "workerinput"):
|
if xmlpath and not hasattr(config, "workerinput"):
|
||||||
junit_family = config.getini("junit_family")
|
junit_family = config.getini("junit_family")
|
||||||
if not junit_family:
|
|
||||||
_issue_warning_captured(deprecated.JUNIT_XML_DEFAULT_FAMILY, config.hook, 2)
|
|
||||||
junit_family = "xunit1"
|
|
||||||
config._store[xml_key] = LogXML(
|
config._store[xml_key] = LogXML(
|
||||||
xmlpath,
|
xmlpath,
|
||||||
config.option.junitprefix,
|
config.option.junitprefix,
|
||||||
|
|
|
@ -26,7 +26,6 @@ from _pytest.compat import TYPE_CHECKING
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import ConftestImportFailure
|
from _pytest.config import ConftestImportFailure
|
||||||
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
||||||
from _pytest.deprecated import NODE_USE_FROM_PARENT
|
|
||||||
from _pytest.fixtures import FixtureDef
|
from _pytest.fixtures import FixtureDef
|
||||||
from _pytest.fixtures import FixtureLookupError
|
from _pytest.fixtures import FixtureLookupError
|
||||||
from _pytest.mark.structures import Mark
|
from _pytest.mark.structures import Mark
|
||||||
|
@ -97,8 +96,13 @@ _NodeType = TypeVar("_NodeType", bound="Node")
|
||||||
|
|
||||||
class NodeMeta(type):
|
class NodeMeta(type):
|
||||||
def __call__(self, *k, **kw):
|
def __call__(self, *k, **kw):
|
||||||
warnings.warn(NODE_USE_FROM_PARENT.format(name=self.__name__), stacklevel=2)
|
msg = (
|
||||||
return super().__call__(*k, **kw)
|
"Direct construction of {name} has been deprecated, please use {name}.from_parent.\n"
|
||||||
|
"See "
|
||||||
|
"https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent"
|
||||||
|
" for more details."
|
||||||
|
).format(name=self.__name__)
|
||||||
|
fail(msg, pytrace=False)
|
||||||
|
|
||||||
def _create(self, *k, **kw):
|
def _create(self, *k, **kw):
|
||||||
return super().__call__(*k, **kw)
|
return super().__call__(*k, **kw)
|
||||||
|
@ -549,8 +553,6 @@ class FSCollector(Collector):
|
||||||
for pat in self._norecursepatterns:
|
for pat in self._norecursepatterns:
|
||||||
if path.check(fnmatch=pat):
|
if path.check(fnmatch=pat):
|
||||||
return False
|
return False
|
||||||
ihook = self.session.gethookproxy(path)
|
|
||||||
ihook.pytest_collect_directory(path=path, parent=self)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _collectfile(
|
def _collectfile(
|
||||||
|
|
|
@ -53,7 +53,6 @@ from _pytest.config import ExitCode
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
||||||
from _pytest.deprecated import FUNCARGNAMES
|
|
||||||
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
|
||||||
|
@ -906,12 +905,6 @@ class Metafunc:
|
||||||
self._calls = [] # type: List[CallSpec2]
|
self._calls = [] # type: List[CallSpec2]
|
||||||
self._arg2fixturedefs = fixtureinfo.name2fixturedefs
|
self._arg2fixturedefs = fixtureinfo.name2fixturedefs
|
||||||
|
|
||||||
@property
|
|
||||||
def funcargnames(self) -> List[str]:
|
|
||||||
"""Alias attribute for ``fixturenames`` for pre-2.3 compatibility."""
|
|
||||||
warnings.warn(FUNCARGNAMES, stacklevel=2)
|
|
||||||
return self.fixturenames
|
|
||||||
|
|
||||||
def parametrize(
|
def parametrize(
|
||||||
self,
|
self,
|
||||||
argnames: Union[str, List[str], Tuple[str, ...]],
|
argnames: Union[str, List[str], Tuple[str, ...]],
|
||||||
|
@ -1568,12 +1561,6 @@ class Function(PyobjMixin, nodes.Item):
|
||||||
"""(compatonly) for code expecting pytest-2.2 style request objects."""
|
"""(compatonly) for code expecting pytest-2.2 style request objects."""
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@property
|
|
||||||
def funcargnames(self) -> List[str]:
|
|
||||||
"""Alias attribute for ``fixturenames`` for pre-2.3 compatibility."""
|
|
||||||
warnings.warn(FUNCARGNAMES, stacklevel=2)
|
|
||||||
return self.fixturenames
|
|
||||||
|
|
||||||
def runtest(self) -> None:
|
def runtest(self) -> None:
|
||||||
"""Execute the underlying test function."""
|
"""Execute the underlying test function."""
|
||||||
self.ihook.pytest_pyfunc_call(pyfuncitem=self)
|
self.ihook.pytest_pyfunc_call(pyfuncitem=self)
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
"""log machine-parseable test session result information to a plain text file."""
|
|
||||||
import os
|
|
||||||
from typing import IO
|
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
from _pytest._code.code import ExceptionRepr
|
|
||||||
from _pytest.config import Config
|
|
||||||
from _pytest.config.argparsing import Parser
|
|
||||||
from _pytest.reports import CollectReport
|
|
||||||
from _pytest.reports import TestReport
|
|
||||||
from _pytest.store import StoreKey
|
|
||||||
|
|
||||||
|
|
||||||
resultlog_key = StoreKey["ResultLog"]()
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser: Parser) -> None:
|
|
||||||
group = parser.getgroup("terminal reporting", "resultlog plugin options")
|
|
||||||
group.addoption(
|
|
||||||
"--resultlog",
|
|
||||||
"--result-log",
|
|
||||||
action="store",
|
|
||||||
metavar="path",
|
|
||||||
default=None,
|
|
||||||
help="DEPRECATED path for machine-readable result log.",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_configure(config: Config) -> None:
|
|
||||||
resultlog = config.option.resultlog
|
|
||||||
# Prevent opening resultlog on worker nodes (xdist).
|
|
||||||
if resultlog and not hasattr(config, "workerinput"):
|
|
||||||
dirname = os.path.dirname(os.path.abspath(resultlog))
|
|
||||||
if not os.path.isdir(dirname):
|
|
||||||
os.makedirs(dirname)
|
|
||||||
logfile = open(resultlog, "w", 1) # line buffered
|
|
||||||
config._store[resultlog_key] = ResultLog(config, logfile)
|
|
||||||
config.pluginmanager.register(config._store[resultlog_key])
|
|
||||||
|
|
||||||
from _pytest.deprecated import RESULT_LOG
|
|
||||||
from _pytest.warnings import _issue_warning_captured
|
|
||||||
|
|
||||||
_issue_warning_captured(RESULT_LOG, config.hook, stacklevel=2)
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_unconfigure(config: Config) -> None:
|
|
||||||
resultlog = config._store.get(resultlog_key, None)
|
|
||||||
if resultlog:
|
|
||||||
resultlog.logfile.close()
|
|
||||||
del config._store[resultlog_key]
|
|
||||||
config.pluginmanager.unregister(resultlog)
|
|
||||||
|
|
||||||
|
|
||||||
class ResultLog:
|
|
||||||
def __init__(self, config: Config, logfile: IO[str]) -> None:
|
|
||||||
self.config = config
|
|
||||||
self.logfile = logfile # preferably line buffered
|
|
||||||
|
|
||||||
def write_log_entry(self, testpath: str, lettercode: str, longrepr: str) -> None:
|
|
||||||
print("{} {}".format(lettercode, testpath), file=self.logfile)
|
|
||||||
for line in longrepr.splitlines():
|
|
||||||
print(" %s" % line, file=self.logfile)
|
|
||||||
|
|
||||||
def log_outcome(
|
|
||||||
self, report: Union[TestReport, CollectReport], lettercode: str, longrepr: str
|
|
||||||
) -> None:
|
|
||||||
testpath = getattr(report, "nodeid", None)
|
|
||||||
if testpath is None:
|
|
||||||
testpath = report.fspath
|
|
||||||
self.write_log_entry(testpath, lettercode, longrepr)
|
|
||||||
|
|
||||||
def pytest_runtest_logreport(self, report: TestReport) -> None:
|
|
||||||
if report.when != "call" and report.passed:
|
|
||||||
return
|
|
||||||
res = self.config.hook.pytest_report_teststatus(
|
|
||||||
report=report, config=self.config
|
|
||||||
)
|
|
||||||
code = res[1] # type: str
|
|
||||||
if code == "x":
|
|
||||||
longrepr = str(report.longrepr)
|
|
||||||
elif code == "X":
|
|
||||||
longrepr = ""
|
|
||||||
elif report.passed:
|
|
||||||
longrepr = ""
|
|
||||||
elif report.skipped:
|
|
||||||
assert isinstance(report.longrepr, tuple)
|
|
||||||
longrepr = str(report.longrepr[2])
|
|
||||||
else:
|
|
||||||
longrepr = str(report.longrepr)
|
|
||||||
self.log_outcome(report, code, longrepr)
|
|
||||||
|
|
||||||
def pytest_collectreport(self, report: CollectReport) -> None:
|
|
||||||
if not report.passed:
|
|
||||||
if report.failed:
|
|
||||||
code = "F"
|
|
||||||
longrepr = str(report.longrepr)
|
|
||||||
else:
|
|
||||||
assert report.skipped
|
|
||||||
code = "S"
|
|
||||||
longrepr = "%s:%d: %s" % report.longrepr # type: ignore
|
|
||||||
self.log_outcome(report, code, longrepr)
|
|
||||||
|
|
||||||
def pytest_internalerror(self, excrepr: ExceptionRepr) -> None:
|
|
||||||
if excrepr.reprcrash is not None:
|
|
||||||
path = excrepr.reprcrash.path
|
|
||||||
else:
|
|
||||||
path = "cwd:%s" % os.getcwd()
|
|
||||||
self.write_log_entry(path, "!", str(excrepr))
|
|
|
@ -31,7 +31,6 @@ from _pytest import nodes
|
||||||
from _pytest import timing
|
from _pytest import timing
|
||||||
from _pytest._code import ExceptionInfo
|
from _pytest._code import ExceptionInfo
|
||||||
from _pytest._code.code import ExceptionRepr
|
from _pytest._code.code import ExceptionRepr
|
||||||
from _pytest._io import TerminalWriter
|
|
||||||
from _pytest._io.wcwidth import wcswidth
|
from _pytest._io.wcwidth import wcswidth
|
||||||
from _pytest.compat import order_preserving_dict
|
from _pytest.compat import order_preserving_dict
|
||||||
from _pytest.compat import TYPE_CHECKING
|
from _pytest.compat import TYPE_CHECKING
|
||||||
|
@ -39,7 +38,6 @@ from _pytest.config import _PluggyPlugin
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import ExitCode
|
from _pytest.config import ExitCode
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.deprecated import TERMINALWRITER_WRITER
|
|
||||||
from _pytest.nodes import Item
|
from _pytest.nodes import Item
|
||||||
from _pytest.nodes import Node
|
from _pytest.nodes import Node
|
||||||
from _pytest.reports import BaseReport
|
from _pytest.reports import BaseReport
|
||||||
|
@ -335,16 +333,6 @@ class TerminalReporter:
|
||||||
self._already_displayed_warnings = None # type: Optional[int]
|
self._already_displayed_warnings = None # type: Optional[int]
|
||||||
self._keyboardinterrupt_memo = None # type: Optional[ExceptionRepr]
|
self._keyboardinterrupt_memo = None # type: Optional[ExceptionRepr]
|
||||||
|
|
||||||
@property
|
|
||||||
def writer(self) -> TerminalWriter:
|
|
||||||
warnings.warn(TERMINALWRITER_WRITER, stacklevel=2)
|
|
||||||
return self._tw
|
|
||||||
|
|
||||||
@writer.setter
|
|
||||||
def writer(self, value: TerminalWriter) -> None:
|
|
||||||
warnings.warn(TERMINALWRITER_WRITER, stacklevel=2)
|
|
||||||
self._tw = value
|
|
||||||
|
|
||||||
def _determine_show_progress_info(self) -> "Literal['progress', 'count', False]":
|
def _determine_show_progress_info(self) -> "Literal['progress', 'count', False]":
|
||||||
"""Return whether we should display progress information based on the current config."""
|
"""Return whether we should display progress information based on the current config."""
|
||||||
# do not show progress if we are not capturing output (#3038)
|
# do not show progress if we are not capturing output (#3038)
|
||||||
|
|
|
@ -104,8 +104,6 @@ def catch_warnings_for_item(
|
||||||
warnings.filterwarnings("always", category=DeprecationWarning)
|
warnings.filterwarnings("always", category=DeprecationWarning)
|
||||||
warnings.filterwarnings("always", category=PendingDeprecationWarning)
|
warnings.filterwarnings("always", category=PendingDeprecationWarning)
|
||||||
|
|
||||||
warnings.filterwarnings("error", category=pytest.PytestDeprecationWarning)
|
|
||||||
|
|
||||||
# Filters should have this precedence: mark, cmdline options, ini.
|
# Filters should have this precedence: mark, cmdline options, ini.
|
||||||
# Filters should be applied in the inverse order of precedence.
|
# Filters should be applied in the inverse order of precedence.
|
||||||
for arg in inifilters:
|
for arg in inifilters:
|
||||||
|
|
|
@ -223,13 +223,12 @@ class TestGeneralUsage:
|
||||||
"E {}: No module named 'qwerty'".format(exc_name),
|
"E {}: No module named 'qwerty'".format(exc_name),
|
||||||
]
|
]
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("ignore::pytest.PytestDeprecationWarning")
|
|
||||||
def test_early_skip(self, testdir):
|
def test_early_skip(self, testdir):
|
||||||
testdir.mkdir("xyz")
|
testdir.mkdir("xyz")
|
||||||
testdir.makeconftest(
|
testdir.makeconftest(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
def pytest_collect_directory():
|
def pytest_collect_file():
|
||||||
pytest.skip("early")
|
pytest.skip("early")
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,35 +1,11 @@
|
||||||
import copy
|
|
||||||
import inspect
|
|
||||||
import warnings
|
import warnings
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest import deprecated
|
from _pytest import deprecated
|
||||||
from _pytest import nodes
|
|
||||||
from _pytest.config import Config
|
|
||||||
from _pytest.pytester import Testdir
|
from _pytest.pytester import Testdir
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("default")
|
|
||||||
def test_resultlog_is_deprecated(testdir):
|
|
||||||
result = testdir.runpytest("--help")
|
|
||||||
result.stdout.fnmatch_lines(["*DEPRECATED path for machine-readable result log*"])
|
|
||||||
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
def test():
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
result = testdir.runpytest("--result-log=%s" % testdir.tmpdir.join("result.log"))
|
|
||||||
result.stdout.fnmatch_lines(
|
|
||||||
[
|
|
||||||
"*--result-log is deprecated, please try the new pytest-reportlog plugin.",
|
|
||||||
"*See https://docs.pytest.org/en/stable/deprecations.html#result-log-result-log for more information*",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="should be reintroduced in 6.1: #7361")
|
@pytest.mark.skip(reason="should be reintroduced in 6.1: #7361")
|
||||||
@pytest.mark.parametrize("attribute", pytest.collect.__all__) # type: ignore
|
@pytest.mark.parametrize("attribute", pytest.collect.__all__) # type: ignore
|
||||||
# false positive due to dynamic attribute
|
# false positive due to dynamic attribute
|
||||||
|
@ -38,35 +14,6 @@ def test_pytest_collect_module_deprecated(attribute):
|
||||||
getattr(pytest.collect, attribute)
|
getattr(pytest.collect, attribute)
|
||||||
|
|
||||||
|
|
||||||
def test_terminal_reporter_writer_attr(pytestconfig: Config) -> None:
|
|
||||||
"""Check that TerminalReporter._tw is also available as 'writer' (#2984)
|
|
||||||
This attribute has been deprecated in 5.4.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
import xdist # noqa
|
|
||||||
|
|
||||||
pytest.skip("xdist workers disable the terminal reporter plugin")
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
terminal_reporter = pytestconfig.pluginmanager.get_plugin("terminalreporter")
|
|
||||||
original_tw = terminal_reporter._tw
|
|
||||||
|
|
||||||
with pytest.warns(pytest.PytestDeprecationWarning) as cw:
|
|
||||||
assert terminal_reporter.writer is original_tw
|
|
||||||
assert len(cw) == 1
|
|
||||||
assert cw[0].filename == __file__
|
|
||||||
|
|
||||||
new_tw = copy.copy(original_tw)
|
|
||||||
with pytest.warns(pytest.PytestDeprecationWarning) as cw:
|
|
||||||
terminal_reporter.writer = new_tw
|
|
||||||
try:
|
|
||||||
assert terminal_reporter._tw is new_tw
|
|
||||||
finally:
|
|
||||||
terminal_reporter.writer = original_tw
|
|
||||||
assert len(cw) == 2
|
|
||||||
assert cw[0].filename == cw[1].filename == __file__
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("plugin", sorted(deprecated.DEPRECATED_EXTERNAL_PLUGINS))
|
@pytest.mark.parametrize("plugin", sorted(deprecated.DEPRECATED_EXTERNAL_PLUGINS))
|
||||||
@pytest.mark.filterwarnings("default")
|
@pytest.mark.filterwarnings("default")
|
||||||
def test_external_plugins_integrated(testdir, plugin):
|
def test_external_plugins_integrated(testdir, plugin):
|
||||||
|
@ -77,49 +24,6 @@ def test_external_plugins_integrated(testdir, plugin):
|
||||||
testdir.parseconfig("-p", plugin)
|
testdir.parseconfig("-p", plugin)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("junit_family", [None, "legacy", "xunit2"])
|
|
||||||
def test_warn_about_imminent_junit_family_default_change(testdir, junit_family):
|
|
||||||
"""Show a warning if junit_family is not defined and --junitxml is used (#6179)"""
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
def test_foo():
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
if junit_family:
|
|
||||||
testdir.makeini(
|
|
||||||
"""
|
|
||||||
[pytest]
|
|
||||||
junit_family={junit_family}
|
|
||||||
""".format(
|
|
||||||
junit_family=junit_family
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
result = testdir.runpytest("--junit-xml=foo.xml")
|
|
||||||
warning_msg = (
|
|
||||||
"*PytestDeprecationWarning: The 'junit_family' default value will change*"
|
|
||||||
)
|
|
||||||
if junit_family:
|
|
||||||
result.stdout.no_fnmatch_line(warning_msg)
|
|
||||||
else:
|
|
||||||
result.stdout.fnmatch_lines([warning_msg])
|
|
||||||
|
|
||||||
|
|
||||||
def test_node_direct_ctor_warning() -> None:
|
|
||||||
class MockConfig:
|
|
||||||
pass
|
|
||||||
|
|
||||||
ms = MockConfig()
|
|
||||||
with pytest.warns(
|
|
||||||
DeprecationWarning,
|
|
||||||
match="Direct construction of .* has been deprecated, please use .*.from_parent.*",
|
|
||||||
) as w:
|
|
||||||
nodes.Node(name="test", config=ms, session=ms, nodeid="None") # type: ignore
|
|
||||||
assert w[0].lineno == inspect.currentframe().f_lineno - 1 # type: ignore
|
|
||||||
assert w[0].filename == __file__
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="should be reintroduced in 6.1: #7361")
|
@pytest.mark.skip(reason="should be reintroduced in 6.1: #7361")
|
||||||
def test_fillfuncargs_is_deprecated() -> None:
|
def test_fillfuncargs_is_deprecated() -> None:
|
||||||
with pytest.warns(
|
with pytest.warns(
|
||||||
|
|
|
@ -815,28 +815,6 @@ class TestRequestBasic:
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
|
||||||
def test_funcargnames_compatattr(self, testdir):
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
import pytest
|
|
||||||
def pytest_generate_tests(metafunc):
|
|
||||||
with pytest.warns(pytest.PytestDeprecationWarning):
|
|
||||||
assert metafunc.funcargnames == metafunc.fixturenames
|
|
||||||
@pytest.fixture
|
|
||||||
def fn(request):
|
|
||||||
with pytest.warns(pytest.PytestDeprecationWarning):
|
|
||||||
assert request._pyfuncitem.funcargnames == \
|
|
||||||
request._pyfuncitem.fixturenames
|
|
||||||
with pytest.warns(pytest.PytestDeprecationWarning):
|
|
||||||
return request.funcargnames, request.fixturenames
|
|
||||||
|
|
||||||
def test_hello(fn):
|
|
||||||
assert fn[0] == fn[1]
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
reprec = testdir.inline_run()
|
|
||||||
reprec.assertoutcome(passed=1)
|
|
||||||
|
|
||||||
def test_setupdecorator_and_xunit(self, testdir):
|
def test_setupdecorator_and_xunit(self, testdir):
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
@ -4152,73 +4130,6 @@ def test_fixture_named_request(testdir):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_fixture_duplicated_arguments() -> None:
|
|
||||||
"""Raise error if there are positional and keyword arguments for the same parameter (#1682)."""
|
|
||||||
with pytest.raises(TypeError) as excinfo:
|
|
||||||
|
|
||||||
@pytest.fixture("session", scope="session") # type: ignore[call-overload]
|
|
||||||
def arg(arg):
|
|
||||||
pass
|
|
||||||
|
|
||||||
assert (
|
|
||||||
str(excinfo.value)
|
|
||||||
== "The fixture arguments are defined as positional and keyword: scope. "
|
|
||||||
"Use only keyword arguments."
|
|
||||||
)
|
|
||||||
|
|
||||||
with pytest.raises(TypeError) as excinfo:
|
|
||||||
|
|
||||||
@pytest.fixture( # type: ignore[call-overload]
|
|
||||||
"function",
|
|
||||||
["p1"],
|
|
||||||
True,
|
|
||||||
["id1"],
|
|
||||||
"name",
|
|
||||||
scope="session",
|
|
||||||
params=["p1"],
|
|
||||||
autouse=True,
|
|
||||||
ids=["id1"],
|
|
||||||
name="name",
|
|
||||||
)
|
|
||||||
def arg2(request):
|
|
||||||
pass
|
|
||||||
|
|
||||||
assert (
|
|
||||||
str(excinfo.value)
|
|
||||||
== "The fixture arguments are defined as positional and keyword: scope, params, autouse, ids, name. "
|
|
||||||
"Use only keyword arguments."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_fixture_with_positionals() -> None:
|
|
||||||
"""Raise warning, but the positionals should still works (#1682)."""
|
|
||||||
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
|
|
||||||
|
|
||||||
with pytest.warns(pytest.PytestDeprecationWarning) as warnings:
|
|
||||||
|
|
||||||
@pytest.fixture("function", [0], True) # type: ignore[call-overload]
|
|
||||||
def fixture_with_positionals():
|
|
||||||
pass
|
|
||||||
|
|
||||||
assert str(warnings[0].message) == str(FIXTURE_POSITIONAL_ARGUMENTS)
|
|
||||||
|
|
||||||
assert fixture_with_positionals._pytestfixturefunction.scope == "function"
|
|
||||||
assert fixture_with_positionals._pytestfixturefunction.params == (0,)
|
|
||||||
assert fixture_with_positionals._pytestfixturefunction.autouse
|
|
||||||
|
|
||||||
|
|
||||||
def test_fixture_with_too_many_positionals() -> None:
|
|
||||||
with pytest.raises(TypeError) as excinfo:
|
|
||||||
|
|
||||||
@pytest.fixture("function", [0], True, ["id"], "name", "extra") # type: ignore[call-overload]
|
|
||||||
def fixture_with_positionals():
|
|
||||||
pass
|
|
||||||
|
|
||||||
assert (
|
|
||||||
str(excinfo.value) == "fixture() takes 5 positional arguments but 6 were given"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_indirect_fixture_does_not_break_scope(testdir):
|
def test_indirect_fixture_does_not_break_scope(testdir):
|
||||||
"""Ensure that fixture scope is respected when using indirect fixtures (#570)"""
|
"""Ensure that fixture scope is respected when using indirect fixtures (#570)"""
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
|
|
|
@ -257,20 +257,6 @@ class TestCollectPluginHookRelay:
|
||||||
assert len(wascalled) == 1
|
assert len(wascalled) == 1
|
||||||
assert wascalled[0].ext == ".abc"
|
assert wascalled[0].ext == ".abc"
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("ignore:.*pytest_collect_directory.*")
|
|
||||||
def test_pytest_collect_directory(self, testdir):
|
|
||||||
wascalled = []
|
|
||||||
|
|
||||||
class Plugin:
|
|
||||||
def pytest_collect_directory(self, path):
|
|
||||||
wascalled.append(path.basename)
|
|
||||||
|
|
||||||
testdir.mkdir("hello")
|
|
||||||
testdir.mkdir("world")
|
|
||||||
pytest.main(testdir.tmpdir, plugins=[Plugin()])
|
|
||||||
assert "hello" in wascalled
|
|
||||||
assert "world" in wascalled
|
|
||||||
|
|
||||||
|
|
||||||
class TestPrunetraceback:
|
class TestPrunetraceback:
|
||||||
def test_custom_repr_failure(self, testdir):
|
def test_custom_repr_failure(self, testdir):
|
||||||
|
|
|
@ -306,21 +306,6 @@ def test_no_conftest(testdir):
|
||||||
assert result.ret == ExitCode.USAGE_ERROR
|
assert result.ret == ExitCode.USAGE_ERROR
|
||||||
|
|
||||||
|
|
||||||
def test_conftest_existing_resultlog(testdir):
|
|
||||||
x = testdir.mkdir("tests")
|
|
||||||
x.join("conftest.py").write(
|
|
||||||
textwrap.dedent(
|
|
||||||
"""\
|
|
||||||
def pytest_addoption(parser):
|
|
||||||
parser.addoption("--xyz", action="store_true")
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
testdir.makefile(ext=".log", result="") # Writes result.log
|
|
||||||
result = testdir.runpytest("-h", "--resultlog", "result.log")
|
|
||||||
result.stdout.fnmatch_lines(["*--xyz*"])
|
|
||||||
|
|
||||||
|
|
||||||
def test_conftest_existing_junitxml(testdir):
|
def test_conftest_existing_junitxml(testdir):
|
||||||
x = testdir.mkdir("tests")
|
x = testdir.mkdir("tests")
|
||||||
x.join("conftest.py").write(
|
x.join("conftest.py").write(
|
||||||
|
|
|
@ -1,256 +0,0 @@
|
||||||
import os
|
|
||||||
from io import StringIO
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import _pytest._code
|
|
||||||
import pytest
|
|
||||||
from _pytest.pytester import Testdir
|
|
||||||
from _pytest.resultlog import pytest_configure
|
|
||||||
from _pytest.resultlog import pytest_unconfigure
|
|
||||||
from _pytest.resultlog import ResultLog
|
|
||||||
from _pytest.resultlog import resultlog_key
|
|
||||||
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.filterwarnings("ignore:--result-log is deprecated")
|
|
||||||
|
|
||||||
|
|
||||||
def test_write_log_entry() -> None:
|
|
||||||
reslog = ResultLog(None, None) # type: ignore[arg-type]
|
|
||||||
reslog.logfile = StringIO()
|
|
||||||
reslog.write_log_entry("name", ".", "")
|
|
||||||
entry = reslog.logfile.getvalue()
|
|
||||||
assert entry[-1] == "\n"
|
|
||||||
entry_lines = entry.splitlines()
|
|
||||||
assert len(entry_lines) == 1
|
|
||||||
assert entry_lines[0] == ". name"
|
|
||||||
|
|
||||||
reslog.logfile = StringIO()
|
|
||||||
reslog.write_log_entry("name", "s", "Skipped")
|
|
||||||
entry = reslog.logfile.getvalue()
|
|
||||||
assert entry[-1] == "\n"
|
|
||||||
entry_lines = entry.splitlines()
|
|
||||||
assert len(entry_lines) == 2
|
|
||||||
assert entry_lines[0] == "s name"
|
|
||||||
assert entry_lines[1] == " Skipped"
|
|
||||||
|
|
||||||
reslog.logfile = StringIO()
|
|
||||||
reslog.write_log_entry("name", "s", "Skipped\n")
|
|
||||||
entry = reslog.logfile.getvalue()
|
|
||||||
assert entry[-1] == "\n"
|
|
||||||
entry_lines = entry.splitlines()
|
|
||||||
assert len(entry_lines) == 2
|
|
||||||
assert entry_lines[0] == "s name"
|
|
||||||
assert entry_lines[1] == " Skipped"
|
|
||||||
|
|
||||||
reslog.logfile = StringIO()
|
|
||||||
longrepr = " tb1\n tb 2\nE tb3\nSome Error"
|
|
||||||
reslog.write_log_entry("name", "F", longrepr)
|
|
||||||
entry = reslog.logfile.getvalue()
|
|
||||||
assert entry[-1] == "\n"
|
|
||||||
entry_lines = entry.splitlines()
|
|
||||||
assert len(entry_lines) == 5
|
|
||||||
assert entry_lines[0] == "F name"
|
|
||||||
assert entry_lines[1:] == [" " + line for line in longrepr.splitlines()]
|
|
||||||
|
|
||||||
|
|
||||||
class TestWithFunctionIntegration:
|
|
||||||
# XXX (hpk) i think that the resultlog plugin should
|
|
||||||
# provide a Parser object so that one can remain
|
|
||||||
# ignorant regarding formatting details.
|
|
||||||
def getresultlog(self, testdir: Testdir, arg: str) -> List[str]:
|
|
||||||
resultlog = testdir.tmpdir.join("resultlog")
|
|
||||||
testdir.plugins.append("resultlog")
|
|
||||||
args = ["--resultlog=%s" % resultlog] + [arg]
|
|
||||||
testdir.runpytest(*args)
|
|
||||||
return [x for x in resultlog.readlines(cr=0) if x]
|
|
||||||
|
|
||||||
def test_collection_report(self, testdir: Testdir) -> None:
|
|
||||||
ok = testdir.makepyfile(test_collection_ok="")
|
|
||||||
fail = testdir.makepyfile(test_collection_fail="XXX")
|
|
||||||
lines = self.getresultlog(testdir, ok)
|
|
||||||
assert not lines
|
|
||||||
|
|
||||||
lines = self.getresultlog(testdir, fail)
|
|
||||||
assert lines
|
|
||||||
assert lines[0].startswith("F ")
|
|
||||||
assert lines[0].endswith("test_collection_fail.py"), lines[0]
|
|
||||||
for x in lines[1:]:
|
|
||||||
assert x.startswith(" ")
|
|
||||||
assert "XXX" in "".join(lines[1:])
|
|
||||||
|
|
||||||
def test_log_test_outcomes(self, testdir: Testdir) -> None:
|
|
||||||
mod = testdir.makepyfile(
|
|
||||||
test_mod="""
|
|
||||||
import pytest
|
|
||||||
def test_pass(): pass
|
|
||||||
def test_skip(): pytest.skip("hello")
|
|
||||||
def test_fail(): raise ValueError("FAIL")
|
|
||||||
|
|
||||||
@pytest.mark.xfail
|
|
||||||
def test_xfail(): raise ValueError("XFAIL")
|
|
||||||
@pytest.mark.xfail
|
|
||||||
def test_xpass(): pass
|
|
||||||
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
lines = self.getresultlog(testdir, mod)
|
|
||||||
assert len(lines) >= 3
|
|
||||||
assert lines[0].startswith(". ")
|
|
||||||
assert lines[0].endswith("test_pass")
|
|
||||||
assert lines[1].startswith("s "), lines[1]
|
|
||||||
assert lines[1].endswith("test_skip")
|
|
||||||
assert lines[2].find("hello") != -1
|
|
||||||
|
|
||||||
assert lines[3].startswith("F ")
|
|
||||||
assert lines[3].endswith("test_fail")
|
|
||||||
tb = "".join(lines[4:8])
|
|
||||||
assert tb.find('raise ValueError("FAIL")') != -1
|
|
||||||
|
|
||||||
assert lines[8].startswith("x ")
|
|
||||||
tb = "".join(lines[8:14])
|
|
||||||
assert tb.find('raise ValueError("XFAIL")') != -1
|
|
||||||
|
|
||||||
assert lines[14].startswith("X ")
|
|
||||||
assert len(lines) == 15
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("style", ("native", "long", "short"))
|
|
||||||
def test_internal_exception(self, style) -> None:
|
|
||||||
# they are produced for example by a teardown failing
|
|
||||||
# at the end of the run or a failing hook invocation
|
|
||||||
try:
|
|
||||||
raise ValueError
|
|
||||||
except ValueError:
|
|
||||||
excinfo = _pytest._code.ExceptionInfo.from_current()
|
|
||||||
file = StringIO()
|
|
||||||
reslog = ResultLog(None, file) # type: ignore[arg-type]
|
|
||||||
reslog.pytest_internalerror(excinfo.getrepr(style=style))
|
|
||||||
entry = file.getvalue()
|
|
||||||
entry_lines = entry.splitlines()
|
|
||||||
|
|
||||||
assert entry_lines[0].startswith("! ")
|
|
||||||
if style != "native":
|
|
||||||
assert os.path.basename(__file__)[:-9] in entry_lines[0] # .pyc/class
|
|
||||||
assert entry_lines[-1][0] == " "
|
|
||||||
assert "ValueError" in entry
|
|
||||||
|
|
||||||
|
|
||||||
def test_generic(testdir: Testdir, LineMatcher) -> None:
|
|
||||||
testdir.plugins.append("resultlog")
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
import pytest
|
|
||||||
def test_pass():
|
|
||||||
pass
|
|
||||||
def test_fail():
|
|
||||||
assert 0
|
|
||||||
def test_skip():
|
|
||||||
pytest.skip("")
|
|
||||||
@pytest.mark.xfail
|
|
||||||
def test_xfail():
|
|
||||||
assert 0
|
|
||||||
@pytest.mark.xfail(run=False)
|
|
||||||
def test_xfail_norun():
|
|
||||||
assert 0
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
testdir.runpytest("--resultlog=result.log")
|
|
||||||
lines = testdir.tmpdir.join("result.log").readlines(cr=0)
|
|
||||||
LineMatcher(lines).fnmatch_lines(
|
|
||||||
[
|
|
||||||
". *:test_pass",
|
|
||||||
"F *:test_fail",
|
|
||||||
"s *:test_skip",
|
|
||||||
"x *:test_xfail",
|
|
||||||
"x *:test_xfail_norun",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_makedir_for_resultlog(testdir: Testdir, LineMatcher) -> None:
|
|
||||||
"""--resultlog should automatically create directories for the log file"""
|
|
||||||
testdir.plugins.append("resultlog")
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
import pytest
|
|
||||||
def test_pass():
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
testdir.runpytest("--resultlog=path/to/result.log")
|
|
||||||
lines = testdir.tmpdir.join("path/to/result.log").readlines(cr=0)
|
|
||||||
LineMatcher(lines).fnmatch_lines([". *:test_pass"])
|
|
||||||
|
|
||||||
|
|
||||||
def test_no_resultlog_on_workers(testdir: Testdir) -> None:
|
|
||||||
config = testdir.parseconfig("-p", "resultlog", "--resultlog=resultlog")
|
|
||||||
|
|
||||||
assert resultlog_key not in config._store
|
|
||||||
pytest_configure(config)
|
|
||||||
assert resultlog_key in config._store
|
|
||||||
pytest_unconfigure(config)
|
|
||||||
assert resultlog_key not in config._store
|
|
||||||
|
|
||||||
config.workerinput = {} # type: ignore[attr-defined]
|
|
||||||
pytest_configure(config)
|
|
||||||
assert resultlog_key not in config._store
|
|
||||||
pytest_unconfigure(config)
|
|
||||||
assert resultlog_key not in config._store
|
|
||||||
|
|
||||||
|
|
||||||
def test_unknown_teststatus(testdir: Testdir) -> None:
|
|
||||||
"""Ensure resultlog correctly handles unknown status from pytest_report_teststatus
|
|
||||||
|
|
||||||
Inspired on pytest-rerunfailures.
|
|
||||||
"""
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
def test():
|
|
||||||
assert 0
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
testdir.makeconftest(
|
|
||||||
"""
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
def pytest_report_teststatus(report):
|
|
||||||
if report.outcome == 'rerun':
|
|
||||||
return "rerun", "r", "RERUN"
|
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
|
||||||
def pytest_runtest_makereport():
|
|
||||||
res = yield
|
|
||||||
report = res.get_result()
|
|
||||||
if report.when == "call":
|
|
||||||
report.outcome = 'rerun'
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
result = testdir.runpytest("--resultlog=result.log")
|
|
||||||
result.stdout.fnmatch_lines(
|
|
||||||
["test_unknown_teststatus.py r *[[]100%[]]", "* 1 rerun *"]
|
|
||||||
)
|
|
||||||
|
|
||||||
lines = testdir.tmpdir.join("result.log").readlines(cr=0)
|
|
||||||
assert lines[0] == "r test_unknown_teststatus.py::test"
|
|
||||||
|
|
||||||
|
|
||||||
def test_failure_issue380(testdir: Testdir) -> None:
|
|
||||||
testdir.makeconftest(
|
|
||||||
"""
|
|
||||||
import pytest
|
|
||||||
class MyCollector(pytest.File):
|
|
||||||
def collect(self):
|
|
||||||
raise ValueError()
|
|
||||||
def repr_failure(self, excinfo):
|
|
||||||
return "somestring"
|
|
||||||
def pytest_collect_file(path, parent):
|
|
||||||
return MyCollector(parent=parent, fspath=path)
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
def test_func():
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
result = testdir.runpytest("--resultlog=log")
|
|
||||||
assert result.ret == 2
|
|
|
@ -513,6 +513,9 @@ class TestDeprecationWarningsByDefault:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("change_default", [None, "ini", "cmdline"])
|
@pytest.mark.parametrize("change_default", [None, "ini", "cmdline"])
|
||||||
|
@pytest.mark.skip(
|
||||||
|
reason="This test should be enabled again before pytest 7.0 is released"
|
||||||
|
)
|
||||||
def test_deprecation_warning_as_error(testdir, change_default):
|
def test_deprecation_warning_as_error(testdir, change_default):
|
||||||
"""This ensures that PytestDeprecationWarnings raised by pytest are turned into errors.
|
"""This ensures that PytestDeprecationWarnings raised by pytest are turned into errors.
|
||||||
|
|
||||||
|
@ -737,31 +740,6 @@ class TestStackLevel:
|
||||||
assert "config{sep}__init__.py".format(sep=os.sep) in file
|
assert "config{sep}__init__.py".format(sep=os.sep) in file
|
||||||
assert func == "import_plugin"
|
assert func == "import_plugin"
|
||||||
|
|
||||||
def test_issue4445_resultlog(self, testdir, capwarn):
|
|
||||||
"""#4445: Make sure the warning points to a reasonable location
|
|
||||||
See origin of _issue_warning_captured at: _pytest.resultlog.py:35
|
|
||||||
"""
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
def test_dummy():
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
# Use parseconfigure() because the warning in resultlog.py is triggered in
|
|
||||||
# the pytest_configure hook
|
|
||||||
testdir.parseconfigure(
|
|
||||||
"--result-log={dir}".format(dir=testdir.tmpdir.join("result.log"))
|
|
||||||
)
|
|
||||||
|
|
||||||
# with stacklevel=2 the warning originates from resultlog.pytest_configure
|
|
||||||
# and is thrown when --result-log is used
|
|
||||||
warning, location = capwarn.captured.pop()
|
|
||||||
file, _, func = location
|
|
||||||
|
|
||||||
assert "--result-log is deprecated" in str(warning.message)
|
|
||||||
assert "resultlog.py" in file
|
|
||||||
assert func == "pytest_configure"
|
|
||||||
|
|
||||||
def test_issue4445_issue5928_mark_generator(self, testdir):
|
def test_issue4445_issue5928_mark_generator(self, testdir):
|
||||||
"""#4445 and #5928: Make sure the warning from an unknown mark points to
|
"""#4445 and #5928: Make sure the warning from an unknown mark points to
|
||||||
the test file where this mark is used.
|
the test file where this mark is used.
|
||||||
|
|
Loading…
Reference in New Issue