Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Actually deprecate long standing features #3978

22 changes: 22 additions & 0 deletions changelog/3616.deprecation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
The following accesses have been documented as deprecated for years, but are now actually emitting deprecation warnings.

* Access of ``Module``, ``Function``, ``Class``, ``Instance``, ``File`` and ``Item`` through ``Node`` instances. Now
users will this warning::

usage of Function.Module is deprecated, please use pytest.Module instead

Users should just ``import pytest`` and access those objects using the ``pytest`` module.

* ``request.cached_setup``, this was the precursor of the setup/teardown mechanism available to fixtures. You can
consult `funcarg comparision section in the docs <https://docs.pytest.org/en/latest/funcarg_compare.html>`_.

* Using objects named ``"Class"`` as a way to customize the type of nodes that are collected in ``Collector``
subclasses has been deprecated. Users instead should use ``pytest_collect_make_item`` to customize node types during
collection.

This issue should affect only advanced plugins who create new collection types, so if you see this warning
message please contact the authors so they can change the code.

* The warning that produces the message below has changed to ``RemovedInPytest4Warning``::

getfuncargvalue is deprecated, use getfixturevalue
3 changes: 2 additions & 1 deletion changelog/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ Each file should be named like ``<ISSUE>.<TYPE>.rst``, where
* ``feature``: new user facing features, like new command-line options and new behavior.
* ``bugfix``: fixes a reported bug.
* ``doc``: documentation improvement, like rewording an entire session or adding missing docs.
* ``removal``: feature deprecation or removal.
* ``deprecation``: feature deprecation.
* ``removal``: feature removal.
* ``vendor``: changes in packages vendored in pytest.
* ``trivial``: fixing a small typo or internal change that might be noteworthy.

Expand Down
10 changes: 10 additions & 0 deletions doc/en/historical-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,13 @@ Previous to version 2.4 to set a break point in code one needed to use ``pytest.
This is no longer needed and one can use the native ``import pdb;pdb.set_trace()`` call directly.

For more details see :ref:`breakpoints`.

"compat" properties
-------------------

.. deprecated:: 3.9

Access of ``Module``, ``Function``, ``Class``, ``Instance``, ``File`` and ``Item`` through ``Node`` instances have long
been documented as deprecated, but started to emit warnings from pytest ``3.9`` and onward.

Users should just ``import pytest`` and access those objects using the ``pytest`` module.
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ template = "changelog/_template.rst"

[[tool.towncrier.type]]
directory = "removal"
name = "Deprecations and Removals"
name = "Removals"
showcontent = true

[[tool.towncrier.type]]
directory = "deprecation"
name = "Deprecations"
showcontent = true

[[tool.towncrier.type]]
Expand Down
9 changes: 3 additions & 6 deletions src/_pytest/config/findpaths.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,21 +103,18 @@ def determine_setup(inifile, args, rootdir_cmd_arg=None, config=None):
if inifile:
iniconfig = py.iniconfig.IniConfig(inifile)
is_cfg_file = str(inifile).endswith(".cfg")
# TODO: [pytest] section in *.cfg files is depricated. Need refactoring.
sections = ["tool:pytest", "pytest"] if is_cfg_file else ["pytest"]
for section in sections:
try:
inicfg = iniconfig[section]
if is_cfg_file and section == "pytest" and config is not None:
from _pytest.deprecated import CFG_PYTEST_SECTION
from _pytest.warning_types import RemovedInPytest4Warning
from _pytest.warnings import _issue_config_warning

# TODO: [pytest] section in *.cfg files is deprecated. Need refactoring once
# the deprecation expires.
_issue_config_warning(
RemovedInPytest4Warning(
CFG_PYTEST_SECTION.format(filename=str(inifile))
),
config,
CFG_PYTEST_SECTION.format(filename=str(inifile)), config
)
break
except KeyError:
Expand Down
46 changes: 37 additions & 9 deletions src/_pytest/deprecated.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@

Keeping it in a central location makes it easy to track what is deprecated and should
be removed when the time comes.

