py4web-debug-tools 1.3.0__tar.gz → 1.3.1__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.
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/CHANGELOG.md +6 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/PKG-INFO +2 -1
- py4web_debug_tools-1.3.1/example.py +29 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/pyproject.toml +2 -1
- py4web_debug_tools-1.3.1/src/py4web_debug/__about__.py +1 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/dumping.py +118 -21
- py4web_debug_tools-1.3.0/src/py4web_debug/__about__.py +0 -1
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/.gitignore +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/README.md +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/__init__.py +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/_internals.py +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/core.py +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/debugbar.py +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/env.py +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/internals.py +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/py.typed +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/templates/debugbar.html +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/templates/dumpdie.html +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/templates/error_default.html +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/templates/fancy_dumpdie.html +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/types.py +0 -0
- {py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/wsgi.py +0 -0
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
<!--next-version-placeholder-->
|
|
4
4
|
|
|
5
|
+
## v1.3.1 (2025-04-01)
|
|
6
|
+
|
|
7
|
+
### Fix
|
|
8
|
+
|
|
9
|
+
* **dbg:** Detect more variable names ([`a80556c`](https://github.com/trialandsuccess/py4web-debug-tools/commit/a80556c2d35f89a5c0f900518074b7800dfeb844))
|
|
10
|
+
|
|
5
11
|
## v1.3.0 (2025-04-01)
|
|
6
12
|
|
|
7
13
|
### Feature
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py4web-debug-tools
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: Debug Tools for py4web
|
|
5
5
|
Project-URL: Documentation, https://github.com/trialandsuccess/py4web-debug-tools#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/trialandsuccess/py4web-debug-tools/issues
|
|
@@ -18,6 +18,7 @@ Requires-Python: >=3.10
|
|
|
18
18
|
Requires-Dist: configurable-json>=1.1
|
|
19
19
|
Requires-Dist: py4web
|
|
20
20
|
Requires-Dist: pydal
|
|
21
|
+
Requires-Dist: typing-extensions
|
|
21
22
|
Requires-Dist: yatl
|
|
22
23
|
Provides-Extra: dev
|
|
23
24
|
Requires-Dist: hatch; extra == 'dev'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import py4web_debug
|
|
2
|
+
from py4web_debug import dbg
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def handle(_): ...
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def my_decorator(fn):
|
|
9
|
+
return fn
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@my_decorator
|
|
13
|
+
def some_func(arg):
|
|
14
|
+
return dbg("hello")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
dbg("hi")
|
|
18
|
+
y = dbg("hi")
|
|
19
|
+
py4web_debug.dbg("hi")
|
|
20
|
+
z = py4web_debug.dbg("hi")
|
|
21
|
+
|
|
22
|
+
handle(dbg("hi"))
|
|
23
|
+
handle(py4web_debug.dbg("hi"))
|
|
24
|
+
|
|
25
|
+
some_long_expression = " 3 + 123"
|
|
26
|
+
|
|
27
|
+
handle(dbg((some_long_expression + some_long_expression)))
|
|
28
|
+
|
|
29
|
+
some_func(arg=dbg("arg1"))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.3.1"
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import ast
|
|
2
|
+
import inspect
|
|
2
3
|
import json
|
|
3
4
|
import sys
|
|
4
5
|
import traceback
|
|
5
6
|
import typing
|
|
7
|
+
from types import FrameType
|
|
6
8
|
|
|
7
9
|
import pydal.objects
|
|
8
10
|
from configurablejson import ConfigurableJsonEncoder, JSONRule
|
|
@@ -159,35 +161,130 @@ def dd(*data: typing.Any, fancy: bool = True, api: bool = False) -> None:
|
|
|
159
161
|
T = typing.TypeVar("T")
|
|
160
162
|
|
|
161
163
|
|
|
162
|
-
def
|
|
164
|
+
def _extract_dbg_variable_name(frame: FrameType) -> str:
|
|
165
|
+
# Read the full source of the caller file
|
|
166
|
+
with open(frame.f_code.co_filename, "r", encoding="utf-8") as f:
|
|
167
|
+
source_code = f.readlines()
|
|
168
|
+
|
|
169
|
+
# Extract the relevant lines (handle multiline statements)
|
|
170
|
+
full_source = ""
|
|
171
|
+
line_number = frame.f_lineno - 1
|
|
172
|
+
while line_number < len(source_code):
|
|
173
|
+
full_source += source_code[line_number].strip()
|
|
174
|
+
if full_source.count("(") == full_source.count(")"): # Ensure all parentheses are closed
|
|
175
|
+
break
|
|
176
|
+
line_number += 1
|
|
177
|
+
|
|
178
|
+
# Parse the AST
|
|
179
|
+
tree = ast.parse(full_source)
|
|
180
|
+
|
|
181
|
+
# Find the relevant call in the AST
|
|
182
|
+
var_name = ""
|
|
183
|
+
for node in ast.walk(tree):
|
|
184
|
+
if isinstance(node, ast.Expr) and isinstance(node.value, ast.Call):
|
|
185
|
+
call = node.value
|
|
186
|
+
while isinstance(call, ast.Call): # Handle nested function calls
|
|
187
|
+
if hasattr(call.func, "id") and call.func.id == "dbg":
|
|
188
|
+
var_name = ast.unparse(call.args[0])
|
|
189
|
+
break
|
|
190
|
+
elif isinstance(call.func, ast.Attribute) and call.func.attr == "dbg":
|
|
191
|
+
var_name = ast.unparse(call.args[0])
|
|
192
|
+
break
|
|
193
|
+
call = call.args[0] if call.args else None
|
|
194
|
+
elif isinstance(node, ast.Assign) and isinstance(node.value, ast.Call):
|
|
195
|
+
call = node.value
|
|
196
|
+
while isinstance(call, ast.Call): # Handle assignments with nested calls
|
|
197
|
+
if hasattr(call.func, "id") and call.func.id == "dbg":
|
|
198
|
+
var_name = ast.unparse(call.args[0])
|
|
199
|
+
break
|
|
200
|
+
elif isinstance(call.func, ast.Attribute) and call.func.attr == "dbg":
|
|
201
|
+
var_name = ast.unparse(call.args[0])
|
|
202
|
+
break
|
|
203
|
+
call = call.args[0] if call.args else None
|
|
204
|
+
elif isinstance(node, ast.Return) and isinstance(node.value, ast.Call):
|
|
205
|
+
call = node.value
|
|
206
|
+
while isinstance(call, ast.Call): # Handle return dbg(x)
|
|
207
|
+
if hasattr(call.func, "id") and call.func.id == "dbg":
|
|
208
|
+
var_name = ast.unparse(call.args[0])
|
|
209
|
+
break
|
|
210
|
+
elif isinstance(call.func, ast.Attribute) and call.func.attr == "dbg":
|
|
211
|
+
var_name = ast.unparse(call.args[0])
|
|
212
|
+
break
|
|
213
|
+
call = call.args[0] if call.args else None
|
|
214
|
+
elif isinstance(node, ast.keyword) and isinstance(node.value, ast.Call):
|
|
215
|
+
# Handle function keyword arguments like some_func(arg=dbg("arg"))
|
|
216
|
+
call = node.value
|
|
217
|
+
if hasattr(call.func, "id") and call.func.id == "dbg":
|
|
218
|
+
var_name = ast.unparse(call.args[0])
|
|
219
|
+
break
|
|
220
|
+
elif isinstance(call.func, ast.Attribute) and call.func.attr == "dbg":
|
|
221
|
+
var_name = ast.unparse(call.args[0])
|
|
222
|
+
break
|
|
223
|
+
|
|
224
|
+
return var_name
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def extract_dbg_variable_name(frame: FrameType) -> typing.Optional[str]:
|
|
228
|
+
"""
|
|
229
|
+
Extracts the variable name passed to dbg() from the source code.
|
|
230
|
+
Handles multiline statements, assignments, function arguments, and return values.
|
|
163
231
|
"""
|
|
164
|
-
|
|
232
|
+
try:
|
|
233
|
+
return _extract_dbg_variable_name(frame)
|
|
234
|
+
except Exception:
|
|
235
|
+
return None
|
|
236
|
+
|
|
165
237
|
|
|
166
|
-
|
|
167
|
-
Also returns the original value.
|
|
238
|
+
def dbg(value: T) -> T:
|
|
168
239
|
"""
|
|
169
|
-
|
|
240
|
+
Improved Rust-like dbg function that captures variable names even in assignments,
|
|
241
|
+
namespaced calls, and nested function calls.
|
|
242
|
+
Handles multiline calls, function arguments, and return statements.
|
|
170
243
|
|
|
171
|
-
|
|
172
|
-
file_name = frame.filename.split("/")[-1]
|
|
173
|
-
line_number = frame.lineno
|
|
244
|
+
Examples:
|
|
174
245
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
246
|
+
```
|
|
247
|
+
def handle(_):
|
|
248
|
+
...
|
|
178
249
|
|
|
179
|
-
|
|
180
|
-
tree = ast.parse(line)
|
|
250
|
+
def my_decorator(fn): return fn
|
|
181
251
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if isinstance(call, ast.Call) and call.func.id == "dbg":
|
|
186
|
-
var_name = ast.unparse(call.args[0]) # Extract the first argument as a string
|
|
252
|
+
@my_decorator
|
|
253
|
+
def some_func(arg):
|
|
254
|
+
return dbg("hello")
|
|
187
255
|
|
|
188
|
-
except Exception:
|
|
189
|
-
var_name = "?"
|
|
190
256
|
|
|
191
|
-
|
|
257
|
+
dbg('hi')
|
|
258
|
+
y = dbg('hi')
|
|
259
|
+
py4web_debug.dbg('hi')
|
|
260
|
+
z = py4web_debug.dbg('hi')
|
|
192
261
|
|
|
262
|
+
handle(dbg('hi'))
|
|
263
|
+
handle(py4web_debug.dbg('hi'))
|
|
264
|
+
|
|
265
|
+
some_long_expression = " 3 + 123"
|
|
266
|
+
|
|
267
|
+
handle(
|
|
268
|
+
dbg(
|
|
269
|
+
(
|
|
270
|
+
some_long_expression +
|
|
271
|
+
some_long_expression
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
some_func(arg=dbg("arg1"))
|
|
277
|
+
```
|
|
278
|
+
"""
|
|
279
|
+
value_type = type(value).__name__
|
|
280
|
+
|
|
281
|
+
# Get the caller's frame
|
|
282
|
+
frame = inspect.currentframe().f_back
|
|
283
|
+
frame_info = inspect.getframeinfo(frame)
|
|
284
|
+
file_name = frame_info.filename.split("/")[-1]
|
|
285
|
+
line_number = frame_info.lineno
|
|
286
|
+
|
|
287
|
+
var_name = extract_dbg_variable_name(frame) or "?"
|
|
288
|
+
|
|
289
|
+
print(f"[{file_name}:{line_number}] {value_type}({var_name}) = {value}", file=sys.stderr)
|
|
193
290
|
return value
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.3.0"
|
|
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
|
{py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/templates/debugbar.html
RENAMED
|
File without changes
|
{py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/templates/dumpdie.html
RENAMED
|
File without changes
|
{py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/templates/error_default.html
RENAMED
|
File without changes
|
{py4web_debug_tools-1.3.0 → py4web_debug_tools-1.3.1}/src/py4web_debug/templates/fancy_dumpdie.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|