Skip to content

Commit

Permalink
remove yield tests and compat properties
Browse files Browse the repository at this point in the history
  • Loading branch information
RonnyPfannschmidt committed Nov 20, 2018
1 parent 6e85feb commit e8cc371
Show file tree
Hide file tree
Showing 18 changed files with 16 additions and 682 deletions.
1 change: 1 addition & 0 deletions changelog/3079.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove support for yield tests - they are fundamentally broken since collection and test execution were separated.
1 change: 1 addition & 0 deletions changelog/3616.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove the informally deprecated compat properties for node.Class/Function/Module - use pytest... now.
1 change: 0 additions & 1 deletion src/_pytest/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,6 @@ def safe_str(v):
COLLECT_FAKEMODULE_ATTRIBUTES = (
"Collector",
"Module",
"Generator",
"Function",
"Instance",
"Session",
Expand Down
15 changes: 2 additions & 13 deletions src/_pytest/deprecated.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,13 @@
"yield tests are deprecated, and scheduled to be removed in pytest 4.0"
)

YIELD_TESTS = "yield tests were removed in pytest 4.0 - {name} will be ignored"

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 '
Expand Down
6 changes: 0 additions & 6 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -1303,17 +1303,11 @@ def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
if holderobj in self._holderobjseen:
return

from _pytest.nodes import _CompatProperty

self._holderobjseen.add(holderobj)
autousenames = []
for name in dir(holderobj):
# The attribute can be an arbitrary descriptor, so the attribute
# access below can raise. safe_getatt() ignores such exceptions.
maybe_property = safe_getattr(type(holderobj), name, None)
if isinstance(maybe_property, _CompatProperty):
# deprecated
continue
obj = safe_getattr(holderobj, name, None)
marker = getfixturemarker(obj)
# fixture functions have a pytest_funcarg__ prefix (pre-2.3 style)
Expand Down
35 changes: 0 additions & 35 deletions src/_pytest/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import os
import warnings

import attr
import py
import six

Expand Down Expand Up @@ -56,22 +55,6 @@ def ischildnode(baseid, nodeid):
return node_parts[: len(base_parts)] == base_parts


@attr.s
class _CompatProperty(object):
name = attr.ib()

def __get__(self, obj, owner):
if obj is None:
return self

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)


class Node(object):
""" base class for Collector and Item the test collection tree.
Collector subclasses have children, Items are terminal nodes."""
Expand Down Expand Up @@ -119,24 +102,6 @@ def ihook(self):
""" fspath sensitive hook proxy used to call pytest hooks"""
return self.session.gethookproxy(self.fspath)

Module = _CompatProperty("Module")
Class = _CompatProperty("Class")
Instance = _CompatProperty("Instance")
Function = _CompatProperty("Function")
File = _CompatProperty("File")
Item = _CompatProperty("Item")

def _getcustomclass(self, name):
maybe_compatprop = getattr(type(self), name)
if isinstance(maybe_compatprop, _CompatProperty):
return getattr(__import__("pytest"), name)
else:
from _pytest.deprecated import CUSTOM_CLASS

cls = getattr(self, name)
self.warn(CUSTOM_CLASS.format(name=name, type_name=type(self).__name__))
return cls

def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, getattr(self, "name", None))

Expand Down
12 changes: 0 additions & 12 deletions src/_pytest/nose.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,6 @@ def pytest_runtest_makereport(item, call):
@hookimpl(trylast=True)
def pytest_runtest_setup(item):
if is_potential_nosetest(item):
if isinstance(item.parent, python.Generator):
gen = item.parent
if not hasattr(gen, "_nosegensetup"):
call_optional(gen.obj, "setup")
if isinstance(gen.parent, python.Instance):
call_optional(gen.parent.obj, "setup")
gen._nosegensetup = True
if not call_optional(item.obj, "setup"):
# call module level setup if there is no object level one
call_optional(item.parent.obj, "setup")
Expand All @@ -53,11 +46,6 @@ def teardown_nose(item):
# del item.parent._nosegensetup


def pytest_make_collect_report(collector):
if isinstance(collector, python.Generator):
call_optional(collector.obj, "setup")


def is_potential_nosetest(item):
# extra check needed since we do not do nose style setup/teardown
# on direct unittest style classes
Expand Down
64 changes: 11 additions & 53 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from _pytest.compat import STRING_TYPES
from _pytest.config import hookimpl
from _pytest.main import FSHookProxy
from _pytest.mark import MARK_GEN
from _pytest.mark.structures import get_unpacked_marks
from _pytest.mark.structures import normalize_mark_list
from _pytest.mark.structures import transfer_markers
Expand Down Expand Up @@ -199,7 +200,6 @@ def pytest_pycollect_makeitem(collector, name, obj):
# nothing was collected elsewhere, let's do it here
if safe_isclass(obj):
if collector.istestclass(obj, name):
Class = collector._getcustomclass("Class")
outcome.force_result(Class(name, parent=collector))
elif collector.istestfunction(obj, name):
# mock seems to store unbound methods (issue473), normalize it
Expand All @@ -219,7 +219,12 @@ def pytest_pycollect_makeitem(collector, name, obj):
)
elif getattr(obj, "__test__", True):
if is_generator(obj):
res = Generator(name, parent=collector)
res = Function(name, parent=collector)
res.add_marker(
MARK_GEN.xfail(
run=False, reason=deprecated.YIELD_TESTS.format(name=name)
)
)
else:
res = list(collector._genfunctions(name, obj))
outcome.force_result(res)
Expand Down Expand Up @@ -408,7 +413,6 @@ def _genfunctions(self, name, funcobj):
else:
self.ihook.pytest_generate_tests(metafunc=metafunc)

