py4web-debug-tools 0.1.1__py3-none-any.whl → 0.2.0.dev0__py3-none-any.whl

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/core.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # Transforms janky 'internals' into easy to work with tool
2
2
  import os
3
+ import typing
3
4
 
4
5
  from py4web import DAL as P4WDAL
5
6
 
@@ -18,11 +19,20 @@ class DebugTools:
18
19
 
19
20
  def enable(
20
21
  self,
22
+ # general settings:
21
23
  db: P4WDAL,
22
- enabled: bool = None,
23
- fancy_rendering=True,
24
- bar_style="bootstrap",
25
- slow_threshold_ms=10,
24
+ enabled: bool | None = None, # OVERWRITES errorpage_enabled and debugbar_enabled
25
+ set_env_var: bool = True,
26
+
27
+ # error screen settings:
28
+ errorpage_enabled: bool = None, # value of 'enabled' is used by default
29
+ errorpage_renderer: typing.Callable = None,
30
+
31
+ # debugbar settings:
32
+ debugbar_enabled: bool = None, # value of 'enabled' is used by default
33
+ debugbar_fancy_rendering: bool = True,
34
+ debugbar_style: typing.Literal["bootstrap"] = "bootstrap",
35
+ debugbar_slow_threshold_ms: int = 10,
26
36
  ):
27
37
  """
28
38
  By default, on_off looks at PY4WEB_DEBUG_MODE in the env
@@ -30,20 +40,25 @@ class DebugTools:
30
40
  @todo: debugbar style (bootstrap/default, bulma, ...)
31
41
  """
32
42
  if enabled is None:
33
- enabled = os.getenv("PY4WEB_DEBUG_MODE", False)
43
+ enabled = is_debug()
34
44
 
35
45
  self.db = db
36
46
  self.enabled = enabled
37
47
 
38
48
  self.IS_DEBUG = enabled
39
49
  if enabled:
40
- patch_py4()
41
- self.debug_bar = DebugBar(db, fancy_rendering, bar_style, slow_threshold_ms)
50
+ if errorpage_enabled in (True, None):
51
+ patch_py4(errorpage_renderer)
42
52
 
43
- # will change the result of is_debug (hopefully)
44
- os.environ["PY4WEB_DEBUG_MODE"] = "1"
53
+ if debugbar_enabled in (True, None):
54
+ self.debug_bar = DebugBar(db, debugbar_fancy_rendering, debugbar_style, debugbar_slow_threshold_ms)
55
+
56
+ if set_env_var:
57
+ # will change the result of is_debug (hopefully)
58
+ os.environ["PY4WEB_DEBUG_MODE"] = "1"
45
59
  else:
46
60
  self.debug_bar = DummyDebugBar()
47
61
 
48
62
 
49
63
  tools = DebugTools()
64
+
py4web_debug/debugbar.py CHANGED
@@ -133,7 +133,7 @@ def debugbar_template(fixture: Fixture, debug_data: dict, fancy=True):
133
133
  T = typing.TypeVar("T")
134
134
 
135
135
 
136
- def _prune_long_values(value: T) -> T:
136
+ def _prune_long_values(value: T) -> T | str:
137
137
  """
138
138
  Find the end values in nested dicts and lists and prune them if they are too long,
139
139
  Usually (tm) returns the same type as the input (except when the input is long bytes)
@@ -146,7 +146,7 @@ def _prune_long_values(value: T) -> T:
146
146
  for key, item in value.items():
147
147
  value[key] = _prune_long_values(item)
148
148
  elif isinstance(value, str | bytes) and len(value) > 50:
149
- value = "[pruned due to length]"
149
+ return "[pruned due to length]"
150
150
 
151
151
  return value
152
152
 
py4web_debug/dumping.py CHANGED
@@ -104,7 +104,7 @@ class ApiDumpDieError(Exception):
104
104
  pass
105
105
 
106
106
 
107
- def dd(*data, fancy=True, api=False) -> typing.NoReturn:
107
+ def dd(*data, fancy=True, api=False):
108
108
  """
109
109
  Dump and Die:
110
110
  Show args as JSON an halt the rest of the request
py4web_debug/internals.py CHANGED
@@ -5,6 +5,7 @@ import logging
5
5
  import os
6
6
  import re
7
7
  import traceback
8
+ import typing
8
9
 
9
10
  import ombott as bottle
10
11
  import yatl
@@ -27,12 +28,17 @@ def custom_error_page(
27
28
  color=None,
28
29
  message=None,
29
30
  traceback="",
30
- err_type=None,
31
+ err_type: str | typing.Type | None = None,
31
32
  bare_exception=None,
33
+
34
+ renderer=None
32
35
  ):
33
- if not isinstance(err_type, str):
36
+ if err_type and not isinstance(err_type, str):
34
37
  err_type = err_type.__name__
35
38
 
39
+ # make sure the name is a string (but it probably already is:)
40
+ err_type_name = str(err_type)
41
+
36
42
  if hasattr(Template, "_on_success"):
37
43
  # reset here on error, because on_error might not be called!
38
44
  Template.on_success = Template._on_success
