Skip to content

Comments

[DONTMERGE] refactor: clean up node constructors — move logic to from_parent#14227

Open
RonnyPfannschmidt wants to merge 6 commits intopytest-dev:mainfrom
RonnyPfannschmidt:cleanup/node-constructors-to-from-parent
Open

[DONTMERGE] refactor: clean up node constructors — move logic to from_parent#14227
RonnyPfannschmidt wants to merge 6 commits intopytest-dev:mainfrom
RonnyPfannschmidt:cleanup/node-constructors-to-from-parent

Conversation

@RonnyPfannschmidt
Copy link
Member

Summary

Moves initialization logic out of node __init__ methods and into from_parent, completing the refactoring that NodeMeta was designed to enable (see src/_pytest/nodes.py docstring on NodeMeta).

After this change, __init__ methods are "dumb stores" focused on assigning attributes, while from_parent methods handle derivation, validation, and post-construction setup.

Changes by node class

  • Item: Diamond inheritance check (_check_item_and_collector_diamond_inheritance) moved from __init__ to from_parent; converted to classmethod
  • Package: Removed redundant __init__ (only did session = parent.session, already handled by FSCollector); added explicit typed from_parent
  • FSCollector: Extracted _derive_name / _derive_nodeid as classmethods; from_parent pre-computes name and nodeid; __init__ retains fallback for non-cooperative constructors
  • DoctestItem: Fixture resolution (getfixtureinfo, _initrequest) moved from __init__ to from_parent
  • Function: Marker extension, keyword updates, fixture resolution, and _initrequest extracted into _setup_markers_and_fixtures, called from from_parent
  • Review fix: Eliminated double _imply_path call that would produce duplicate deprecation warnings; cleaned up dead __init__ parameters

What was NOT changed

Node.__init__ config/session/path/nodeid derivation was left in place — pushing these into from_parent as kwargs breaks backward compat for plugins with non-cooperative constructors (the NodeMeta._create fallback filters unknown kwargs, causing TypeErrors). The derivation is trivially parent.config/parent.session, and from_parent already enforces callers can't override them.

Test plan

  • Full test suite passes: 4032 passed, 45 skipped, 17 xfailed, 4 xpassed
  • All pre-commit hooks pass (ruff, mypy, codespell, etc.)
  • Key test files verified: test_nodes.py, test_collection.py, deprecated_test.py, python/collect.py, python/metafunc.py, test_doctest.py, test_runner.py, test_skipping.py, test_unittest.py, test_mark.py, test_junitxml.py

Note

This PR was AI-generated using Cursor with Claude (Anthropic). All commits include Co-authored-by attribution for both Cursor AI and Anthropic Claude.

Made with Cursor

RonnyPfannschmidt and others added 6 commits February 22, 2026 20:01
… Item.from_parent

The _check_item_and_collector_diamond_inheritance validation is not
data storage — it's a one-time class-level check. Moving it to
from_parent keeps __init__ focused on storing values.

Also converts the method to a classmethod since it only inspects
the class, not the instance.

Co-authored-by: Cursor AI <ai@cursor.sh>
Co-authored-by: Anthropic Claude <claude@anthropic.com>
…m_parent

Package.__init__ only did `session = parent.session` which FSCollector
already handles. Removing the __init__ entirely and adding an explicit
from_parent with a typed signature for clarity.

Also removes the now-unused LEGACY_PATH import from python.py.

Co-authored-by: Cursor AI <ai@cursor.sh>
Co-authored-by: Anthropic Claude <claude@anthropic.com>
…smethods and from_parent

Extracts _derive_name and _derive_nodeid as classmethods on FSCollector.
The from_parent method now pre-computes name and nodeid before calling
super, so __init__ receives fully resolved values in the normal path.

The __init__ retains fallback derivation for backward compatibility
with non-cooperative constructors (plugins using positional args).

Co-authored-by: Cursor AI <ai@cursor.sh>
Co-authored-by: Anthropic Claude <claude@anthropic.com>
…to from_parent

DoctestItem.__init__ now only stores runner, dtest, and obj. The
fixture resolution (getfixtureinfo) and _initrequest() are moved to
from_parent, which is the only production entry point for creating
DoctestItem instances.

Co-authored-by: Cursor AI <ai@cursor.sh>
Co-authored-by: Anthropic Claude <claude@anthropic.com>
…nit__ to from_parent

Function.__init__ now only stores _obj, _instance, originalname, and
callspec. The marker extension, keyword updates, fixture resolution
(getfixtureinfo), and _initrequest() are extracted into a new
_setup_markers_and_fixtures method called from from_parent after
construction.

