Skip to content

Commit

Permalink
Revert "Node from parent (pytest-dev#5975)"
Browse files Browse the repository at this point in the history
This reverts commit a3c8246, reversing
changes made to 886b8d2.

Ref: pytest-dev#5975 (comment)

Conflicts:
	src/_pytest/main.py
	src/_pytest/unittest.py

Squashed:

7c4b8a0  Revert "Node from parent (pytest-dev#5975)"
89abdb9  keep test, warning
c9062ed  warn via NodeMeta
149bddb  keep/adjust testing/test_mark.py:1013::test_addmarker_order
de91222  tests: handle DefinitionMock
9e3b17f  fix testing/test_collection.py:453::TestSession::test_parsearg

Conflicts:

    both modified:   src/_pytest/nodes.py
    both modified:   src/_pytest/python.py
  • Loading branch information
blueyed committed Feb 24, 2020
1 parent 13b7f5a commit d98dca3
Show file tree
Hide file tree
Showing 15 changed files with 55 additions and 79 deletions.
6 changes: 0 additions & 6 deletions changelog/5975.deprecation.rst

This file was deleted.

10 changes: 0 additions & 10 deletions doc/en/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,6 @@ Below is a complete list of all pytest features which are considered deprecated.
:class:`_pytest.warning_types.PytestWarning` or subclasses, which can be filtered using
:ref:`standard warning filters <warnings>`.


Node Construction changed to ``Node.from_parent``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. deprecated:: 5.3

The construction of nodes new should use the named constructor ``from_parent``.
This limitation in api surface intends to enable better/simpler refactoring of the collection tree.


``junit_family`` default value change to "xunit2"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
4 changes: 2 additions & 2 deletions doc/en/example/nonpython/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

def pytest_collect_file(parent, path):
if path.ext == ".yaml" and path.basename.startswith("test"):
return YamlFile.from_parent(parent, fspath=path)
return YamlFile(path, parent)


class YamlFile(pytest.File):
Expand All @@ -13,7 +13,7 @@ def collect(self):

raw = yaml.safe_load(self.fspath.open())
for name, spec in sorted(raw.items()):
yield YamlItem.from_parent(self, name=name, spec=spec)
yield YamlItem(name, self, spec)


class YamlItem(pytest.Item):
Expand Down
16 changes: 4 additions & 12 deletions src/_pytest/doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ def pytest_collect_file(path, parent):
config = parent.config
if path.ext == ".py":
if config.option.doctestmodules and not _is_setup_py(config, path, parent):
return DoctestModule.from_parent(parent, fspath=path)
return DoctestModule(path, parent)
elif _is_doctest(config, path, parent):
return DoctestTextfile.from_parent(parent, fspath=path)
return DoctestTextfile(path, parent)


def _is_setup_py(config, path, parent):
Expand Down Expand Up @@ -220,10 +220,6 @@ def __init__(self, name, parent, runner=None, dtest=None):
self.obj = None
self.fixture_request = None

@classmethod
def from_parent(cls, parent, *, name, runner, dtest):
return cls._create(name=name, parent=parent, runner=runner, dtest=dtest)

def setup(self):
if self.dtest is not None:
self.fixture_request = _setup_fixtures(self)
Expand Down Expand Up @@ -382,9 +378,7 @@ def collect(self):
parser = doctest.DocTestParser()
test = parser.get_doctest(text, globs, name, filename, 0)
if test.examples:
yield DoctestItem.from_parent(
self, name=test.name, runner=runner, dtest=test
)
yield DoctestItem(test.name, self, runner, test)


def _check_all_skipped(test):
Expand Down Expand Up @@ -493,9 +487,7 @@ def _find(

for test in finder.find(module, module.__name__):
if test.examples: # skip empty doctests
yield DoctestItem.from_parent(
self, name=test.name, runner=runner, dtest=test
)
yield DoctestItem(test.name, self, runner, test)


def _setup_fixtures(doctest_item):
Expand Down
6 changes: 1 addition & 5 deletions src/_pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def wrap_session(
config: Config, doit: Callable[[Config, "Session"], Optional[Union[int, ExitCode]]]
) -> Union[int, ExitCode]:
"""Skeleton command line program"""
session = Session.from_config(config)
session = Session(config)
session.exitstatus = ExitCode.OK
initstate = 0
try:
Expand Down Expand Up @@ -424,10 +424,6 @@ def __init__(self, config: Config) -> None:

self._deselected = [] # type: List[nodes.Item]

@classmethod
def from_config(cls, config):
return cls._create(config)

def __repr__(self):
return "<%s %s exitstatus=%r testsfailed=%d testscollected=%d>" % (
self.__class__.__name__,
Expand Down
30 changes: 21 additions & 9 deletions src/_pytest/nodes.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import inspect
import os
import warnings
from functools import lru_cache
Expand Down Expand Up @@ -77,12 +78,27 @@ def ischildnode(baseid, nodeid):


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)
def __call__(cls, *args, **kwargs):
from _pytest.main import Session # noqa: F811

def _create(self, *k, **kw):
return super().__call__(*k, **kw)
sig = inspect.signature(cls.__init__)
bsig = sig.bind(cls, *args, **kwargs)

if cls is Session:
# from_config
assert isinstance(bsig.arguments["config"], Config), (args, kwargs)
assert len(bsig.arguments) == 2, bsig # self, config
else:
# from_parent
# TODO: assert hierarchy?
if "parent" not in bsig.arguments:
# XXX: handle DefinitionMock from tests. Could also go through
# _create instead (or get fixed?)?
if not getattr(cls, "_pytest_skip_ctor_check", False):
warnings.warn(
NODE_USE_FROM_PARENT.format(name=cls.__name__), stacklevel=2
)
return super().__call__(*args, **kwargs)


class Node(metaclass=NodeMeta):
Expand Down Expand Up @@ -145,10 +161,6 @@ def __init__(
if self.name != "()":
self._nodeid += "::" + self.name

@classmethod
def from_parent(cls, parent, *, name):
return cls._create(parent=parent, name=name)

@property
def ihook(self):
""" fspath sensitive hook proxy used to call pytest hooks"""
Expand Down
4 changes: 2 additions & 2 deletions src/_pytest/pytester.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ def getnode(self, config, arg):
:param arg: a :py:class:`py.path.local` instance of the file
"""
session = Session.from_config(config)
session = Session(config)
assert "::" not in str(arg)
p = py.path.local(arg)
config.hook.pytest_sessionstart(session=session)
Expand All @@ -825,7 +825,7 @@ def getpathnode(self, path):
"""
config = self.parseconfigure(path)
session = Session.from_config(config)
session = Session(config)
x = session.fspath.bestrelpath(path)
config.hook.pytest_sessionstart(session=session)
res = session.perform_collect([x], genitems=False)[0]
Expand Down
28 changes: 10 additions & 18 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ def path_matches_patterns(path, patterns):

def pytest_pycollect_makemodule(path, parent):
if path.basename == "__init__.py":
return Package.from_parent(parent, fspath=path)
return Module.from_parent(parent, fspath=path)
return Package(path, parent)
return Module(path, parent)


@hookimpl(hookwrapper=True)
Expand All @@ -209,7 +209,7 @@ 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):
outcome.force_result(Class.from_parent(collector, name=name, obj=obj))
outcome.force_result(Class(name, parent=collector))
elif collector.istestfunction(obj, name):
# mock seems to store unbound methods (issue473), normalize it
obj = getattr(obj, "__func__", obj)
Expand All @@ -228,7 +228,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
)
elif getattr(obj, "__test__", True):
if is_generator(obj):
res = Function.from_parent(collector, name=name)
res = Function(name, parent=collector)
reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format(
name=name
)
Expand Down Expand Up @@ -395,7 +395,7 @@ def _genfunctions(self, name, funcobj):
cls = clscol and clscol.obj or None
fm = self.session._fixturemanager

definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj)
definition = FunctionDefinition(parent=self, name=name, callobj=funcobj)
fixtureinfo = definition._fixtureinfo

metafunc = Metafunc(
Expand All @@ -410,7 +410,7 @@ def _genfunctions(self, name, funcobj):
self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc))

if not metafunc._calls:
yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo)
yield Function(name, parent=self, fixtureinfo=fixtureinfo)
else:
# add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs
fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm)
Expand All @@ -422,9 +422,9 @@ def _genfunctions(self, name, funcobj):

for callspec in metafunc._calls:
subname = "{}[{}]".format(name, callspec.id)
yield Function.from_parent(
self,
yield Function(
name=subname,
parent=self,
callspec=callspec,
callobj=funcobj,
fixtureinfo=fixtureinfo,
Expand Down Expand Up @@ -624,7 +624,7 @@ def collect(self):
if init_module.check(file=1) and path_matches_patterns(
init_module, self.config.getini("python_files")
):
yield Module.from_parent(self, fspath=init_module)
yield Module(init_module, self)
pkg_prefixes = set()
for path in this_path.visit(rec=self._recurse, bf=True, sort=True):
# We will visit our own __init__.py file, in which case we skip it.
Expand Down Expand Up @@ -675,10 +675,6 @@ def _get_first_non_fixture_func(obj, names):
class Class(PyCollector):
""" Collector for test methods. """

@classmethod
def from_parent(cls, parent, *, name, obj=None):
return cls._create(name=name, parent=parent)

def collect(self):
if not safe_getattr(self.obj, "__test__", True):
return []
Expand All @@ -704,7 +700,7 @@ def collect(self):
self._inject_setup_class_fixture()
self._inject_setup_method_fixture()

return [Instance.from_parent(self, name="()")]
return [Instance(name="()", parent=self)]

def _inject_setup_class_fixture(self):
"""Injects a hidden autouse, class scoped fixture into the collected class object
Expand Down Expand Up @@ -1467,10 +1463,6 @@ def __init__(
#: .. versionadded:: 3.0
self.originalname = originalname

@classmethod
def from_parent(cls, parent, **kw):
return cls._create(parent=parent, **kw)

def _initrequest(self):
self.funcargs = {}
self._request = fixtures.FixtureRequest(self)
Expand Down
7 changes: 3 additions & 4 deletions src/_pytest/unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
except Exception:
return
# yes, so let's collect it
return UnitTestCase.from_parent(collector, name=name, obj=obj)
return UnitTestCase(name, parent=collector)


class UnitTestCase(Class):
Expand Down Expand Up @@ -52,16 +52,15 @@ def collect(self):
if not getattr(x, "__test__", True):
continue
funcobj = getimfunc(x)
yield TestCaseFunction.from_parent(self, name=name, callobj=funcobj)
yield TestCaseFunction(name, parent=self, callobj=funcobj)
foundsomething = True

if not foundsomething:
runtest = getattr(self.obj, "runTest", None)
if runtest is not None:
ut = sys.modules.get("twisted.trial.unittest", None)
if ut is None or runtest != ut.TestCase.runTest:
# TODO: callobj consistency
yield TestCaseFunction.from_parent(self, name="runTest")
yield TestCaseFunction("runTest", parent=self)

def _inject_setup_teardown_fixtures(self, cls):
"""Injects a hidden auto-use fixture to invoke setUpClass/setup_method and corresponding
Expand Down
6 changes: 3 additions & 3 deletions testing/python/collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,10 @@ def make_function(testdir, **kwargs):
from _pytest.fixtures import FixtureManager

config = testdir.parseconfigure()
session = testdir.Session.from_config(config)
session = testdir.Session(config)
session._fixturemanager = FixtureManager(session)

return pytest.Function.from_parent(config=config, parent=session, **kwargs)
return pytest.Function(config=config, parent=session, **kwargs)

def test_function_equality(self, testdir):
def func1():
Expand Down Expand Up @@ -1025,7 +1025,7 @@ def reportinfo(self):
return "ABCDE", 42, "custom"
def pytest_pycollect_makeitem(collector, name, obj):
if name == "test_func":
return MyFunction.from_parent(name=name, parent=collector)
return MyFunction(name, parent=collector)
"""
)
item = testdir.getitem("def test_func(): pass")
Expand Down
4 changes: 2 additions & 2 deletions testing/python/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage
import pytest
def pytest_pycollect_makeitem(collector, name, obj):
if name == "MyClass":
return MyCollector.from_parent(collector, name=name)
return MyCollector(name, parent=collector)
class MyCollector(pytest.Collector):
def reportinfo(self):
return self.fspath, 3, "xyz"
Expand Down Expand Up @@ -40,7 +40,7 @@ def test_autouse_fixture(self, testdir): # rough jstests usage
import pytest
def pytest_pycollect_makeitem(collector, name, obj):
if name == "MyClass":
return MyCollector.from_parent(collector, name=name)
return MyCollector(name, parent=collector)
class MyCollector(pytest.Collector):
def reportinfo(self):
return self.fspath, 3, "xyz"
Expand Down
3 changes: 2 additions & 1 deletion testing/python/metafunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ def __init__(self, names):

@attr.s
class DefinitionMock(python.FunctionDefinition):
_pytest_skip_ctor_check = True
obj = attr.ib()

names = fixtures.getfuncargnames(func)
fixtureinfo = FixtureInfo(names)
definition = DefinitionMock._create(func)
definition = DefinitionMock(func)
return python.Metafunc(definition, fixtureinfo, config)

def test_no_funcargs(self):
Expand Down
6 changes: 3 additions & 3 deletions testing/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class CustomFile(pytest.File):
pass
def pytest_collect_file(path, parent):
if path.ext == ".xxx":
return CustomFile.from_parent(fspath=path, parent=parent)
return CustomFile(path, parent=parent)
"""
)
node = testdir.getpathnode(hello)
Expand Down Expand Up @@ -450,7 +450,7 @@ def test_parsearg(self, testdir):
p.move(target)
subdir.chdir()
config = testdir.parseconfig(p.basename)
rcol = Session.from_config(config)
rcol = Session(config=config)
assert rcol.fspath == subdir
parts = rcol._parsearg(p.basename)

Expand All @@ -467,7 +467,7 @@ def test_collect_topdir(self, testdir):
# XXX migrate to collectonly? (see below)
config = testdir.parseconfig(id)
topdir = testdir.tmpdir
rcol = Session.from_config(config)
rcol = Session(config)
assert topdir == rcol.fspath
# rootid = rcol.nodeid
# root2 = rcol.perform_collect([rcol.nodeid], genitems=False)[0]
Expand Down
2 changes: 1 addition & 1 deletion testing/test_mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -1014,7 +1014,7 @@ def test_addmarker_order():
session.own_markers = []
session.parent = None
session.nodeid = ""
node = Node.from_parent(session, name="Test")
node = Node(parent=session, name="Test")
node.add_marker("foo")
node.add_marker("bar")
node.add_marker("baz", append=False)
Expand Down
2 changes: 1 addition & 1 deletion testing/test_pluginmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def pytest_plugin_registered(self):
def test_hook_proxy(self, testdir):
"""Test the gethookproxy function(#2016)"""
config = testdir.parseconfig()
session = Session.from_config(config)
session = Session(config)
testdir.makepyfile(**{"tests/conftest.py": "", "tests/subdir/conftest.py": ""})

conftest1 = testdir.tmpdir.join("tests/conftest.py")
Expand Down

0 comments on commit d98dca3

Please sign in to comment.