@@ -60,6 +66,9 @@ def custom_error_page(
60
66
  return json.dumps(context)
61
67
  # else - return html error-page
62
68
 
69
+ if renderer:
70
+ return renderer(context)
71
+
63
72
  templates = {
64
73
  "default": "error_default.html",
65
74
  "DumpDieError": "dumpdie.html",
@@ -67,7 +76,7 @@ def custom_error_page(
67
76
  "ApiDumpDieError": None,
68
77
  }
69
78
 
70
- _tmpl = templates.get(err_type, templates["default"])
79
+ _tmpl = templates.get(err_type_name, templates["default"])
71
80
 
72
81
  if _tmpl is None:
73
82
  # just bare exception
@@ -84,44 +93,45 @@ def custom_error_page(
84
93
  )
85
94
 
86
95
 
87
- def custom_catch_errors(app_name, func):
88
- """Catches and logs errors in an action; also sets request.app_name"""
89
-
90
- @functools.wraps(func)
91
- def wrapper(*func_args, **func_kwargs):
92
- try:
93
- request.app_name = app_name
94
- ret = func(*func_args, **func_kwargs)
95
- if isinstance(ret, dict):
96
- response.headers["Content-Type"] = "application/json"
97
- ret = dumps(ret)
98
- return ret
99
- except HTTP as http:
100
- response.status = http.status
101
- response.headers.update(http.headers)
102
- return http.body
103
- except bottle.HTTPResponse:
104
- raise
105
- except Exception as e:
106
- snapshot = get_error_snapshot()
107
- logging.error(snapshot["traceback"])
108
- ticket_uuid = error_logger.log(request.app_name, snapshot) or "unknown"
109
- response.status = 500
110
-
111
- raise bottle.HTTPResponse(
112
- body=custom_error_page(
113
- 500,
114
- button_text=ticket_uuid,
115
- href="/_dashboard/ticket/" + ticket_uuid,
116
- traceback=traceback.format_exc(),
117
- err_type=type(e),
118
- bare_exception=e,
119
- ),
120
- status=500,
121
- )
122
-
123
- return wrapper
124
-
125
-
126
- def patch_py4():
96
+ def patch_py4(renderer=None):
97
+ def custom_catch_errors(app_name, func):
98
+ """Catches and logs errors in an action; also sets request.app_name"""
99
+
100
+ @functools.wraps(func)
101
+ def wrapper(*func_args, **func_kwargs):
102
+ try:
103
+ request.app_name = app_name
104
+ ret = func(*func_args, **func_kwargs)
105
+ if isinstance(ret, dict):
106
+ response.headers["Content-Type"] = "application/json"
107
+ ret = dumps(ret)
108
+ return ret
109
+ except HTTP as http:
110
+ response.status = http.status
111
+ response.headers.update(http.headers)
112
+ return http.body
113
+ except bottle.HTTPResponse:
114
+ raise
115
+ except Exception as e:
116
+ snapshot = get_error_snapshot()
117
+ logging.error(snapshot["traceback"])
118
+ ticket_uuid = error_logger.log(request.app_name, snapshot) or "unknown"
119
+ response.status = 500
120
+
121
+ raise bottle.HTTPResponse(
122
+ body=custom_error_page(
123
+ 500,
124
+ button_text=ticket_uuid,
125
+ href="/_dashboard/ticket/" + ticket_uuid,
126
+ traceback=traceback.format_exc(),
127
+ err_type=type(e),
128
+ bare_exception=e,
129
+
130
+ renderer=renderer
131
+ ),
132
+ status=500,
133
+ )
134
+
135
+ return wrapper
136
+
127
137
  action.catch_errors = custom_catch_errors
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: py4web-debug-tools
3
- Version: 0.1.1
3
+ Version: 0.2.0.dev0
4
4
  Summary: Debug Tools for py4web
5
5
  Home-page: https://github.com/trialandsuccess/py4web-debug-tools
6
6
  Author: Robin van der Noord
@@ -13,7 +13,8 @@ Requires-Dist: pydal
13
13
  Requires-Dist: configurable-json
14
14
 
15
15
  # py4web debug tools
16
- Tooling to improve the developer experience when working with py4web.
16
+
17
+ Tooling to improve the developer experience when working with py4web.
17
18
 
18
19
  There are two main tools and some helpers that this package provides.
19
20
 
@@ -23,9 +24,16 @@ There are two main tools and some helpers that this package provides.
23
24
  ## In this package:
24
25
 
25
26
  - `tools`:
26
- - `enable(db: DAL, enabled: bool)`: activate or disable the debug tools (add the custom error screen and create the Debug Bar
27
- Fixture).
28
- The first argument (DAL) is required for the debug bar to collect queries.
27
+ - `enable(db: DAL, enabled?: bool, fancy_rendering?: bool, bar_style?: "bootstrap", slow_threshold_ms?: int)`:
28
+ activate or disable the debug tools (add the custom error screen and create the Debug Bar Fixture).
29
+ The first argument (DAL) is required for the debug bar to collect queries.
30
+ `enabled` can be used to dynamically turn on/off the debug features.
31
+ `fancy_rendering`: This will render the `data` section of the debug bar using `json-browse`, which
32
+ includes `jQuery`. If this clashes with the other JS on a page, this can be set to False to simply show a `<pre>`
33
+ block with data.
34
+ `bar_style`: only bootstrap is supported at this moment.
35
+ `slow_threshold_ms`: amount of milliseconds an SQL query has to take before it is considered a 'slow query' in the
36
+ debug bar.
29
37
  - `debug_bar`: this Fixture can be used on py4web actions to enable the bar for that page.
30
38
  - `catch(*)`: Data and a stacktrace of where `catch()` is called can be found in the catch tab of the debug bar.
31
39
  - `dump(*)`: convert objects to JSON, with a more capable converter than the default json.dumps (e.g. it works better
@@ -0,0 +1,14 @@
1
+ py4web_debug/__init__.py,sha256=OC9yuI3jIBjwi7KGL4uUNtQbyuzZcq913z31gfgSK7s,108
2
+ py4web_debug/core.py,sha256=Vydfzbiv0USgdrT_4hB6OtkXd3lNQOFTNRboI1sxejQ,1989
3
+ py4web_debug/debugbar.py,sha256=bzuJ4FX_bYzz0-V8f9A1bjkEmLbj8q00CAerCAu3aMs,7243
4
+ py4web_debug/dumping.py,sha256=AF4UVSyqI43JgxffjeoozQ0az0J5nvpeZVYtWuUlC6s,3496
5
+ py4web_debug/env.py,sha256=CXJX6nmNgGYL0Z9rt8fmQPO1UAydhW-Cv-zWObPlleQ,82
6
+ py4web_debug/internals.py,sha256=J8pmil-GzxT2BPs4QSoVos5k27vFIi8nGfujTIX90U0,3844
7
+ py4web_debug/templates/debugbar.html,sha256=gbaBLBJpDIti9QAkDWXRP10_LEvCrQmK1GxFlbsHasA,7169
8
+ py4web_debug/templates/dumpdie.html,sha256=pFjRjs2VBZPo1nyfObCO5kksyQS9q-K4EqPdZNFo9lg,60
9
+ py4web_debug/templates/error_default.html,sha256=OGMvbHWb7xX91GH5Kx9WPXqv93ee7UcII5B-nUu3TYk,868
10
+ py4web_debug/templates/fancy_dumpdie.html,sha256=eaquCyAOM1QU3QMv__fIqgbmyFHOnwd495PMGMVOzo8,532
11
+ py4web_debug_tools-0.2.0.dev0.dist-info/METADATA,sha256=j3kOMOtXp9ftrCYPhqw1NJ4Lwgj7xcy6qn7Upv0V1JA,2627
12
+ py4web_debug_tools-0.2.0.dev0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
13
+ py4web_debug_tools-0.2.0.dev0.dist-info/top_level.txt,sha256=sZikb7uiewpax6SbMw-PL1La6xqDq8ZUbN7J9kFKodA,13
14
+ py4web_debug_tools-0.2.0.dev0.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- py4web_debug/__init__.py,sha256=OC9yuI3jIBjwi7KGL4uUNtQbyuzZcq913z31gfgSK7s,108
2
- py4web_debug/core.py,sha256=8vbG9XNZ1GHZUNdGG8-kWl_2TjH47alrkA9SYdFL9yw,1348
3
- py4web_debug/debugbar.py,sha256=mkejAAX9tHVizqlQecoNZOdjJebseo2FAdf0pTzD_K4,7238
4
- py4web_debug/dumping.py,sha256=Jj15zWrbcdn32Ys0nquv1G0aa9wMLCh7njhU4SJD1FU,3515
5
- py4web_debug/env.py,sha256=CXJX6nmNgGYL0Z9rt8fmQPO1UAydhW-Cv-zWObPlleQ,82
6
- py4web_debug/internals.py,sha256=U4S8QSdxErmf3KuVszxrsMTSQCtHzFxQyEO5W2scnn0,3421
7
- py4web_debug/templates/debugbar.html,sha256=gbaBLBJpDIti9QAkDWXRP10_LEvCrQmK1GxFlbsHasA,7169
8
- py4web_debug/templates/dumpdie.html,sha256=pFjRjs2VBZPo1nyfObCO5kksyQS9q-K4EqPdZNFo9lg,60
9
- py4web_debug/templates/error_default.html,sha256=OGMvbHWb7xX91GH5Kx9WPXqv93ee7UcII5B-nUu3TYk,868
10
- py4web_debug/templates/fancy_dumpdie.html,sha256=eaquCyAOM1QU3QMv__fIqgbmyFHOnwd495PMGMVOzo8,532
11
- py4web_debug_tools-0.1.1.dist-info/METADATA,sha256=dJXtQ_ii8ooWi5ZVUrT-Ue1YfJCm9Q-xurC0ZWf77Xw,2014
12
- py4web_debug_tools-0.1.1.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
13
- py4web_debug_tools-0.1.1.dist-info/top_level.txt,sha256=sZikb7uiewpax6SbMw-PL1La6xqDq8ZUbN7J9kFKodA,13
14
- py4web_debug_tools-0.1.1.dist-info/RECORD,,