From 78a81d8a4bc2ba9793d2b747db862cad2dfa0301 Mon Sep 17 00:00:00 2001 From: Imgyu Kim Date: Fri, 20 Mar 2026 05:49:25 +0900 Subject: [PATCH 1/2] gh-145865: Fix CoverageResults.__init__ to copy the counts dict Fix typo where self.counter was assigned instead of self.counts, causing the counts dict to not be copied. This made CoverageResults.update() mutate the caller's original dict. The fix changes self.counter = self.counts.copy() to self.counts = self.counts.copy(), matching the pattern used for calledfuncs and callers on the following lines. --- Lib/test/test_trace.py | 27 +++++++++++++++++++++++++++ Lib/trace.py | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index 19eee19bdea6d5..74adecbbaf6fb0 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -590,5 +590,32 @@ def test_no_source_file(self): self.assertIn(f"{filename}({firstlineno + 4})", out[4]) +class TestCoverageResultsInit(unittest.TestCase): + def test_counts_dict_is_copied(self): + # gh-145865: CoverageResults.__init__ should copy the counts dict + from trace import CoverageResults + + counts = {} + cr = CoverageResults(counts=counts) + cr.update(CoverageResults(counts={("file.py", 1): 5})) + self.assertEqual(counts, {}) + + def test_calledfuncs_dict_is_copied(self): + from trace import CoverageResults + + calledfuncs = {} + cr = CoverageResults(calledfuncs=calledfuncs) + cr.update(CoverageResults(calledfuncs={("file.py", "mod", "func"): 1})) + self.assertEqual(calledfuncs, {}) + + def test_callers_dict_is_copied(self): + from trace import CoverageResults + + callers = {} + cr = CoverageResults(callers=callers) + cr.update(CoverageResults(callers={(("a.py", "m", "f"), ("b.py", "m", "g")): 1})) + self.assertEqual(callers, {}) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/trace.py b/Lib/trace.py index cd3a6d30661da3..15894921d3e434 100644 --- a/Lib/trace.py +++ b/Lib/trace.py @@ -155,7 +155,7 @@ def __init__(self, counts=None, calledfuncs=None, infile=None, self.counts = counts if self.counts is None: self.counts = {} - self.counter = self.counts.copy() # map (filename, lineno) to count + self.counts = self.counts.copy() # map (filename, lineno) to count self.calledfuncs = calledfuncs if self.calledfuncs is None: self.calledfuncs = {} From d658d7ad4b760def83e4519d6c39bd5c5d63f6dc Mon Sep 17 00:00:00 2001 From: Imgyu Kim Date: Fri, 20 Mar 2026 05:52:44 +0900 Subject: [PATCH 2/2] Add NEWS entry for gh-145865 --- .../Library/2026-03-20-00-00-00.gh-issue-145865.TrCpCo.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-03-20-00-00-00.gh-issue-145865.TrCpCo.rst diff --git a/Misc/NEWS.d/next/Library/2026-03-20-00-00-00.gh-issue-145865.TrCpCo.rst b/Misc/NEWS.d/next/Library/2026-03-20-00-00-00.gh-issue-145865.TrCpCo.rst new file mode 100644 index 00000000000000..ea5c56e4a455fa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-20-00-00-00.gh-issue-145865.TrCpCo.rst @@ -0,0 +1,3 @@ +Fix typo in :class:`trace.CoverageResults` where ``self.counter`` was +assigned instead of ``self.counts``, causing the ``counts`` dict to not +be copied and ``update()`` to mutate the caller's original dict.