Skip to content

Commit bdd1bf5

Browse files
committed
support dump traceback anywhere
1 parent fb72f5d commit bdd1bf5

13 files changed

+113
-14
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ __pycache__
33
*.pyc
44
/.pytest_cache
55
*.dump
6-
test.py
6+
test.py
7+
/my
8+
.vscode

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
English | [简体中文](README_zh.md)
44

55
It's a fork/optimized version from [elifiner/pydump](https://github.com/elifiner/pydump).The main optimization points are:
6+
* Save the `Python traceback` anywhere, not just when it's an exception.
67
* Optimize code structure && remove redundant code
78
* fix bug in python2.7 && support python3.10+
89
* supported more pdb commnd
@@ -24,11 +25,33 @@ Python version:>= 2.7, >=3.6
2425

2526
Not published in pypi,so use the `.whl` file install pydumpling in the dist path.
2627
```
27-
pip install dist/pydumpling-0.1.0-py2.py3-none-any.whl
28+
pip install dist/pydumpling-0.1.1-py2.py3-none-any.whl
2829
```
2930

3031
## How to use pydumpling
3132

33+
34+
### Save the python traceback anywhere.
35+
```python
36+
from pydumpling import dump_current_traceback
37+
from inspect import currentframe
38+
39+
40+
def inner():
41+
a = 1
42+
b = "2"
43+
dump_current_traceback("test.dump")
44+
c = str(a) + b
45+
46+
47+
def outer():
48+
d = 4
49+
inner()
50+
51+
```
52+
53+
### Save the exception traceback.
54+
3255
In the code, find the place where we need to do the `try ... except ...` and use `save_dumpling()`. When we save the dump file, it will default to `${exception filename}:${error lineno}.dump`.
3356

3457
```python

README_zh.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# 针对Python的异常调试器
22

33
这是 [elifiner/pydump](https://github.com/elifiner/pydump) 的fork/优化版本, 主要优化点有:
4+
* 支持在任何地方保存`Python traceback`,而不是只在异常发生的时候
45
* 优化代码结构, 去除冗余代码
56
* 修复其在2.7及3.10版本中的bug
67
* 支持更多的pdb命令
@@ -19,12 +20,34 @@ Python版本支持:>= 2.7, >=3.6
1920

2021
目前还没有在Pypi上面进行发布,因此直接使用PIP安装dist目录下的whl文件
2122
```
22-
pip install dist/pydumpling-0.1.0-py2.py3-none-any.whl
23+
pip install dist/pydumpling-0.1.1-py2.py3-none-any.whl
2324
```
2425

2526
## 使用方法
2627

27-
In the code, find the place where we need to do the exception replump and use 'save_dumpling()'. When we save the dump file, it will default to '${exception file}:${line number of the exception}.dump'.
28+
### 在任何地方进行`traceback`的保存
29+
```python
30+
from pydumpling import dump_current_traceback
31+
from inspect import currentframe
32+
33+
34+
def inner():
35+
a = 1
36+
b = "2"
37+
dump_current_traceback("test.dump")
38+
c = str(a) + b
39+
40+
41+
def outer():
42+
d = 4
43+
inner()
44+
45+
```
46+
47+
48+
49+
### 在异常发生时进行异常堆栈的保存
50+
在异常捕获的处理代码中使用`save_dumpling()`. 如过不指定文件名,默认使用:`${exception file}:${line number of the exception}.dump`.
2851

2952
```python
3053
from pydumpling import save_dumping
-4.35 KB
Binary file not shown.

dist/pydumpling-0.1.0.tar.gz

-3.51 KB
Binary file not shown.
5.96 KB
Binary file not shown.

dist/pydumpling-0.1.1.tar.gz

5.3 KB
Binary file not shown.

pydumpling/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import absolute_import, division, print_function, unicode_literals
22

33
from .debug_dumpling import debug_dumpling, load_dumpling
4-
from .pydumpling import save_dumping, __version__
4+
from .pydumpling import save_dumping, dump_current_traceback, __version__
55

66
__version__ == __version__
7-
__all__ = ["debug_dumpling", "load_dumpling", "save_dumping"]
7+
__all__ = ["debug_dumpling", "load_dumpling", "save_dumping", "dump_current_traceback"]

pydumpling/fake_types.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ def _convert(cls, v):
6060

6161
class FakeTraceback(FakeType):
6262

63-
def __init__(self, traceback):
63+
def __init__(self, traceback=None):
6464
self.tb_frame = FakeFrame(
65-
traceback.tb_frame) if traceback.tb_frame else None
66-
self.tb_lineno = traceback.tb_lineno
65+
traceback.tb_frame) if traceback and traceback.tb_frame else None
66+
self.tb_lineno = traceback.tb_lineno if traceback else None
6767
self.tb_next = FakeTraceback(
68-
traceback.tb_next) if traceback.tb_next else None
68+
traceback.tb_next) if traceback and traceback.tb_next else None
6969
self.tb_lasti = 0
7070

7171

pydumpling/pydumpling.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
import dill
66
import pickle
77
import warnings
8-
from .fake_types import FakeTraceback
8+
import inspect
9+
from .fake_types import FakeFrame, FakeTraceback
910

10-
__version__ = "0.0.1"
11+
__version__ = "0.1.1"
1112

1213

1314
def save_dumping(filename=None, tb=None):
@@ -34,3 +35,48 @@ def save_dumping(filename=None, tb=None):
3435
except Exception as e:
3536
err_msg = "Unexpected error: %s when dumping traceback" % str(e)
3637
warnings.warn(err_msg, RuntimeWarning)
38+
39+
40+
def dump_current_traceback(filename=None):
41+
try:
42+
fake_tb = gen_tb_from_frame(inspect.currentframe())
43+
dumpling = {
44+
"traceback": fake_tb,
45+
"version": __version__,
46+
"dump_type": "DILL"
47+
}
48+
if filename is None:
49+
filename = "%s:%d.dump" % (
50+
fake_tb.tb_frame.f_code.co_filename, fake_tb.tb_frame.f_lineno)
51+
with gzip.open(filename, "wb") as f:
52+
try:
53+
dill.dump(dumpling, f, protocol=dill.HIGHEST_PROTOCOL)
54+
except Exception:
55+
dumpling["dump_type"] = "PICKLE"
56+
pickle.dump(dumpling, f, protocol=dill.HIGHEST_PROTOCOL)
57+
except Exception as e:
58+
err_msg = "Unexpected error: %s when dumping traceback" % str(e)
59+
warnings.warn(err_msg, RuntimeWarning)
60+
61+
62+
def gen_tb_from_frame(f):
63+
tb = FakeTraceback()
64+
tb.tb_frame = FakeFrame(f)
65+
tb.tb_lasti = f.f_lasti
66+
tb.tb_lineno = f.f_lineno
67+
queue = []
68+
f = f.f_back
69+
if f is None:
70+
return tb
71+
while f and "python" not in f.f_code.co_filename.lower():
72+
tb = FakeTraceback()
73+
tb.tb_frame = FakeFrame(f)
74+
tb.tb_lasti = f.f_lasti
75+
tb.tb_lineno = f.f_lineno
76+
queue.append(tb)
77+
f = f.f_back
78+
79+
for i in range(len(queue)-1, 0, -1):
80+
queue[i].tb_next = queue[i-1]
81+
queue[0].tb_next = None
82+
return queue[-1]

0 commit comments

Comments
 (0)