Function = self._getcustomclass("Function")
if not metafunc._calls:
yield Function(name, parent=self, fixtureinfo=fixtureinfo)
else:
Expand Down Expand Up @@ -648,7 +652,7 @@ def collect(self):
)
)
return []
return [self._getcustomclass("Instance")(name="()", parent=self)]
return [Instance(name="()", parent=self)]

def setup(self):
setup_class = _get_xunit_func(self.obj, "setup_class")
Expand Down Expand Up @@ -739,51 +743,6 @@ def repr_failure(self, excinfo, outerr=None):
return self._repr_failure_py(excinfo, style=style)


class Generator(FunctionMixin, PyCollector):
def collect(self):

# test generators are seen as collectors but they also
# invoke setup/teardown on popular request
# (induced by the common "test_*" naming shared with normal tests)
from _pytest import deprecated

self.warn(deprecated.YIELD_TESTS)

self.session._setupstate.prepare(self)
# see FunctionMixin.setup and test_setupstate_is_preserved_134
self._preservedparent = self.parent.obj
values = []
seen = {}
_Function = self._getcustomclass("Function")
for i, x in enumerate(self.obj()):
name, call, args = self.getcallargs(x)
if not callable(call):
raise TypeError("%r yielded non callable test %r" % (self.obj, call))
if name is None:
name = "[%d]" % i
else:
name = "['%s']" % name
if name in seen:
raise ValueError(
"%r generated tests with non-unique name %r" % (self, name)
)
seen[name] = True
values.append(_Function(name, self, args=args, callobj=call))
return values

def getcallargs(self, obj):
if not isinstance(obj, (tuple, list)):
obj = (obj,)
# explicit naming
if isinstance(obj[0], six.string_types):
name = obj[0]
obj = obj[1:]
else:
name = None
call, args = obj[0], obj[1:]
return name, call, args


def hasinit(obj):
init = getattr(obj, "__init__", None)
if init:
Expand Down Expand Up @@ -1326,20 +1285,19 @@ def _showfixtures_main(config, session):
tw.line(" %s: no docstring available" % (loc,), red=True)


def write_docstring(tw, doc):
INDENT = " "
def write_docstring(tw, doc, indent=" "):
doc = doc.rstrip()
if "\n" in doc:
firstline, rest = doc.split("\n", 1)
else:
firstline, rest = doc, ""

if firstline.strip():
tw.line(INDENT + firstline.strip())
tw.line(indent + firstline.strip())

if rest:
for line in dedent(rest).split("\n"):
tw.write(INDENT + line + "\n")
tw.write(indent + line + "\n")


class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
Expand Down
2 changes: 0 additions & 2 deletions src/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from _pytest.outcomes import xfail
from _pytest.python import Class
from _pytest.python import Function
from _pytest.python import Generator
from _pytest.python import Instance
from _pytest.python import Module
from _pytest.python import Package
Expand Down Expand Up @@ -57,7 +56,6 @@
"fixture",
"freeze_includes",
"Function",
"Generator",
"hookimpl",
"hookspec",
"importorskip",
Expand Down
71 changes: 0 additions & 71 deletions testing/deprecated_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,6 @@
pytestmark = pytest.mark.pytester_example_path("deprecated")


def test_yield_tests_deprecation(testdir):
testdir.makepyfile(
"""
def func1(arg, arg2):
assert arg == arg2
def test_gen():
yield "m1", func1, 15, 3*5
yield "m2", func1, 42, 6*7
def test_gen2():
for k in range(10):
yield func1, 1, 1
"""
)
result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG)
result.stdout.fnmatch_lines(
[
"*test_yield_tests_deprecation.py:3:*yield tests are deprecated*",
"*test_yield_tests_deprecation.py:6:*yield tests are deprecated*",
"*2 passed*",
]
)
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(SHOW_PYTEST_WARNINGS_ARG)
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(
"""
Expand All @@ -72,36 +31,6 @@ def test_foo(fix):
)


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(SHOW_PYTEST_WARNINGS_ARG)
result.stdout.fnmatch_lines(
[
'*test_custom_class_deprecation.py:1:*"Class" objects in collectors of type "MyModule*',
"*1 passed, 1 warnings in*",
]
)


def test_funcarg_prefix_deprecation(testdir):
testdir.makepyfile(
"""
Expand Down
Loading

0 comments on commit e8cc371

Please sign in to comment.