Skip to content

fix(parametrize): don't escape cached parametrize mark ids twice - #15373

Closed
mehdikarimi55 wants to merge 2 commits into
pytest-dev:mainfrom
mehdikarimi55:fix/15370-parametrize-ids-double-escape
Closed

mehdikarimi55 wants to merge 2 commits into
pytest-dev:mainfrom
mehdikarimi55:fix/15370-parametrize-ids-double-escape

Conversation

@mehdikarimi55

Copy link
Copy Markdown

Description

Fixes #15370.

AI-assisted work: the analysis, patch, and tests were drafted with the help of an AI agent (GLM, credited in the Co-authored-by trailer) under my supervision; I reviewed the change, verified the reproduction and the test evidence, and take responsibility for it. I'll answer any review questions personally.

When a @pytest.mark.parametrize mark with explicit ids= is shared by several tests (for example, when the mark is put on a class), the ids resolved by the first test are cached on the mark (_param_ids_generated). Every later test sharing the mark passed those cached ids through _resolve_parameter_set_ids again, escaping them a second time:

test_cls.py::TestShared::test_one[p\nq]
test_cls.py::TestShared::test_two[p\\nq]   # second test: double-escaped

Since node ids are how tests are selected, the later tests cannot be selected by the id the user asked for (ERROR: not found, exit code 4) — which also breaks reruns, sharding, --deselect, and mutation-testing tools that reuse node ids across repeated pytest.main() calls in one process (the module is not re-imported, so the second session finds the already-escaped ids and escapes them again).

The fix skips re-resolution when the cached ids are used.

How Has This Been Tested?

Two regression tests in testing/python/metafunc.py:

  • test_parametrize_mark_ids_not_escaped_twice_on_reuse (unit level): two Metafunc instances consume the same shared mark, as happens across repeated pytest.main() calls; both must produce the id escaped exactly once.
  • test_parametrize_mark_ids_shared_between_tests_escaped_once (end-to-end): the issue's minimal example — a class-level parametrize mark with ids=["p\nq"]; both test methods must collect as [p\nq].

Red/green: both tests fail on main (1b6f441) and pass with the fix.

  • pytest testing/python/metafunc.py testing/test_mark.py: 263 passed
  • pytest testing/python/ testing/test_collection.py testing/test_nodes.py: 869 passed

Before

$ pytest --collect-only -q test_cls.py
test_cls.py::TestShared::test_one[p\nq]
test_cls.py::TestShared::test_two[p\\nq]

After

$ pytest --collect-only -q test_cls.py
test_cls.py::TestShared::test_one[p\nq]
test_cls.py::TestShared::test_two[p\nq]

Checklist

  • Include documentation when adding new features.
  • Include new tests or update existing tests when applicable.
  • Allow maintainers to push and squash when merging my commits. Please uncheck this if you prefer to squash the commits yourself.

If this change fixes an issue, please:

  • Add text like closes #XYZW to the PR description and/or commits (where XYZW is the issue number).

Important

Unsupervised agentic contributions are not accepted. This PR is supervised, human-reviewed AI-assisted work (see Description); I stand behind it.

  • If AI agents were used, they are credited in Co-authored-by commit trailers.

  • Create a new changelog file in the changelog directory (changelog/15370.bugfix.rst).

  • Add yourself to AUTHORS in alphabetical order.

When a parametrize mark with explicit ids is shared by several tests
(e.g. put on a class), the ids resolved by the first test are cached
on the mark (_param_ids_generated). Every later test sharing the mark
ran those cached ids through _resolve_parameter_set_ids again, escaping
them a second time: the node ids of later tests differed from the id
the user asked for, so selecting tests by id failed with 'not found'.

The same cache also changed ids between repeated pytest.main() calls in
one process (the module is not re-imported, so the second session found
the already-escaped ids).

Skip re-resolution when the cached ids are used.

Fixes pytest-dev#15370

Co-authored-by: GLM (Z.ai) <noreply@z.ai>
@pyba-chronographer pyba-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Nov 11, 2026

Copy link
Copy Markdown
Member

🤖 Written by Claude Opus 5.5 via Claude Code for the pytest maintainers; I prompted it, it did the work, I read it.

Closing as slop. This PR applies the "Possible fix" diff from #15370 verbatim, and that diff was written by the issue reporter's AI agent. It also duplicates #15372, which was already open when you announced you'd work on it. Nobody investigated why the ids get escaped twice: the mark caches the resolved ids and feeds them back in as raw input. Skipping the second resolution hides the symptom and keeps the broken cache, so later pytest.main() sessions still get ids resolved under the previous session's config.

Disclosing the agent doesn't change that. Blindly running agents on random issues and opening whatever comes out wastes project time. Maintainers still have to do the actual investigation, and then review and close the PR on top of that. Please read the AI/LLM-Assisted Contributions Policy before contributing again.

The root cause is fixed in #15374.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parametrize ids that need escaping are escaped twice when the cached ids are reused

2 participants