Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions Lib/profiling/sampling/module_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,17 @@ def extract_module_name(filename, path_info):
except (ValueError, OSError):
return (str(filename), 'other')

# Check if it's in stdlib
if path_info['stdlib'] and file_path.is_relative_to(path_info['stdlib']):
return (_path_to_module(file_path.relative_to(path_info['stdlib'])), 'stdlib')

# Check site-packages
for site_pkg in path_info['site_packages']:
if file_path.is_relative_to(site_pkg):
return (_path_to_module(file_path.relative_to(site_pkg)), 'site-packages')
# Check stdlib and site-packages, preferring the most specific directory
# because site-packages is usually located inside the stdlib directory
bases = [(path_info['stdlib'], 'stdlib')]
bases.extend((site_pkg, 'site-packages')
for site_pkg in path_info['site_packages'])
matches = [(base, module_type) for base, module_type in bases
if base and file_path.is_relative_to(base)]
if matches:
# max() returns the first of equal candidates, so stdlib wins ties
base, module_type = max(matches, key=lambda m: len(m[0].parts))
return (_path_to_module(file_path.relative_to(base)), module_type)

# Check other sys.path entries (project files)
if not str(file_path).startswith(('<', '[')): # Skip special files
Expand Down
19 changes: 19 additions & 0 deletions Lib/test/test_profiling/test_heatmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,25 @@ def test_extract_module_name_with_stdlib_file(self):
self.assertEqual(module_type, 'stdlib')
self.assertIn('os', module_name)

def test_extract_module_name_with_site_packages_inside_stdlib(self):
"""Test extract_module_name when site-packages is inside stdlib."""
stdlib = Path('/usr/lib/python3.15')
site_packages = stdlib / 'site-packages'
# On Windows, site.getsitepackages() also includes sys.prefix
path_info = {
'stdlib': stdlib,
'site_packages': [Path('/usr'), site_packages],
'sys_path': [],
}

site_packages_file = str(site_packages / 'requests' / 'api.py')
self.assertEqual(extract_module_name(site_packages_file, path_info),
('requests.api', 'site-packages'))

stdlib_file = str(stdlib / 'json' / 'decoder.py')
self.assertEqual(extract_module_name(stdlib_file, path_info),
('json.decoder', 'stdlib'))

def test_extract_module_name_with_project_file(self):
"""Test extract_module_name with a project file."""
path_info = get_python_path_info()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix :mod:`profiling.sampling` reporting modules in a ``site-packages``
directory located inside the standard library directory as standard library
modules. The heatmap now lists them under "Site Packages" and flamegraph
labels no longer start with ``site-packages.``.
Loading