All constants defined in this module should be either PytestWarning instances or UnformattedWarning
in case of warnings which need to format their messages.
"""
from __future__ import absolute_import, division, print_function

from _pytest.warning_types import RemovedInPytest4Warning

from _pytest.warning_types import UnformattedWarning, RemovedInPytest4Warning


MAIN_STR_ARGS = RemovedInPytest4Warning(
"passing a string to pytest.main() is deprecated, "
Expand All @@ -18,25 +23,48 @@
"yield tests are deprecated, and scheduled to be removed in pytest 4.0"
)

FUNCARG_PREFIX = (
CACHED_SETUP = RemovedInPytest4Warning(
"cached_setup is deprecated and will be removed in a future release. "
"Use standard fixture functions instead."
)

COMPAT_PROPERTY = UnformattedWarning(
RemovedInPytest4Warning,
"usage of {owner}.{name} is deprecated, please use pytest.{name} instead",
)

CUSTOM_CLASS = UnformattedWarning(
RemovedInPytest4Warning,
'use of special named "{name}" objects in collectors of type "{type_name}" to '
"customize the created nodes is deprecated. "
"Use pytest_pycollect_makeitem(...) to create custom "
"collection nodes instead.",
)

FUNCARG_PREFIX = UnformattedWarning(
RemovedInPytest4Warning,
'{name}: declaring fixtures using "pytest_funcarg__" prefix is deprecated '
"and scheduled to be removed in pytest 4.0. "
"Please remove the prefix and use the @pytest.fixture decorator instead."
"Please remove the prefix and use the @pytest.fixture decorator instead.",
)

FIXTURE_FUNCTION_CALL = (
FIXTURE_FUNCTION_CALL = UnformattedWarning(
RemovedInPytest4Warning,
'Fixture "{name}" called directly. Fixtures are not meant to be called directly, '
"are created automatically when test functions request them as parameters. "
"See https://docs.pytest.org/en/latest/fixture.html for more information."
"See https://docs.pytest.org/en/latest/fixture.html for more information.",
)

CFG_PYTEST_SECTION = (
"[pytest] section in {filename} files is deprecated, use [tool:pytest] instead."
CFG_PYTEST_SECTION = UnformattedWarning(
RemovedInPytest4Warning,
"[pytest] section in {filename} files is deprecated, use [tool:pytest] instead.",
)

GETFUNCARGVALUE = "getfuncargvalue is deprecated, use getfixturevalue"
GETFUNCARGVALUE = RemovedInPytest4Warning(
"getfuncargvalue is deprecated, use getfixturevalue"
)

RESULT_LOG = (
RESULT_LOG = RemovedInPytest4Warning(
"--result-log is deprecated and scheduled for removal in pytest 4.0.\n"
"See https://docs.pytest.org/en/latest/usage.html#creating-resultlog-format-files for more information."
)
Expand Down
16 changes: 9 additions & 7 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
get_real_method,
_PytestWrapper,
)
from _pytest.deprecated import FIXTURE_FUNCTION_CALL, RemovedInPytest4Warning
from _pytest.deprecated import FIXTURE_FUNCTION_CALL
from _pytest.outcomes import fail, TEST_OUTCOME

FIXTURE_MSG = 'fixtures cannot have "pytest_funcarg__" prefix and be decorated with @pytest.fixture:\n{}'
Expand Down Expand Up @@ -479,6 +479,9 @@ def cached_setup(self, setup, teardown=None, scope="module", extrakey=None):
or ``session`` indicating the caching lifecycle of the resource.
:arg extrakey: added to internal caching key of (funcargname, scope).
"""
from _pytest.deprecated import CACHED_SETUP

warnings.warn(CACHED_SETUP, stacklevel=2)
if not hasattr(self.config, "_setupcache"):
self.config._setupcache = {} # XXX weakref?
cachekey = (self.fixturename, self._getscopeitem(scope), extrakey)
Expand Down Expand Up @@ -512,7 +515,7 @@ def getfuncargvalue(self, argname):
""" Deprecated, use getfixturevalue. """
from _pytest import deprecated

warnings.warn(deprecated.GETFUNCARGVALUE, DeprecationWarning, stacklevel=2)
warnings.warn(deprecated.GETFUNCARGVALUE, stacklevel=2)
return self.getfixturevalue(argname)