This keeps __init__ focused on storing values while from_parent
handles all post-construction initialization logic.

Co-authored-by: Cursor AI <ai@cursor.sh>
Co-authored-by: Anthropic Claude <claude@anthropic.com>
1. FSCollector: fix double _imply_path call that would produce duplicate
   deprecation warnings when fspath is used. Now __init__ skips the
   _imply_path call when path is already resolved (by from_parent),
   using _check_path for consistency validation instead.

2. Function: remove dead keywords/fixtureinfo parameters from __init__
   since from_parent now pops them before calling super. Replace with
   **kw for forward-compatibility with additional kwargs.

Co-authored-by: Cursor AI <ai@cursor.sh>
Co-authored-by: Anthropic Claude <claude@anthropic.com>
@RonnyPfannschmidt RonnyPfannschmidt marked this pull request as ready for review February 23, 2026 09:52
Copilot AI review requested due to automatic review settings February 23, 2026 09:52
@RonnyPfannschmidt
Copy link
Member Author

moved to rfr to trigger copilot review - this one is likely to need more iteration

@RonnyPfannschmidt RonnyPfannschmidt changed the title refactor: clean up node constructors — move logic to from_parent [DONTMERGE] refactor: clean up node constructors — move logic to from_parent Feb 23, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors pytest node construction so that initialization/derivation logic happens in from_parent, keeping __init__ methods closer to “attribute stores” and reducing side effects during object construction.

Changes:

  • Updated FSCollector to pre-compute path, name, and nodeid in from_parent, and avoid duplicate fspath deprecation warnings.
  • Moved Item’s diamond-inheritance warning to Item.from_parent.
  • Moved fixture/marker setup out of Function/DoctestItem.__init__ into from_parent, and replaced Package.__init__ with an explicit Package.from_parent.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/_pytest/python.py Refactors Package construction and moves Function fixture/marker setup into from_parent.
src/_pytest/nodes.py Refactors FSCollector name/nodeid derivation into helpers and from_parent; moves Item diamond check into from_parent.
src/_pytest/doctest.py Moves doctest fixture initialization from __init__ into DoctestItem.from_parent.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +654 to +667
@classmethod
def from_parent( # type: ignore[override]
cls,
parent: nodes.Collector,
# NOTE: following args are unused:
config=None,
session=None,
nodeid=None,
path: Path | None = None,
) -> None:
# NOTE: Could be just the following, but kept as-is for compat.
# super().__init__(self, fspath, parent=parent)
session = parent.session
super().__init__(
fspath=fspath,
path=path,
parent=parent,
config=config,
session=session,
nodeid=nodeid,
)
*,
path: Path,
) -> Self:
"""The public constructor.

:param parent: The parent collector of this Package.
:param path: The package directory's path.
:type path: pathlib.Path
"""
return super().from_parent(parent=parent, path=path)
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Package.from_parent overrides Directory/FSCollector.from_parent but drops the fspath keyword entirely. This is a runtime API break: external code/plugins that still call Package.from_parent(..., fspath=...) (even if deprecated) will now get TypeError: got an unexpected keyword argument 'fspath'. Consider keeping fspath: LEGACY_PATH | None = None (and optionally path: Path | None = None) in the signature and delegating to super().from_parent(...) after resolving/validating, so deprecated callers continue to work while steering new code to path.

Copilot uses AI. Check for mistakes.
Comment on lines 1558 to 1586
If given, the object which will be called when the Function is invoked,
otherwise the callobj will be obtained from ``parent`` using ``originalname``.
:param keywords:
Keywords bound to the function object for "-k" matching.
:param session:
The pytest Session object.
:param fixtureinfo:
Fixture information already resolved at this fixture node..
:param originalname:
The attribute name to use for accessing the underlying function object.
Defaults to ``name``. Set this if name is different from the original name,
for example when it contains decorations like those added by parametrization
(``my_func[my_param]``).
"""

# Disable since functions handle it themselves.
_ALLOW_MARKERS = False

def __init__(
self,
name: str,
parent,
config: Config | None = None,
callspec: CallSpec2 | None = None,
callobj=NOTSET,
keywords: Mapping[str, Any] | None = None,
session: Session | None = None,
fixtureinfo: FuncFixtureInfo | None = None,
originalname: str | None = None,
**kw,
) -> None:
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Function class docstring still documents keywords and fixtureinfo as constructor parameters, but __init__ no longer accepts them explicitly (they’re now handled in from_parent). This makes the public API docs misleading. Please update the docstring (or add a note) to reflect that keywords/fixtureinfo are from_parent kwargs and not __init__ parameters anymore.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant