https://github.com/python/cpython/commit/722687092f8aaa005708302a502e930a729e8660 commit: 722687092f8aaa005708302a502e930a729e8660 branch: main author: tonghuaroot (童话) <[email protected]> committer: pablogsal <[email protected]> date: 2026-10-05T15:50:03+01:00 summary:
gh-153838: Skip non-regular source files in the heatmap exporter (#153839) * gh-153838: Skip non-regular and oversized files in heatmap exporter * Drop the size cap, keep the is_file() guard only * gh-153838: Verify non-regular sources are never opened --------- Co-authored-by: Pablo Galindo Salgado <[email protected]> files: A Misc/NEWS.d/next/Library/2026-07-17-12-00-00.gh-issue-153838.HmCp5s.rst M Lib/profiling/sampling/heatmap_collector.py M Lib/test/test_profiling/test_heatmap.py diff --git a/Lib/profiling/sampling/heatmap_collector.py b/Lib/profiling/sampling/heatmap_collector.py index 220b6b8150ac98..62f42755827f06 100644 --- a/Lib/profiling/sampling/heatmap_collector.py +++ b/Lib/profiling/sampling/heatmap_collector.py @@ -783,14 +783,14 @@ def _generate_file_html(self, output_path: Path, filename: str, line_counts: Dict[int, int], self_counts: Dict[int, int], file_stat: FileStats): """Generate HTML for a single source file with heatmap coloring.""" - # Read source file + source_lines = [f"# Source file not available: {filename}"] try: - source_lines = Path(filename).read_text(encoding='utf-8', errors='replace').splitlines() - except (IOError, OSError) as e: - if not (filename.startswith('<') or filename.startswith('[') or - filename in ('~', '...', '.') or len(filename) < 2): - print(f"Warning: Could not read source file {filename}: {e}") - source_lines = [f"# Source file not available: {filename}"] + path = Path(filename) + if path.is_file(): + source_lines = path.read_text( + encoding='utf-8', errors='replace').splitlines() + except (IOError, OSError): + pass # Generate HTML for each line max_samples = max(line_counts.values()) if line_counts else 1 diff --git a/Lib/test/test_profiling/test_heatmap.py b/Lib/test/test_profiling/test_heatmap.py index ee27fdd3fa3053..1b4de0caa3d00f 100644 --- a/Lib/test/test_profiling/test_heatmap.py +++ b/Lib/test/test_profiling/test_heatmap.py @@ -4,6 +4,7 @@ import shutil import tempfile import unittest +from unittest import mock from collections import namedtuple from pathlib import Path @@ -615,13 +616,43 @@ def test_export_file_html_has_line_numbers(self): html_files = [f for f in os.listdir(output_path) if f.startswith('file_') and f.endswith('.html')] - if html_files: - with open(os.path.join(output_path, html_files[0]), 'r', encoding='utf-8') as f: - content = f.read() + self.assertEqual(len(html_files), 1) + with open(os.path.join(output_path, html_files[0]), 'r', encoding='utf-8') as f: + content = f.read() + + # Should have line-related content + self.assertIn('line-', content) + + def test_export_skips_nonexistent_source(self): + self.check_unavailable_source(os.path.join(self.test_dir, 'missing.py')) - # Should have line-related content - self.assertIn('line-', content) + def test_export_skips_directory_source(self): + self.check_unavailable_source(self.test_dir) + @unittest.skipUnless(hasattr(os, 'mkfifo'), 'requires os.mkfifo') + def test_export_skips_fifo_source(self): + filename = os.path.join(self.test_dir, 'source.fifo') + os.mkfifo(filename) + self.check_unavailable_source(filename) + + def check_unavailable_source(self, filename): + collector = HeatmapCollector(sample_interval_usec=100) + frames = [(filename, (1, 1, -1, -1), 'f', None)] + collector.process_frames(frames, thread_id=1) + output_path = Path(self.test_dir) / 'unavailable_source' + read_text = Path.read_text + + def check_read(path, *args, **kwargs): + self.assertNotEqual(path, Path(filename), + 'Non-regular source must not be opened') + return read_text(path, *args, **kwargs) + + with (captured_stdout(), captured_stderr(), + mock.patch.object(Path, 'read_text', check_read)): + collector.export(output_path) + content = (output_path / collector.file_index[filename]).read_text( + encoding='utf-8') + self.assertIn('Source file not available', content) class MockFrameInfo: """Mock FrameInfo for testing. diff --git a/Misc/NEWS.d/next/Library/2026-07-17-12-00-00.gh-issue-153838.HmCp5s.rst b/Misc/NEWS.d/next/Library/2026-07-17-12-00-00.gh-issue-153838.HmCp5s.rst new file mode 100644 index 00000000000000..faecab8cff3e7b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-17-12-00-00.gh-issue-153838.HmCp5s.rst @@ -0,0 +1,2 @@ +Skip non-regular files in the ``profiling.sampling`` heatmap exporter +instead of reading them unconditionally. Patch by tonghuaroot. _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
