Skip to content

fix: query_param_matcher no longer mutates the caller's params dict#801

Merged
markstory merged 2 commits into
getsentry:masterfrom
gaoflow:fix/query-param-matcher-mutates-input
Jun 26, 2026
Merged

fix: query_param_matcher no longer mutates the caller's params dict#801
markstory merged 2 commits into
getsentry:masterfrom
gaoflow:fix/query-param-matcher-mutates-input

Conversation

@gaoflow

@gaoflow gaoflow commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Problem

query_param_matcher converts int and float values to strings so they can
be compared against URL query-string values (which are always strings). However,
this conversion was done in-place on the dict reference obtained via
params_dict = params or {}, which aliases the original dict when a non-empty
mapping is passed. As a result, every numeric value in the caller's dict is
silently overwritten with its string equivalent:

params = {"page": 1, "ratio": 0.5}
matchers.query_param_matcher(params)
# params is now {"page": "1", "ratio": "0.5"} — mutated!

This is surprising behaviour that can cause hard-to-debug failures when the
same dict is inspected or reused after the matcher is created.

Fix

Copy the mapping into a fresh dict before the normalisation loop:

-    params_dict = params or {}
+    params_dict = dict(params) if params else {}

A regression test (test_query_param_matcher_does_not_mutate_input) is
included.

This pull request was prepared with the assistance of AI, under my direction and review.

@gaoflow
gaoflow requested a review from markstory as a code owner June 24, 2026 23:58

@markstory markstory left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you also add a note to the CHANGES file about this fix?

@gaoflow

gaoflow commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Added the CHANGES entry in 03e670a.

Verification:

uv run --with pytest --with pytest-asyncio --with requests --with urllib3 --with pyyaml --with types-PyYAML python -m pytest responses/tests/test_matchers.py::test_query_param_matcher_does_not_mutate_input -q
# 1 passed

git diff --check
# passed

I first tried the system python -m pytest, but the local global pytest plugin stack is broken (pytest_pylint missing tomlkit, then a pytest/pytest-asyncio version mismatch), so I used an isolated uv run --with ... environment for the focused test.

gaoflow and others added 2 commits June 26, 2026 13:11
Numeric values (int/float) in the params mapping were being converted to
strings in-place, silently modifying the original dict passed by the
caller. Copy the mapping into a local dict before performing the
int/float→str normalisation.
@gaoflow
gaoflow force-pushed the fix/query-param-matcher-mutates-input branch from 03e670a to e2f6b30 Compare June 26, 2026 11:13
@gaoflow

gaoflow commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current master and resolved the CHANGES conflict while keeping both the upstream #798 entry and the #801 note.

Verification after the rebase:

uv run --with pytest --with pytest-asyncio --with requests --with urllib3 --with pyyaml --with types-PyYAML python -m pytest responses/tests/test_matchers.py::test_query_param_matcher_does_not_mutate_input -q
git diff --check origin/master...HEAD

@markstory
markstory merged commit a19add7 into getsentry:master Jun 26, 2026
23 checks passed
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.

2 participants