diff --git a/Lib/pstats.py b/Lib/pstats.py index 07ecda07796e44..14c62a0ca53d28 100644 --- a/Lib/pstats.py +++ b/Lib/pstats.py @@ -64,11 +64,14 @@ class FunctionProfile: file_name: str line_number: int +FunctionLoc = tuple[str, int, str] # filename, line number, function name + @dataclass(unsafe_hash=True) class StatsProfile: '''Class for keeping track of an item in inventory.''' total_tt: float - func_profiles: dict[str, FunctionProfile] + func_profiles: dict[tuple[str, int, str], FunctionProfile] + func_profiles_by_loc: dict[FunctionLoc, FunctionProfile] class Stats: """This class is used for creating reports from data generated by the @@ -366,7 +369,8 @@ def get_stats_profile(self): total_tt = float(f8(self.total_tt)) func_profiles = {} - stats_profile = StatsProfile(total_tt, func_profiles) + func_profiles_by_loc = {} + stats_profile = StatsProfile(total_tt, func_profiles, func_profiles_by_loc) for func in func_list: cc, nc, tt, ct, callers = self.stats[func] @@ -386,6 +390,7 @@ def get_stats_profile(self): line_number ) func_profiles[func_name] = func_profile + func_profiles_by_loc[func] = func_profile return stats_profile diff --git a/Lib/test/test_pstats.py b/Lib/test/test_pstats.py index a26a8c1d522a70..bb8be695abc9ea 100644 --- a/Lib/test/test_pstats.py +++ b/Lib/test/test_pstats.py @@ -151,6 +151,35 @@ def pass3(): pass self.assertIn('pass2', funcs_called) self.assertIn('pass3', funcs_called) + def test_get_stats_profile_duplicate_func_names(self): + class Test1: + def pass_fn(self): + pass + + class Test2: + def pass_fn(self): + pass + + def main(): + Test1().pass_fn() + Test2().pass_fn() + + pr = cProfile.Profile() + pr.enable() + main() + pr.create_stats() + ps = pstats.Stats(pr) + stats_profile = ps.get_stats_profile() + # Functions with same name have different keys in func_profiles_by_loc + pass_fn_keys = [ + (file_name, line_number, func_name) + for file_name, line_number, func_name + in stats_profile.func_profiles_by_loc + if func_name == "pass_fn" + ] + + self.assertEqual(len(pass_fn_keys), 2) + def test_SortKey_enum(self): self.assertEqual(SortKey.FILENAME, 'filename') self.assertNotEqual(SortKey.FILENAME, SortKey.CALLS) diff --git a/Misc/NEWS.d/next/Library/2026-05-09-21-18-15.gh-issue-126850.om89px.rst b/Misc/NEWS.d/next/Library/2026-05-09-21-18-15.gh-issue-126850.om89px.rst new file mode 100644 index 00000000000000..cf40b8e15041e6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-09-21-18-15.gh-issue-126850.om89px.rst @@ -0,0 +1,3 @@ +Add an attribute to the object returned by :func:`pstats.Stats.get_stats_profile` to access +function profiles disambiguated by file name and line number. This handles cases +where multiple functions share the same name in profiling programs. Contributed by Edward Thomas.