Opt-in FunctionDefinition collection tree node (#3926) - #14769
Opt-in FunctionDefinition collection tree node (#3926)#14769RonnyPfannschmidt wants to merge 3 commits into
Conversation
7fe152c to
d011e35
Compare
|
Heads-up on a defect in this PR as it currently stands: selecting a test by node id is broken in the tree modes. $ cat pytest.ini
[pytest]
collect_function_definition = pedantic
$ pytest -q "test_x.py::test_a[1]"
ERROR: not found: test_x.py::test_a
(no match in any of [<Module test_x.py>])Works under
holds for the node id strings, but not for selection: The minimal fix is to let the matcher look through an Worth noting the existing comment right at that spot already anticipates this: # A non-parameterized arg matches all parametrizations (if any).
# TODO: Remove the hacky split once the collection structure
# contains parametrization.
is_match = node.name.split("[")[0] == matchparts[0]This PR is exactly what makes the collection structure contain the parametrization, so that |
leaf(name, params) conflated two operations that NodeId already separates
elsewhere: child() appends a name segment, with_params() attaches params
(terminalizing the id). Separating them makes the API self-documenting:
parent.child("test_a").with_params("1-x") # → test_x.py::test_a[1-x]
More importantly, this enables the FunctionDefinition collection node work
(pytest-dev#14769 / pytest-dev#14805) to be merged without conflict: a FunctionDefinition
collector already carries the function name in its names tuple, so
Function.__init__ can simply call definition.id.with_params(params) — no
name added, correct id produced:
definition node test_x.py::test_a
definition.with_params() test_x.py::test_a[1] ← correct
old leaf(name, p) test_x.py::test_a::test_a[1] ← name doubled
The flat layout (no FunctionDefinition) is identical in outcome because
child(name).with_params(params) produces the same string as the old
leaf(name, params).
Production callers updated: nodes.py (generic Item) and python.py (Function).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
d011e35 to
708a8e8
Compare
708a8e8 to
c7fa6ea
Compare
c7fa6ea to
7cd892a
Compare
7cd892a to
478b721
Compare
478b721 to
36095a1
Compare
TestReport.from_item_and_call asserted reportinfo() yields a line number. Items which cannot point at a line -- non-Python items legitimately report None -- crashed with an INTERNALERROR when skipped via a marker. Report the location without a line instead.
36095a1 to
382218b
Compare
Move IdMaker, CallSpec and _ascii_escaped_by_config out of python.py into a new module, together with the long-str id strategy literals they own. python.py imports them back, so _pytest.python.CallSpec and the CallSpec2 alias keep resolving as before. Pure move, no behaviour change. The two CollectError call sites go through a small _collect_error() helper which imports nodes lazily, so nodes stays free to import this module later.
Introduce the ``collect_function_definition`` ini option to promote the internal ``FunctionDefinition`` from a transient parametrization helper to a real collector node in the collection tree. The option is tri-state: - ``hidden`` (default): unchanged flat layout; the definition drives parametrization and is kept out of the tree. - ``pedantic``: insert the definition node between the Module/Class and its test functions; function-level markers are scoped to the definition and each invocation owns only its callspec markers. - ``messy``: migration stopgap -- insert the node but transfer the function-level markers back onto each invocation to preserve the legacy marker layout for code not yet prepared for the new scope; emits a header warning. FunctionDefinition becomes a PyCollector (no longer a Function/Item), so it is collected rather than run. Invocation node ids stay flat and identical across all modes, so selection, caching and reporting are unaffected. obj and instance resolution and getmodpath skip an interposed definition node. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
382218b to
38e054a
Compare
Note
Stack: #14855 -> #14856 -> #14769 -> #14857 -> #14858. Review only the last commit.
Adds the
collect_function_definitionini option, promoting the internalFunctionDefinitionto a real node in the collection tree.hidden(default): unchanged flat layout, no node in the treepedantic: node inserted, function-level markers scoped to itmessy: node inserted, markers transferred down to each invocation (warns)Node ids of the individual invocations are unchanged in all modes.
Also introduces
nodes.ItemDefinition, the collector-agnostic base for "acollector standing for one test definition". Concept only here — it carries the
name/selection semantics and nothing else. The generic parametrization protocol
on top of it is #14858.
Node-id selection is fixed as part of this: the definition node carries the bare
name while the parametrization lives on the items below it, so the argument
matcher has to look through the definition. Without this,
pytest test_x.py::test_a[1]reports "no match" underpedantic/messy. Covered by a6 selector x 3 mode regression matrix.
Part of #3926.