diff --git a/changelog/5975.deprecation.rst b/changelog/5975.deprecation.rst deleted file mode 100644 index 6e5dbc2acef..00000000000 --- a/changelog/5975.deprecation.rst +++ /dev/null @@ -1,6 +0,0 @@ -Deprecate using direct constructors for ``Nodes``. - -Instead they are new constructed via ``Node.from_parent``. - -This transitional mechanism enables us to detangle the very intensely -entangled ``Node`` relationships by enforcing more controlled creation/configruation patterns. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 88112b12a98..748d3ac65a4 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -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 `. - -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" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/en/example/nonpython/conftest.py b/doc/en/example/nonpython/conftest.py index d30ab3841dc..93d8285bfa7 100644 --- a/doc/en/example/nonpython/conftest.py +++ b/doc/en/example/nonpython/conftest.py @@ -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): @@ -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): diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 69edaf4bef9..09861be6442 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -9,7 +9,6 @@ in case of warnings which need to format their messages. """ from _pytest.warning_types import PytestDeprecationWarning -from _pytest.warning_types import UnformattedWarning # set of plugins which have been integrated into the core; we use this list to ignore # them during registration to avoid conflicts @@ -36,11 +35,6 @@ "as a keyword argument instead." ) -NODE_USE_FROM_PARENT = UnformattedWarning( - PytestDeprecationWarning, - "direct construction of {name} has been deprecated, please use {name}.from_parent", -) - JUNIT_XML_DEFAULT_FAMILY = PytestDeprecationWarning( "The 'junit_family' default value will change to 'xunit2' in pytest 6.0.\n" "Add 'junit_family=xunit1' to your pytest.ini file to keep the current format " diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 96b2a83b816..70fb453dcda 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -110,9 +110,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): @@ -219,10 +219,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) @@ -378,9 +374,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): @@ -489,9 +483,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): diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 7a18821b7f5..db9701e70ca 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -199,7 +199,7 @@ def pytest_addoption(parser): def wrap_session(config, doit): """Skeleton command line program""" - session = Session.from_config(config) + session = Session(config) session.exitstatus = ExitCode.OK initstate = 0 try: @@ -423,10 +423,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__, diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 501b39c85e3..0ccc8c145cb 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -19,7 +19,6 @@ from _pytest.compat import getfslineno from _pytest.compat import TYPE_CHECKING from _pytest.config import Config -from _pytest.deprecated import NODE_USE_FROM_PARENT from _pytest.fixtures import FixtureDef from _pytest.fixtures import FixtureLookupError from _pytest.fixtures import FixtureLookupErrorRepr @@ -75,16 +74,7 @@ def ischildnode(baseid, nodeid): return node_parts[: len(base_parts)] == base_parts -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 _create(self, *k, **kw): - return super().__call__(*k, **kw) - - -class Node(metaclass=NodeMeta): +class Node: """ base class for Collector and Item the test collection tree. Collector subclasses have children, Items are terminal nodes.""" @@ -144,10 +134,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""" @@ -441,10 +427,6 @@ def __init__( super().__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath) - @classmethod - def from_parent(cls, parent, *, fspath): - return cls._create(parent=parent, fspath=fspath) - class File(FSCollector): """ base class for collecting tests from a file. """ diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 049d0d224fd..d7e66935b24 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -777,7 +777,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) @@ -795,7 +795,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] diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 317dbe37ae1..9c84d4a9b55 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -191,8 +191,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) @@ -204,7 +204,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) @@ -223,7 +223,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 ) @@ -384,7 +384,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(name=name, parent=self, callobj=funcobj) fixtureinfo = fm.getfixtureinfo(definition, funcobj, cls) metafunc = Metafunc( @@ -399,7 +399,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) @@ -411,9 +411,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, @@ -629,7 +629,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. @@ -680,10 +680,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 [] @@ -709,7 +705,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 @@ -1471,10 +1467,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) diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index f25f5366c45..a069052ffa8 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -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): @@ -52,7 +52,7 @@ 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: @@ -60,8 +60,7 @@ def collect(self): 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 diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 59cb69a0034..5390d038d8b 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -1,8 +1,5 @@ -import inspect - import pytest from _pytest import deprecated -from _pytest import nodes @pytest.mark.filterwarnings("default") @@ -76,17 +73,3 @@ def test_foo(): result.stdout.no_fnmatch_line(warning_msg) else: result.stdout.fnmatch_lines([warning_msg]) - - -def test_node_direct_ctor_warning(): - 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") - assert w[0].lineno == inspect.currentframe().f_lineno - 1 - assert w[0].filename == __file__ diff --git a/testing/python/collect.py b/testing/python/collect.py index b13947e4759..b0cec101eb1 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -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(): @@ -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") diff --git a/testing/python/integration.py b/testing/python/integration.py index 35e86e6b96c..73419eef424 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -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" @@ -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" diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 24250376561..ae3856ba7b1 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -28,7 +28,7 @@ class DefinitionMock(python.FunctionDefinition): 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): diff --git a/testing/test_collection.py b/testing/test_collection.py index 328fb4edb80..1891b794043 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -75,7 +75,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) @@ -446,7 +446,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) @@ -463,7 +463,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] diff --git a/testing/test_mark.py b/testing/test_mark.py index 6577f35400d..48330aff11c 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -1010,11 +1010,7 @@ def test_3(): def test_addmarker_order(): - session = mock.Mock() - session.own_markers = [] - session.parent = None - session.nodeid = "" - node = Node.from_parent(session, name="Test") + node = Node("Test", config=mock.Mock(), session=mock.Mock(), nodeid="Test") node.add_marker("foo") node.add_marker("bar") node.add_marker("baz", append=False) diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 3b83512092e..e3402d20701 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -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")