watchdock-errors 0.2.4__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {watchdock_errors-0.2.4/src/watchdock_errors.egg-info → watchdock_errors-0.3.0}/PKG-INFO +1 -1
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors/utils.py +32 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0/src/watchdock_errors.egg-info}/PKG-INFO +1 -1
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors.egg-info/SOURCES.txt +2 -1
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors.egg-info/scm_file_list.json +9 -8
- watchdock_errors-0.3.0/src/watchdock_errors.egg-info/scm_version.json +8 -0
- watchdock_errors-0.3.0/tests/test_utils.py +44 -0
- watchdock_errors-0.2.4/src/watchdock_errors.egg-info/scm_version.json +0 -8
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/.github/workflows/ci.yml +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/.github/workflows/release.yml +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/.gitignore +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/README.md +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/pyproject.toml +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/setup.cfg +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors/__init__.py +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors/client.py +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors/config.py +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors/event.py +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors/integrations/__init__.py +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors/integrations/django.py +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors/integrations/fastapi.py +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors.egg-info/dependency_links.txt +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors.egg-info/requires.txt +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors.egg-info/top_level.txt +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/tests/test_client.py +0 -0
- {watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/tests/test_event.py +0 -0
|
@@ -6,6 +6,35 @@ import socket
|
|
|
6
6
|
import traceback
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
#: Number of source lines captured on each side of the failing line. Matches the
|
|
10
|
+
#: window the backend uses when it falls back to enriching frames itself, so a
|
|
11
|
+
#: frame looks the same whether the context came from here or from the server.
|
|
12
|
+
CONTEXT_LINES = 2
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _source_context(filename: str, lineno: int) -> tuple[list[str], list[str]]:
|
|
16
|
+
"""Read the source lines immediately before and after ``lineno``.
|
|
17
|
+
|
|
18
|
+
Returns ``(pre_context, post_context)``. We do this in the SDK, on the box
|
|
19
|
+
where the code actually runs, because the source is here — the backend can
|
|
20
|
+
only guess at it from its own filesystem, which is the wrong machine for any
|
|
21
|
+
real app. Out-of-range lines come back as empty strings so the two lists are
|
|
22
|
+
always the same length regardless of where in the file the error landed.
|
|
23
|
+
"""
|
|
24
|
+
if not filename or not lineno:
|
|
25
|
+
return [], []
|
|
26
|
+
|
|
27
|
+
pre = [
|
|
28
|
+
linecache.getline(filename, i).rstrip("\n")
|
|
29
|
+
for i in range(max(1, lineno - CONTEXT_LINES), lineno)
|
|
30
|
+
]
|
|
31
|
+
post = [
|
|
32
|
+
linecache.getline(filename, i).rstrip("\n")
|
|
33
|
+
for i in range(lineno + 1, lineno + CONTEXT_LINES + 1)
|
|
34
|
+
]
|
|
35
|
+
return pre, post
|
|
36
|
+
|
|
37
|
+
|
|
9
38
|
def extract_stacktrace(exc: BaseException) -> list[dict]:
|
|
10
39
|
"""Extract structured stack frames from an exception."""
|
|
11
40
|
tb = exc.__traceback__
|
|
@@ -15,12 +44,15 @@ def extract_stacktrace(exc: BaseException) -> list[dict]:
|
|
|
15
44
|
frames = []
|
|
16
45
|
for frame_summary in traceback.extract_tb(tb):
|
|
17
46
|
context_line = linecache.getline(frame_summary.filename, frame_summary.lineno).strip()
|
|
47
|
+
pre_context, post_context = _source_context(frame_summary.filename, frame_summary.lineno)
|
|
18
48
|
frames.append(
|
|
19
49
|
{
|
|
20
50
|
"filename": frame_summary.filename,
|
|
21
51
|
"function": frame_summary.name,
|
|
22
52
|
"lineno": frame_summary.lineno,
|
|
23
53
|
"context_line": context_line,
|
|
54
|
+
"pre_context": pre_context,
|
|
55
|
+
"post_context": post_context,
|
|
24
56
|
}
|
|
25
57
|
)
|
|
26
58
|
return frames
|
{watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors.egg-info/scm_file_list.json
RENAMED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": [
|
|
3
|
+
".gitignore",
|
|
3
4
|
"README.md",
|
|
4
5
|
"pyproject.toml",
|
|
5
|
-
".
|
|
6
|
-
"src/watchdock_errors/
|
|
6
|
+
"src/watchdock_errors/event.py",
|
|
7
|
+
"src/watchdock_errors/client.py",
|
|
7
8
|
"src/watchdock_errors/config.py",
|
|
8
9
|
"src/watchdock_errors/utils.py",
|
|
9
|
-
"src/watchdock_errors/
|
|
10
|
-
"src/watchdock_errors/event.py",
|
|
11
|
-
"src/watchdock_errors/integrations/__init__.py",
|
|
10
|
+
"src/watchdock_errors/__init__.py",
|
|
12
11
|
"src/watchdock_errors/integrations/fastapi.py",
|
|
13
12
|
"src/watchdock_errors/integrations/django.py",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
13
|
+
"src/watchdock_errors/integrations/__init__.py",
|
|
14
|
+
".github/workflows/ci.yml",
|
|
16
15
|
".github/workflows/release.yml",
|
|
17
|
-
"
|
|
16
|
+
"tests/test_event.py",
|
|
17
|
+
"tests/test_client.py",
|
|
18
|
+
"tests/test_utils.py"
|
|
18
19
|
]
|
|
19
20
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from watchdock_errors.utils import extract_stacktrace
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _boom():
|
|
5
|
+
# This line and the two on either side are what the context capture should
|
|
6
|
+
# pick up. Keep the surrounding lines stable so the assertions below hold.
|
|
7
|
+
x = 1
|
|
8
|
+
raise ValueError("boom") # <-- failing line
|
|
9
|
+
return x
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_extract_stacktrace_captures_source_context():
|
|
13
|
+
try:
|
|
14
|
+
_boom()
|
|
15
|
+
except ValueError as exc:
|
|
16
|
+
frames = extract_stacktrace(exc)
|
|
17
|
+
|
|
18
|
+
assert frames, "expected at least one frame"
|
|
19
|
+
|
|
20
|
+
# The frame for _boom is the one whose failing line raised.
|
|
21
|
+
boom_frame = next(f for f in frames if f["function"] == "_boom")
|
|
22
|
+
|
|
23
|
+
assert boom_frame["context_line"] == 'raise ValueError("boom") # <-- failing line'
|
|
24
|
+
# Two lines before and two after, in order, with indentation preserved.
|
|
25
|
+
assert len(boom_frame["pre_context"]) == 2
|
|
26
|
+
assert len(boom_frame["post_context"]) == 2
|
|
27
|
+
# The line immediately before the raise is `x = 1` (indentation kept).
|
|
28
|
+
assert boom_frame["pre_context"][1] == " x = 1"
|
|
29
|
+
# The line immediately after the raise is the return.
|
|
30
|
+
assert boom_frame["post_context"][0] == " return x"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_frames_always_have_context_keys():
|
|
34
|
+
try:
|
|
35
|
+
raise KeyError("missing")
|
|
36
|
+
except KeyError as exc:
|
|
37
|
+
frames = extract_stacktrace(exc)
|
|
38
|
+
|
|
39
|
+
for frame in frames:
|
|
40
|
+
assert "context_line" in frame
|
|
41
|
+
assert "pre_context" in frame
|
|
42
|
+
assert "post_context" in frame
|
|
43
|
+
assert isinstance(frame["pre_context"], list)
|
|
44
|
+
assert isinstance(frame["post_context"], list)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors/integrations/__init__.py
RENAMED
|
File without changes
|
{watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors/integrations/django.py
RENAMED
|
File without changes
|
{watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors/integrations/fastapi.py
RENAMED
|
File without changes
|
{watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors.egg-info/requires.txt
RENAMED
|
File without changes
|
{watchdock_errors-0.2.4 → watchdock_errors-0.3.0}/src/watchdock_errors.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|