Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: [3.7, 3.8, 3.9, 3.11] # List of Python versions to test against
python-version: [3.7, 3.8, 3.9, 3.11, 3.12, 3.13] # List of Python versions to test against

steps:
- name: Checkout code
Expand Down Expand Up @@ -46,7 +46,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.11
python-version: 3.13
update-environment: false

- name: Install dependencies
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Version 0.4.3

- Fixed invalid source code behavior when the source file doesn't exist or couldn't be read (#28)

# Version 0.4.2

- Fixed a bug that can mark 2+ threads as a faulting thread (#26),
Expand Down
2 changes: 1 addition & 1 deletion backtracepython/source_code_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def collect(self, report):
if new_max_line > source["maxLine"]:
source["maxLine"] = new_max_line

for source_code_path in source_code:
for source_code_path in list(source_code):
source = source_code[source_code_path]
source_code_content = self.read_source(
source_code_path, source["startLine"] - 1, source["maxLine"]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

setup(
name="backtracepython",
version="0.4.2",
version="0.4.3",
description="Backtrace.io error reporting tool for Python",
author="Backtrace.io",
author_email="team@backtrace.io",
Expand Down
77 changes: 77 additions & 0 deletions tests/test_source_code_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import os

from backtracepython.source_code_handler import SourceCodeHandler


def write_file(path, content):
with open(str(path), "w") as f:
f.write(content)


def make_report(source_paths):
"""Build a minimal report whose main thread stack references the given file paths."""
stack = [
{"sourceCode": path, "line": 10, "funcName": "test"} for path in source_paths
]
return {
"mainThread": "main",
"threads": {
"main": {"stack": stack},
},
}


def test_collect_removes_unreadable_sources_without_runtime_error():
"""Reproduces RuntimeError: dictionary changed size during iteration.

When every source file in the stack is unreadable, collect() used to pop
entries from the source_code dict while iterating over it.
"""
handler = SourceCodeHandler(tab_width=4, context_line_count=3)
report = make_report(
[
"/nonexistent/path/a.py",
"/nonexistent/path/b.py",
]
)

# Before the fix this raised:
# RuntimeError: dictionary changed size during iteration
result = handler.collect(report)

assert result["sourceCode"] == {}


def test_collect_keeps_readable_sources(tmp_path):
"""Verify that readable source files are collected normally."""
source_file = tmp_path / "real.py"
write_file(source_file, "foobarbaz")

handler = SourceCodeHandler(tab_width=4, context_line_count=1)
report = make_report([str(source_file)])

result = handler.collect(report)

assert str(source_file) in result["sourceCode"]
assert "text" in result["sourceCode"][str(source_file)]


def test_collect_mixed_readable_and_unreadable(tmp_path):
"""Mix of existing and missing files"""
source_file = tmp_path / "exists.py"
write_file(source_file, "foobarbaz")

handler = SourceCodeHandler(tab_width=4, context_line_count=3)
report = make_report(
[
"/nonexistent/path/missing.py",
str(source_file),
"/another/missing/file.py",
]
)

result = handler.collect(report)

assert str(source_file) in result["sourceCode"]
assert "/nonexistent/path/missing.py" not in result["sourceCode"]
assert "/another/missing/file.py" not in result["sourceCode"]
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py27, py37, py38, py39, py310
envlist = py27, py37, py38, py39, py310, py312, py313
skipsdist = True

[testenv]
Expand Down
Loading