diff --git a/Lib/profiling/sampling/heatmap_collector.py b/Lib/profiling/sampling/heatmap_collector.py index 220b6b8150ac980..ae42f38ab822443 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}"] + resolved = Path(filename).resolve() + if resolved.is_file(): + source_lines = resolved.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 ee27fdd3fa3053c..961e728babf5876 100644 --- a/Lib/test/test_profiling/test_heatmap.py +++ b/Lib/test/test_profiling/test_heatmap.py @@ -622,6 +622,27 @@ def test_export_file_html_has_line_numbers(self): # Should have line-related content self.assertIn('line-', content) + def test_export_skips_nonexistent_source(self): + """Source files that do not exist produce a placeholder.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [('/no/such/file.py', (1, 1, -1, -1), 'f', None)] + collector.process_frames(frames, thread_id=1) + + output_path = os.path.join(self.test_dir, 'missing_src') + + with captured_stdout(), captured_stderr(): + collector.export(output_path) + + 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.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 000000000000000..faecab8cff3e7b1 --- /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.