Merge pull request #7660 from nicoddemus/deprecated-features

This commit is contained in:
Bruno Oliveira 2020-08-19 09:04:10 -03:00 committed by GitHub
commit d69abff2c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 108 additions and 809 deletions

View File

@ -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.

View File

@ -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
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 5.5
.. deprecated:: 6.0
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.
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -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``.
This limitation in api surface intends to enable better/simpler refactoring of the collection tree.
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.
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")`.
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.
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:
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.
.. code-block:: python
``pytest_collect_directory`` hook
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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)
.. versionremoved:: 6.0
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"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 5.2
.. versionchanged:: 6.0
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
@ -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.
* `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``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 5.0
.. versionremoved:: 6.0
The ``FixtureRequest``, ``Metafunc``, and ``Function`` classes track the names of
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``.
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
~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -656,7 +656,6 @@ Collection hooks
.. autofunction:: pytest_collection
.. autofunction:: pytest_ignore_collect
.. autofunction:: pytest_collect_directory
.. autofunction:: pytest_collect_file
.. autofunction:: pytest_pycollect_makemodule

View File

@ -239,7 +239,6 @@ default_plugins = essential_plugins + (
"nose",
"assertion",
"junitxml",
"resultlog",
"doctest",
"cacheprovider",
"freeze_support",

View File

@ -19,45 +19,12 @@ DEPRECATED_EXTERNAL_PLUGINS = {
"pytest_faulthandler",
}
FUNCARGNAMES = PytestDeprecationWarning(
"The `funcargnames` attribute was an alias for `fixturenames`, "
"since pytest 2.3 - use the newer attribute instead."
)
FILLFUNCARGS = PytestDeprecationWarning(
"The `_fillfuncargs` function is deprecated, use "
"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(
PytestDeprecationWarning,
"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(
"The `-k '-expr'` syntax to -k is deprecated.\nUse `-k 'not expr'` instead."
)

View File

@ -2,7 +2,6 @@ import functools
import inspect
import os
import sys
import warnings
from collections import defaultdict
from collections import deque
from types import TracebackType
@ -46,8 +45,6 @@ from _pytest.compat import TYPE_CHECKING
from _pytest.config import _PluggyPlugin
from _pytest.config import Config
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.outcomes import fail
from _pytest.outcomes import TEST_OUTCOME
@ -457,12 +454,6 @@ class FixtureRequest:
result.extend(set(self._fixture_defs).difference(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
def node(self):
"""Underlying collection node (depends on current request scope)."""
@ -1253,7 +1244,7 @@ def fixture( # noqa: F811
def fixture( # noqa: F811
fixture_function: Optional[_FixtureFunction] = None,
*args: Any,
*,
scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function",
params: Optional[Iterable[object]] = None,
autouse: bool = False,
@ -1315,53 +1306,6 @@ def fixture( # noqa: F811
name the decorated function ``fixture_<fixturename>`` and then use
``@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(
scope=scope, params=params, autouse=autouse, ids=ids, name=name,
)

View File

@ -12,7 +12,6 @@ from typing import Union
import py.path
from pluggy import HookspecMarker
from .deprecated import COLLECT_DIRECTORY_HOOK
from _pytest.compat import 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]":
"""Return collection Node or None for the given path.

View File

@ -21,7 +21,6 @@ from typing import Tuple
from typing import Union
import pytest
from _pytest import deprecated
from _pytest import nodes
from _pytest import timing
from _pytest._code.code import ExceptionRepr
@ -33,7 +32,6 @@ from _pytest.fixtures import FixtureRequest
from _pytest.reports import TestReport
from _pytest.store import StoreKey
from _pytest.terminal import TerminalReporter
from _pytest.warnings import _issue_warning_captured
xml_key = StoreKey["LogXML"]()
@ -413,7 +411,9 @@ def pytest_addoption(parser: Parser) -> None:
default="total",
) # choices=['total', 'call'])
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).
if xmlpath and not hasattr(config, "workerinput"):
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(
xmlpath,
config.option.junitprefix,

View File

@ -26,7 +26,6 @@ from _pytest.compat import TYPE_CHECKING
from _pytest.config import Config
from _pytest.config import ConftestImportFailure
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
from _pytest.deprecated import NODE_USE_FROM_PARENT
from _pytest.fixtures import FixtureDef
from _pytest.fixtures import FixtureLookupError
from _pytest.mark.structures import Mark
@ -97,8 +96,13 @@ _NodeType = TypeVar("_NodeType", bound="Node")
class NodeMeta(type):
def __call__(self, *k, **kw):
warnings.warn(NODE_USE_FROM_PARENT.format(name=self.__name__), stacklevel=2)
return super().__call__(*k, **kw)
msg = (
"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):
return super().__call__(*k, **kw)
@ -549,8 +553,6 @@ class FSCollector(Collector):
for pat in self._norecursepatterns:
if path.check(fnmatch=pat):
return False
ihook = self.session.gethookproxy(path)
ihook.pytest_collect_directory(path=path, parent=self)
return True
def _collectfile(

View File

@ -53,7 +53,6 @@ from _pytest.config import ExitCode
from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
from _pytest.deprecated import FUNCARGNAMES
from _pytest.fixtures import FuncFixtureInfo
from _pytest.main import Session
from _pytest.mark import MARK_GEN
@ -906,12 +905,6 @@ class Metafunc:
self._calls = [] # type: List[CallSpec2]
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(
self,
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."""
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:
"""Execute the underlying test function."""
self.ihook.pytest_pyfunc_call(pyfuncitem=self)

View File

@ -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))

View File

@ -31,7 +31,6 @@ from _pytest import nodes
from _pytest import timing
from _pytest._code import ExceptionInfo
from _pytest._code.code import ExceptionRepr
from _pytest._io import TerminalWriter
from _pytest._io.wcwidth import wcswidth
from _pytest.compat import order_preserving_dict
from _pytest.compat import TYPE_CHECKING
@ -39,7 +38,6 @@ from _pytest.config import _PluggyPlugin
from _pytest.config import Config
from _pytest.config import ExitCode
from _pytest.config.argparsing import Parser
from _pytest.deprecated import TERMINALWRITER_WRITER
from _pytest.nodes import Item
from _pytest.nodes import Node
from _pytest.reports import BaseReport
@ -335,16 +333,6 @@ class TerminalReporter:
self._already_displayed_warnings = None # type: Optional[int]
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]":
"""Return whether we should display progress information based on the current config."""
# do not show progress if we are not capturing output (#3038)

View File

@ -104,8 +104,6 @@ def catch_warnings_for_item(
warnings.filterwarnings("always", category=DeprecationWarning)
warnings.filterwarnings("always", category=PendingDeprecationWarning)
warnings.filterwarnings("error", category=pytest.PytestDeprecationWarning)
# Filters should have this precedence: mark, cmdline options, ini.
# Filters should be applied in the inverse order of precedence.
for arg in inifilters:

View File

@ -223,13 +223,12 @@ class TestGeneralUsage:
"E {}: No module named 'qwerty'".format(exc_name),
]
@pytest.mark.filterwarnings("ignore::pytest.PytestDeprecationWarning")
def test_early_skip(self, testdir):
testdir.mkdir("xyz")
testdir.makeconftest(
"""
import pytest
def pytest_collect_directory():
def pytest_collect_file():
pytest.skip("early")
"""
)

View File

@ -1,35 +1,11 @@
import copy
import inspect
import warnings
from unittest import mock
import pytest
from _pytest import deprecated
from _pytest import nodes
from _pytest.config import Config
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.parametrize("attribute", pytest.collect.__all__) # type: ignore
# false positive due to dynamic attribute
@ -38,35 +14,6 @@ def test_pytest_collect_module_deprecated(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.filterwarnings("default")
def test_external_plugins_integrated(testdir, plugin):
@ -77,49 +24,6 @@ def test_external_plugins_integrated(testdir, 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")
def test_fillfuncargs_is_deprecated() -> None:
with pytest.warns(

View File

@ -815,28 +815,6 @@ class TestRequestBasic:
result = testdir.runpytest()
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):
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):
"""Ensure that fixture scope is respected when using indirect fixtures (#570)"""
testdir.makepyfile(

View File

@ -257,20 +257,6 @@ class TestCollectPluginHookRelay:
assert len(wascalled) == 1
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:
def test_custom_repr_failure(self, testdir):

View File

@ -306,21 +306,6 @@ def test_no_conftest(testdir):
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):
x = testdir.mkdir("tests")
x.join("conftest.py").write(

View File

@ -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

View File

@ -513,6 +513,9 @@ class TestDeprecationWarningsByDefault:
@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):
"""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 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):
"""#4445 and #5928: Make sure the warning from an unknown mark points to
the test file where this mark is used.