Skip to content

Commit

Permalink
Emit deprecation warning + deprecation doc + changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
bluetech committed Nov 13, 2021
1 parent 9a57106 commit aef6ea3
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 7 deletions.
3 changes: 3 additions & 0 deletions changelog/9277.breaking.rst
@@ -0,0 +1,3 @@
The ``pytest.Instance`` collector type has been removed.
Importing ``pytest.Instance`` or ``_pytest.python.Instance`` returns a dummy type and emits a deprecation warning.
See :ref:`instance-collector-deprecation` for details.
19 changes: 19 additions & 0 deletions doc/en/deprecations.rst
Expand Up @@ -18,6 +18,25 @@ Deprecated Features
Below is a complete list of all pytest features which are considered deprecated. Using those features will issue
:class:`PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.

.. _instance-collector-deprecation:

The ``pytest.Instance`` collector
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. versionremoved:: 7.0

The ``pytest.Instance`` collector type has been removed.

Previously, Python test methods were collected as :class:`~pytest.Class` -> ``Instance`` -> :class:`~pytest.Function`.
Now :class:`~pytest.Class` collects the test methods directly.

Most plugins which reference ``Instance`` do so in order to ignore or skip it,
using a check such as ``if isinstance(node, Instance): return``.
Such plugins should simply remove consideration of ``Instance`` on pytest>=7.
However, to keep such uses working, a dummy type has been instanted in ``pytest.Instance`` and ``_pytest.python.Instance``,
and importing it emits a deprecation warning. This will be removed in pytest 8.


.. _node-ctor-fspath-deprecation:

``fspath`` argument for Node constructors replaced with ``pathlib.Path``
Expand Down
5 changes: 5 additions & 0 deletions src/_pytest/deprecated.py
Expand Up @@ -119,6 +119,11 @@
"pytest.{func}(msg=...) is now deprecated, use pytest.{func}(reason=...) instead",
)

INSTANCE_COLLECTOR = PytestDeprecationWarning(
"The pytest.Instance collector type is deprecated and is no longer used. "
"See https://docs.pytest.org/en/latest/deprecations.html#the-pytest-instance-collector",
)

# You want to make some `__init__` or function "private".
#
# def my_private_function(some, args):
Expand Down
21 changes: 16 additions & 5 deletions src/_pytest/python.py
Expand Up @@ -58,6 +58,7 @@
from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
from _pytest.deprecated import INSTANCE_COLLECTOR
from _pytest.fixtures import FuncFixtureInfo
from _pytest.main import Session
from _pytest.mark import MARK_GEN
Expand Down Expand Up @@ -868,14 +869,24 @@ def xunit_setup_method_fixture(self, request) -> Generator[None, None, None]:
self.obj.__pytest_setup_method = xunit_setup_method_fixture


# Instance used to be a node type between Class and Function. It has been
# removed in pytest 7.0. Some plugins exist which reference `pytest.Instance`
# only to ignore it; this dummy class keeps them working. This could probably
# be removed at some point.
class Instance:
class InstanceDummy:
"""Instance used to be a node type between Class and Function. It has been
removed in pytest 7.0. Some plugins exist which reference `pytest.Instance`
only to ignore it; this dummy class keeps them working. This will be removed
in pytest 8."""

pass


# Note: module __getattr__ only works on Python>=3.7. Unfortunately
# we can't provide this deprecation warning on Python 3.6.
def __getattr__(name: str) -> object:
if name == "Instance":
warnings.warn(INSTANCE_COLLECTOR, 2)
return InstanceDummy
raise AttributeError(f"module {__name__} has no attribute {name}")


def hasinit(obj: object) -> bool:
init: object = getattr(obj, "__init__", None)
if init:
Expand Down
12 changes: 10 additions & 2 deletions src/pytest/__init__.py
Expand Up @@ -48,7 +48,6 @@
from _pytest.pytester import RunResult
from _pytest.python import Class
from _pytest.python import Function
from _pytest.python import Instance
from _pytest.python import Metafunc
from _pytest.python import Module
from _pytest.python import Package
Expand Down Expand Up @@ -77,6 +76,7 @@

set_trace = __pytestPDB.set_trace


__all__ = [
"__version__",
"_fillfuncargs",
Expand Down Expand Up @@ -106,7 +106,6 @@
"HookRecorder",
"hookspec",
"importorskip",
"Instance",
"Item",
"LineMatcher",
"LogCaptureFixture",
Expand Down Expand Up @@ -153,3 +152,12 @@
"xfail",
"yield_fixture",
]


def __getattr__(name: str) -> object:
if name == "Instance":
# The import emits a deprecation warning.
from _pytest.python import Instance

return Instance
raise AttributeError(f"module {__name__} has no attribute {name}")
18 changes: 18 additions & 0 deletions testing/deprecated_test.py
Expand Up @@ -286,3 +286,21 @@ def test_node_ctor_fspath_argument_is_deprecated(pytester: Pytester) -> None:
parent=mod.parent,
fspath=legacy_path("bla"),
)


@pytest.mark.skipif(
sys.version_info < (3, 7),
reason="This deprecation can only be emitted on python>=3.7",
)
def test_importing_instance_is_deprecated(pytester: Pytester) -> None:
with pytest.warns(
pytest.PytestDeprecationWarning,
match=re.escape("The pytest.Instance collector type is deprecated"),
):
pytest.Instance

with pytest.warns(
pytest.PytestDeprecationWarning,
match=re.escape("The pytest.Instance collector type is deprecated"),
):
from _pytest.python import Instance # noqa: F401

0 comments on commit aef6ea3

Please sign in to comment.