Clarify the relation between marks and keywords - #15011
Open
RonnyPfannschmidt wants to merge 7 commits into
Open
RonnyPfannschmidt wants to merge 7 commits into
RonnyPfannschmidt wants to merge 7 commits into
Conversation
RonnyPfannschmidt
requested review from
Pierre-Sassoulas,
The-Compiler,
Zac-HD and
nicoddemus
and removed request for
nicoddemus
September 13, 2026 13:12
Pierre-Sassoulas
left a comment
Member
There was a problem hiding this comment.
Didn't see anything outrageous but I'm not sure I'm knowledgable enough to do this review.
bluetech
approved these changes
Sep 14, 2026
bluetech
left a comment
Member
There was a problem hiding this comment.
Thanks, look like good changes to me.
nicoddemus
approved these changes
Sep 14, 2026
RonnyPfannschmidt
force-pushed
the
marks-keywords-4569
branch
from
September 15, 2026 11:34
ead8f48 to
b2e6948
Compare
The -k matcher scans a test function's __dict__ to support the legacy `test_fn.foo = True` keyword idiom, but that scan also picked up attributes nobody meant as keywords: pytest's own `pytestmark` storage, and the bookkeeping decorators leave behind (`__wrapped__` and the rest of what functools.wraps copies, lru_cache's `cache_parameters`). So `-k wrapped` selected every wraps-decorated test and `-k pytestmark` selected every directly marked one. Skip private and dunder names, plus a named set of known attributes. Part of pytest-dev#4569. Co-Authored-By: Claude Opus 5 (1M context) via Claude Code <noreply@anthropic.com>
Marks applied during collection are stored in node.keywords as Mark objects; add_marker stored the MarkDecorator instead, so the value type depended on how the mark got there. Nothing inside pytest reads keyword values, and both types expose name/args/kwargs, so downstream `item.keywords["x"].args` keeps working. Part of pytest-dev#4569. Co-Authored-By: Claude Opus 5 (1M context) via Claude Code <noreply@anthropic.com>
RonnyPfannschmidt
force-pushed
the
marks-keywords-4569
branch
from
September 16, 2026 06:45
b2e6948 to
8966368
Compare
The -k help text still described the pre-5.4 implementation ("a Python
evaluable expression"), claimed matching is limited to test names and
their parent classes, and never mentioned that marker names are
keywords - which is the behaviour people trip over in pytest-dev#4569.
Replace it with the real list of keyword sources, and state the
substring/case-insensitive contrast with -m in both option blocks.
Part of pytest-dev#4569.
Co-Authored-By: Claude Opus 5 (1M context) via Claude Code <noreply@anthropic.com>
KeywordMatcher claimed it only matches Class and Function names, which stopped being true in 2.4.0; Function's `keywords` parameter claimed it feeds "-k" matching, which it does not; TestReport.keywords promised a name -> value mapping while the values are all 1, so annotate it `Literal[1]` and let the type checker hold that promise. `FixtureRequest.applymarker` spoke of "a single test function invocation" without saying what that is in contrast to; say that it is the request's node, and that decorating the function marks every invocation instead. Also state what Node.keywords is for, and that -k does not read it. Part of pytest-dev#4569. Co-Authored-By: Claude Opus 5 (1M context) via Claude Code <noreply@anthropic.com>
The markers.rst section title and intro presented -k as matching test names, in contrast to -m matching markers, and only corrected itself in a trailing paragraph after three console dumps. usage.rst, the page the option reference links to, still described the pre-5.4 eval semantics and listed only filenames, classes and functions. Keep the keyword list where it was, after the examples -- the reader needs to see -k work before being told what else it looks at -- but make it a list, and give each entry a name from the example module rather than a description in the abstract. The example module already carries the point: test_send_http is marked webtest, so `-k webtest` selects a test whose name contains no "webtest", and `-k device` selects both device tests where the -m expression above tells their arguments apart. Label the section so the other pages can point at it. usage.rst is a getting-started page, so it keeps one sentence and the link; the option reference keeps the exhaustive list and links here for the examples. Part of pytest-dev#4569. Co-Authored-By: Claude Opus 5 (1M context) via Claude Code <noreply@anthropic.com>
The --runslow and incremental examples tested for a marker with `"name" in item.keywords`, which is also true when a parent node happens to be named that, or when the test function carries an attribute of that name. These snippets are widely copied, so they are where the idiom of treating keywords as a mark lookup keeps coming from. Part of pytest-dev#4569. Co-Authored-By: Claude Opus 5 (1M context) via Claude Code <noreply@anthropic.com>
Several long-standing behaviours had no test: -k matching marks from the module, class and base classes; -k ignoring marker arguments; -k seeing a mark added by a conftest collection hook; extra_keyword_matches on an item rather than a collector; and writing into item.keywords having no effect on either -k or -m, despite the 2.3.4 changelog claiming it "integrates with the -m option". Also enable the -k half of test_mark_expressions_no_smear, commented out since the marker transfer that made marks smear onto a shared base class was removed, and pin that -m is case sensitive. Part of pytest-dev#4569. Co-Authored-By: Claude Opus 5 (1M context) via Claude Code <noreply@anthropic.com>
RonnyPfannschmidt
force-pushed
the
marks-keywords-4569
branch
from
September 16, 2026 07:31
8966368 to
b34b66c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I prompted an agent (Claude Opus 5 in Claude Code) to work through #4569. The
investigation, the commits and this description are its work; I read it and I
am posting it.
#4569 asks to revisit the relation between marks and keywords. Mapping what
that relation actually is came first, and it changes the shape of the issue:
-kdoes not readitem.keywords, and has not since 2.4.0 (#306).KeywordMatcher.from_itembuilds its own name set from the node chain,extra_keyword_matches, the test function's__dict__anditer_markers().node.keywordsis a separate mapping, fed by different code, read only byrequest.keywords,TestReport.keywordsand the terminal's skip folding.So the two mark-to-keyword hacks from #4564 and #4649 are independent of one
another, and the
# todo: this is a hell of a hackcomment pointing at #4569sits on the one that does not influence selection at all. That makes most of
#4569 a documentation and coverage problem rather than a refactoring one.
One commit per item:
Behaviour
-kno longer matches attributes that land in a test function's__dict__without anyone meaning them as keywords.
-k wrappedselected everyfunctools.wraps-decorated test and-k pytestmarkevery directly markedone. Private and dunder names are skipped, plus a named set. The legacy
test_func.slow = Trueidiom keeps working.add_markerstores aMarkinnode.keywordsinstead of aMarkDecorator, matching what collection-time marks store.Documentation
-khelp text described the pre-5.4 implementation ("a Python evaluableexpression"), limited matching to test names and parent classes, and never
mentioned markers — the thing The-Compiler's comment on revisit relation between marks and keywords #4569 asks for. It
now lists the real keyword sources and contrasts with
-m.markers.rstframed-kas name-only and corrected itself only in atrailing paragraph;
usage.rst, which the option reference links to, stilldescribed eval semantics.
KeywordMatcher,Function'skeywordsparameter,TestReport.keywords,Node.keywordsandFixtureRequest.keywordsdocstrings were stale or wrong.--runslowand incremental examples useget_closest_markerrather than"name" in item.keywords.Tests
Pins
-kmatching marks from module/class/base classes,-kignoring markerarguments,
-kseeing a mark added by a conftest collection hook,extra_keyword_matcheson an item, and writing intoitem.keywordshaving noeffect on
-kor-m— despite the 2.3.4 changelog claiming it integrateswith
-m. Also enables the-khalf oftest_mark_expressions_no_smear,commented out since the marker transfer was removed, and pins that
-miscase sensitive.
Deliberately left open
Whether the
function.__dict__scan should exist at all is the remainingdesign question in #4569. Dropping it means deprecating attribute-marks and
finding another discriminator for the skip folding in
terminal.py, whichkeys off
"pytestmark" in report.keywords. UnifyingKeywordMatcherwithnode.keywordsdepends on that answer, so neither is attempted here.🤖 Generated with Claude Code