def _get_active_fixturedef(self, argname):
Expand Down Expand Up @@ -956,8 +959,9 @@ def wrap_function_to_warning_if_called_directly(function, fixture_marker):
used as an argument in a test function.
"""
is_yield_function = is_generator(function)
msg = FIXTURE_FUNCTION_CALL.format(name=fixture_marker.name or function.__name__)
warning = RemovedInPytest4Warning(msg)
warning = FIXTURE_FUNCTION_CALL.format(
name=fixture_marker.name or function.__name__
)

if is_yield_function:

Expand Down Expand Up @@ -1284,9 +1288,7 @@ def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):

filename, lineno = getfslineno(obj)
warnings.warn_explicit(
RemovedInPytest4Warning(
deprecated.FUNCARG_PREFIX.format(name=name)
),
deprecated.FUNCARG_PREFIX.format(name=name),
category=None,
filename=str(filename),
lineno=lineno + 1,
Expand Down
17 changes: 8 additions & 9 deletions src/_pytest/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ def __get__(self, obj, owner):
if obj is None:
return self

# TODO: reenable in the features branch
# warnings.warn(
# "usage of {owner!r}.{name} is deprecated, please use pytest.{name} instead".format(
# name=self.name, owner=type(owner).__name__),
# PendingDeprecationWarning, stacklevel=2)
from _pytest.deprecated import COMPAT_PROPERTY

warnings.warn(
COMPAT_PROPERTY.format(name=self.name, owner=owner.__name__), stacklevel=2
)
return getattr(__import__("pytest"), self.name)


Expand Down Expand Up @@ -126,11 +126,10 @@ def _getcustomclass(self, name):
if isinstance(maybe_compatprop, _CompatProperty):
return getattr(__import__("pytest"), name)
else:
from _pytest.deprecated import CUSTOM_CLASS

cls = getattr(self, name)
# TODO: reenable in the features branch
# warnings.warn("use of node.%s is deprecated, "
# "use pytest_pycollect_makeitem(...) to create custom "
# "collection nodes" % name, category=DeprecationWarning)
self.warn(CUSTOM_CLASS.format(name=name, type_name=type(self).__name__))
return cls

def __repr__(self):
Expand Down
5 changes: 4 additions & 1 deletion src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,10 @@ def collect(self):
"%r generated tests with non-unique name %r" % (self, name)
)
seen[name] = True
values.append(self.Function(name, self, args=args, callobj=call))
with warnings.catch_warnings():
# ignore our own deprecation warning
function_class = self.Function
values.append(function_class(name, self, args=args, callobj=call))
self.warn(deprecated.YIELD_TESTS)
return values

Expand Down
3 changes: 1 addition & 2 deletions src/_pytest/resultlog.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,9 @@ def pytest_configure(config):
config.pluginmanager.register(config._resultlog)

from _pytest.deprecated import RESULT_LOG
from _pytest.warning_types import RemovedInPytest4Warning
from _pytest.warnings import _issue_config_warning

_issue_config_warning(RemovedInPytest4Warning(RESULT_LOG), config)
_issue_config_warning(RESULT_LOG, config)


def pytest_unconfigure(config):
Expand Down
18 changes: 18 additions & 0 deletions src/_pytest/warning_types.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import attr


class PytestWarning(UserWarning):
"""
Bases: :class:`UserWarning`.
Expand Down Expand Up @@ -39,4 +42,19 @@ def simple(cls, apiname):
)


@attr.s
class UnformattedWarning(object):
"""Used to hold warnings that need to format their message at runtime, as opposed to a direct message.

Using this class avoids to keep all the warning types and messages in this module, avoiding misuse.
"""

category = attr.ib()
template = attr.ib()

def format(self, **kwargs):
"""Returns an instance of the warning category, formatted with given kwargs"""
return self.category(self.template.format(**kwargs))


PYTESTER_COPY_EXAMPLE = PytestExperimentalApiWarning.simple("testdir.copy_example")
68 changes: 68 additions & 0 deletions testing/deprecated_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,74 @@ def test_gen2():
assert result.stdout.str().count("yield tests are deprecated") == 2


def test_compat_properties_deprecation(testdir):
testdir.makepyfile(
"""
def test_foo(request):
print(request.node.Module)
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(
[
"*test_compat_properties_deprecation.py:2:*usage of Function.Module is deprecated, "
"please use pytest.Module instead*",
"*1 passed, 1 warnings in*",
]
)


def test_cached_setup_deprecation(testdir):
testdir.makepyfile(
"""
import pytest
@pytest.fixture
def fix(request):
return request.cached_setup(lambda: 1)

def test_foo(fix):
assert fix == 1
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(
[
"*test_cached_setup_deprecation.py:4:*cached_setup is deprecated*",
"*1 passed, 1 warnings in*",
]
)


def test_custom_class_deprecation(testdir):
testdir.makeconftest(
"""
import pytest

class MyModule(pytest.Module):

class Class(pytest.Class):
pass

def pytest_pycollect_makemodule(path, parent):
return MyModule(path, parent)
"""
)
testdir.makepyfile(
"""
class Test:
def test_foo(self):
pass
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(
[
'*test_custom_class_deprecation.py:1:*"Class" objects in collectors of type "MyModule*',
"*1 passed, 1 warnings in*",
]
)


@pytest.mark.filterwarnings("default")
def test_funcarg_prefix_deprecation(testdir):
testdir.makepyfile(
Expand Down
6 changes: 6 additions & 0 deletions testing/python/collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ def prop(self):
assert result.ret == EXIT_NOTESTSCOLLECTED


@pytest.mark.filterwarnings(
"ignore:usage of Generator.Function is deprecated, please use pytest.Function instead"
)
class TestGenerator(object):
def test_generative_functions(self, testdir):
modcol = testdir.getmodulecol(
Expand Down Expand Up @@ -1255,6 +1258,9 @@ def test_hello(self): pass
assert lineno == 1
assert msg == "TestClass"

@pytest.mark.filterwarnings(
"ignore:usage of Generator.Function is deprecated, please use pytest.Function instead"
)
def test_generator_reportinfo(self, testdir):
modcol = testdir.getmodulecol(
"""
Expand Down
Loading