py4web-debug-tools 1.3.3__tar.gz → 1.4.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.
Files changed (23) hide show
  1. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/PKG-INFO +14 -14
  2. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/README.md +1 -3
  3. py4web_debug_tools-1.4.0/pyproject.toml +179 -0
  4. py4web_debug_tools-1.3.3/pyproject.toml → py4web_debug_tools-1.4.0/pyproject.toml.orig +49 -20
  5. py4web_debug_tools-1.4.0/src/py4web_debug/__about__.py +3 -0
  6. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/core.py +2 -1
  7. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/dumping.py +24 -2
  8. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/internals.py +11 -1
  9. py4web_debug_tools-1.3.3/.gitignore +0 -8
  10. py4web_debug_tools-1.3.3/CHANGELOG.md +0 -79
  11. py4web_debug_tools-1.3.3/src/py4web_debug/__about__.py +0 -1
  12. py4web_debug_tools-1.3.3/tests/test_dumping.py +0 -99
  13. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/__init__.py +0 -0
  14. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/_internals.py +0 -0
  15. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/debugbar.py +0 -0
  16. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/env.py +0 -0
  17. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/py.typed +0 -0
  18. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/templates/debugbar.html +0 -0
  19. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/templates/dumpdie.html +0 -0
  20. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/templates/error_default.html +0 -0
  21. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/templates/fancy_dumpdie.html +0 -0
  22. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/types.py +0 -0
  23. {py4web_debug_tools-1.3.3 → py4web_debug_tools-1.4.0}/src/py4web_debug/wsgi.py +0 -0
@@ -1,10 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py4web-debug-tools
3
- Version: 1.3.3
3
+ Version: 1.4.0
4
4
  Summary: Debug Tools for py4web
5
- Project-URL: Documentation, https://github.com/trialandsuccess/py4web-debug-tools#readme
6
- Project-URL: Issues, https://github.com/trialandsuccess/py4web-debug-tools/issues
7
- Project-URL: Source, https://github.com/trialandsuccess/py4web-debug-tools
5
+ Keywords:
6
+ Author: Robin van der Noord
8
7
  Author-email: Robin van der Noord <contact@trialandsuccess.nl>
9
8
  License-Expression: MIT
10
9
  Classifier: Development Status :: 4 - Beta
@@ -12,19 +11,22 @@ Classifier: Programming Language :: Python
12
11
  Classifier: Programming Language :: Python :: 3.10
13
12
  Classifier: Programming Language :: Python :: 3.11
14
13
  Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.14
15
15
  Classifier: Programming Language :: Python :: Implementation :: CPython
16
16
  Classifier: Programming Language :: Python :: Implementation :: PyPy
17
- Requires-Python: >=3.10
18
- Requires-Dist: configurable-json>=1.1
19
17
  Requires-Dist: py4web
18
+ Requires-Dist: yatl
20
19
  Requires-Dist: pydal
20
+ Requires-Dist: configurable-json>=1.1
21
21
  Requires-Dist: typing-extensions
22
- Requires-Dist: yatl
22
+ Requires-Dist: su6[all] ; extra == 'dev'
23
+ Requires-Dist: pytest ; extra == 'dev'
24
+ Requires-Dist: dotmap ; extra == 'dev'
25
+ Requires-Python: >=3.10
26
+ Project-URL: Documentation, https://github.com/trialandsuccess/py4web-debug-tools#readme
27
+ Project-URL: Issues, https://github.com/trialandsuccess/py4web-debug-tools/issues
28
+ Project-URL: Source, https://github.com/trialandsuccess/py4web-debug-tools
23
29
  Provides-Extra: dev
24
- Requires-Dist: dotmap; extra == 'dev'
25
- Requires-Dist: hatch; extra == 'dev'
26
- Requires-Dist: pytest; extra == 'dev'
27
- Requires-Dist: su6[all]; extra == 'dev'
28
30
  Description-Content-Type: text/markdown
29
31
 
30
32
  # py4web debug tools
@@ -109,9 +111,7 @@ You can modify your `py4web_uwsgi.py` file:
109
111
  # yes:
110
112
  from py4web_debug import wsgi
111
113
 
112
- application = wsgi(apps_folder="apps",
113
- password_file='password.txt',
114
- dashboard_mode="full")
114
+ application = wsgi(apps_folder="apps", password_file="password.txt", dashboard_mode="full")
115
115
  ```
116
116
 
117
117
  After this, you can `tools.enable` as before.
@@ -80,9 +80,7 @@ You can modify your `py4web_uwsgi.py` file:
80
80
  # yes:
81
81
  from py4web_debug import wsgi
82
82
 
83
- application = wsgi(apps_folder="apps",
84
- password_file='password.txt',
85
- dashboard_mode="full")
83
+ application = wsgi(apps_folder="apps", password_file="password.txt", dashboard_mode="full")
86
84
  ```
87
85
 
88
86
  After this, you can `tools.enable` as before.
@@ -0,0 +1,179 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.12.4,<0.13"]
3
+ build-backend = "uv_build"
4
+
5
+ [template.plugins.default]
6
+ src-layout = true
7
+
8
+ [tool.setuptools.package-data]
9
+ py4web_debug = ["py.typed"]
10
+
11
+ [tool.uv.build-backend]
12
+ module-name = "py4web_debug"
13
+
14
+ [tool.vommit]
15
+ allow_breaking_bang = true
16
+ allow_breaking_footer = true
17
+ prerelease_token = "beta"
18
+ confirm = true
19
+ ignore = []
20
+
21
+ [tool.vommit.git]
22
+ enabled = true
23
+ origin = "origin"
24
+ branch = "master"
25
+ on_wrong_branch = "error"
26
+ tag_format = "v{version}"
27
+ commit_format = "{version}"
28
+
29
+ [tool.vommit.changelog]
30
+ enabled = true
31
+ file = "CHANGELOG.md"
32
+ include_prereleases = false
33
+ placeholder = "<!--next-version-placeholder-->"
34
+ placeholder_regex = "<auto>"
35
+ entry_title_format = "## v{version} ({date:%Y-%m-%d})"
36
+
37
+ [tool.vommit.changelog.levels]
38
+ feat = "Feature{s}"
39
+ fix = "Fix{es}"
40
+ break = "Breaking Change{s}"
41
+ docs = "Documentation"
42
+ perf = "Performance"
43
+
44
+ [tool.vommit.pypi]
45
+ enabled = true
46
+ use_keyring = true
47
+
48
+ [tool.vommit.commands]
49
+ clean = "rm -rf ./dist"
50
+ build = "uv build"
51
+ publish = "uv publish"
52
+ post_publish = ""
53
+
54
+ [tool.vommit.version_bump_map]
55
+ break = "major"
56
+ feat = "minor"
57
+ fix = "patch"
58
+ perf = "patch"
59
+
60
+ [tool.pytest.ini_options]
61
+ testpaths = ["tests"]
62
+ pythonpath = ["."]
63
+
64
+ [tool.su6]
65
+ directory = "src"
66
+ include = []
67
+ exclude = ["pytest"]
68
+ stop-after-first-failure = true
69
+
70
+ [tool.black]
71
+ target-version = ["py311"]
72
+ line-length = 120
73
+ extend-exclude = '''
74
+ # A regex preceded with ^/ will apply only to files and directories
75
+ # in the root of the project.
76
+ (
77
+ ^.*\.bak\/.+ # ignore every .bak directory
78
+ ^.*venv.+\/.+ # ignore every venv directory
79
+ venv.+|.+\.bak # idk why it suddenly works, let's not touch it
80
+ )
81
+ '''
82
+
83
+ [tool.mypy]
84
+ python_version = "3.11"
85
+ no_implicit_optional = false
86
+ ignore_missing_imports = true
87
+ strict = true
88
+ exclude = [
89
+ "venv",
90
+ ".bak",
91
+ ]
92
+
93
+ [tool.ruff]
94
+ target-version = "py311"
95
+ line-length = 120
96
+ extend-exclude = [
97
+ "*.bak/",
98
+ "venv*/",
99
+ ]
100
+
101
+ [tool.ruff.lint]
102
+ select = [
103
+ "F",
104
+ "E",
105
+ "W",
106
+ "Q",
107
+ "A",
108
+ "SIM",
109
+ "ARG",
110
+ "RUF",
111
+ ]
112
+ unfixable = ["F401"]
113
+ ignore = ["RUF013"]
114
+
115
+ [tool.bandit]
116
+ exclude_dirs = [
117
+ ".bak",
118
+ "venv",
119
+ ]
120
+ skips = ["B108"]
121
+
122
+ [tool.isort]
123
+ profile = "black"
124
+ extend_skip_glob = ["*.bak/*"]
125
+
126
+ [tool.pydocstyle]
127
+ convention = "google"
128
+ match-dir = '(?!venv)[^\.].*'
129
+ add_select = [
130
+ "D213",
131
+ "D416",
132
+ "D417",
133
+ ]
134
+ add_ignore = [
135
+ "D200",
136
+ "D212",
137
+ ]
138
+
139
+ [project]
140
+ name = "py4web-debug-tools"
141
+ description = "Debug Tools for py4web"
142
+ readme = "README.md"
143
+ requires-python = ">=3.10"
144
+ license = "MIT"
145
+ keywords = []
146
+ classifiers = [
147
+ "Development Status :: 4 - Beta",
148
+ "Programming Language :: Python",
149
+ "Programming Language :: Python :: 3.10",
150
+ "Programming Language :: Python :: 3.11",
151
+ "Programming Language :: Python :: 3.12",
152
+ "Programming Language :: Python :: 3.14",
153
+ "Programming Language :: Python :: Implementation :: CPython",
154
+ "Programming Language :: Python :: Implementation :: PyPy",
155
+ ]
156
+ dependencies = [
157
+ "py4web",
158
+ "yatl",
159
+ "pydal",
160
+ "configurable-json >= 1.1",
161
+ "typing-extensions",
162
+ ]
163
+ version = "1.4.0"
164
+
165
+ [[project.authors]]
166
+ name = "Robin van der Noord"
167
+ email = "contact@trialandsuccess.nl"
168
+
169
+ [project.urls]
170
+ Documentation = "https://github.com/trialandsuccess/py4web-debug-tools#readme"
171
+ Issues = "https://github.com/trialandsuccess/py4web-debug-tools/issues"
172
+ Source = "https://github.com/trialandsuccess/py4web-debug-tools"
173
+
174
+ [project.optional-dependencies]
175
+ dev = [
176
+ "su6[all]",
177
+ "pytest",
178
+ "dotmap",
179
+ ]
@@ -1,6 +1,6 @@
1
1
  [build-system]
2
- requires = ["hatchling"]
3
- build-backend = "hatchling.build"
2
+ requires = ["uv_build>=0.12.4,<0.13"]
3
+ build-backend = "uv_build"
4
4
 
5
5
  [template.plugins.default]
6
6
  src-layout = true
@@ -8,17 +8,11 @@ src-layout = true
8
8
  [tool.setuptools.package-data]
9
9
  "py4web_debug" = ["py.typed"]
10
10
 
11
- [tool.hatch.build.targets.wheel]
12
- packages = ["src/py4web_debug"]
13
-
14
- [tool.hatch.build]
15
- artifacts = [
16
- "src/py4web_debug/templates/*.html"
17
- ]
11
+ [tool.uv.build-backend]
12
+ module-name = "py4web_debug"
18
13
 
19
14
  [project]
20
15
  name = "py4web-debug-tools"
21
- dynamic = ["version"]
22
16
  description = 'Debug Tools for py4web'
23
17
  readme = "README.md"
24
18
  requires-python = ">=3.10"
@@ -33,6 +27,7 @@ classifiers = [
33
27
  "Programming Language :: Python :: 3.10",
34
28
  "Programming Language :: Python :: 3.11",
35
29
  "Programming Language :: Python :: 3.12",
30
+ "Programming Language :: Python :: 3.14",
36
31
  "Programming Language :: Python :: Implementation :: CPython",
37
32
  "Programming Language :: Python :: Implementation :: PyPy",
38
33
  ]
@@ -43,6 +38,7 @@ dependencies = [
43
38
  "configurable-json >= 1.1",
44
39
  "typing-extensions",
45
40
  ]
41
+ version = "1.4.0"
46
42
 
47
43
 
48
44
  [project.urls]
@@ -53,23 +49,56 @@ Source = "https://github.com/trialandsuccess/py4web-debug-tools"
53
49
  [project.optional-dependencies]
54
50
  dev = [
55
51
  "su6[all]",
56
- "hatch",
57
52
  "pytest",
58
53
  "dotmap",
59
54
  ]
60
55
 
61
56
 
62
- [tool.hatch.version]
63
- path = "src/py4web_debug/__about__.py"
57
+ [tool.vommit]
58
+ allow_breaking_bang = true
59
+ allow_breaking_footer = true
60
+ prerelease_token = "beta"
61
+ confirm = true
62
+ ignore = []
64
63
 
65
- [tool.semantic_release]
64
+ [tool.vommit.git]
65
+ enabled = true
66
+ origin = "origin"
66
67
  branch = "master"
67
- version_variable = "src/py4web_debug/__about__.py:__version__"
68
- change_log = "CHANGELOG.md"
69
- upload_to_repository = false
70
- upload_to_release = false
71
- build_command = "hatch build"
72
-
68
+ on_wrong_branch = "error"
69
+ tag_format = "v{version}"
70
+ commit_format = "{version}"
71
+
72
+ [tool.vommit.changelog]
73
+ enabled = true
74
+ file = "CHANGELOG.md"
75
+ include_prereleases = false
76
+ placeholder = "<!--next-version-placeholder-->"
77
+ placeholder_regex = "<auto>"
78
+ entry_title_format = "## v{version} ({date:%Y-%m-%d})"
79
+
80
+ [tool.vommit.changelog.levels]
81
+ feat = "Feature{s}"
82
+ fix = "Fix{es}"
83
+ break = "Breaking Change{s}"
84
+ docs = "Documentation"
85
+ perf = "Performance"
86
+
87
+ [tool.vommit.pypi]
88
+ enabled = true
89
+ use_keyring = true
90
+
91
+ [tool.vommit.commands]
92
+ clean = "rm -rf ./dist"
93
+ build = "uv build"
94
+ publish = "uv publish"
95
+ post_publish = ""
96
+
97
+ [tool.vommit.version_bump_map]
98
+ break = "major"
99
+ feat = "minor"
100
+ fix = "patch"
101
+ perf = "patch"
73
102
 
74
103
  [tool.pytest.ini_options]
75
104
  testpaths = ["tests"]
@@ -0,0 +1,3 @@
1
+ from importlib.metadata import version
2
+
3
+ __version__ = version("py4web-debug-tools")
@@ -52,6 +52,7 @@ class DebugTools:
52
52
  debugbar_fancy_rendering: bool = True,
53
53
  debugbar_style: typing.Literal["bootstrap"] = "bootstrap",
54
54
  debugbar_slow_threshold_ms: int = 10,
55
+ errorpage_json_renderer: T_Renderer = None,
55
56
  ) -> None:
56
57
  """
57
58
  By default, on_off looks at PY4WEB_DEBUG_MODE in the env
@@ -73,7 +74,7 @@ class DebugTools:
73
74
 
74
75
  if enabled:
75
76
  if self.config.errorpage.enabled:
76
- patch_py4(errorpage_renderer, error_logger)
77
+ patch_py4(errorpage_renderer, error_logger, json_renderer=errorpage_json_renderer)
77
78
 
78
79
  if self.config.debugbar.enabled:
79
80
  if not db:
@@ -102,8 +102,30 @@ def dump(data: typing.Iterable[typing.Any], with_headers: bool = True) -> str:
102
102
  if with_headers:
103
103
  response.headers["Content-Type"] = "application/json"
104
104
 
105
- return json.dumps(data, indent=2, cls=DDJsonEncoder)
106
- # return json.dumps(data, indent=2, default=DDJsonEncoder._default)
105
+ try:
106
+ return json.dumps(data, indent=2, cls=DDJsonEncoder)
107
+ # return json.dumps(data, indent=2, default=DDJsonEncoder._default)
108
+ except Exception as e:
109
+ raise DumpSerializationError(data, e) from e
110
+
111
+
112
+ class DumpSerializationError(TypeError):
113
+ """
114
+ Raised by dump() when the data (or something nested inside it) could not be
115
+ JSON-serialized, even with DDJsonEncoder's custom object-to-dict fallback rules.
116
+
117
+ Wraps the original error (still available via `__cause__`/`.original`) with
118
+ context about what was being dumped, so this is actionable instead of a bare
119
+ stdlib error surfacing from deep inside json.dumps()/DDJsonEncoder internals.
120
+ """
121
+
122
+ def __init__(self, data: typing.Any, original: Exception) -> None:
123
+ self.data = data
124
+ self.original = original
125
+ super().__init__(
126
+ f"dump() could not JSON-serialize this data (top-level type: {type(data).__name__}): "
127
+ f"{original.__class__.__name__}: {original}"
128
+ )
107
129
 
108
130
 
109
131
  class DumpDieError(Exception):
@@ -51,6 +51,7 @@ def custom_error_page(
51
51
  err_type: str | typing.Type[Exception] | None = None,
52
52
  bare_exception: typing.Optional[Exception | str] = None,
53
53
  renderer: T_Renderer = None,
54
+ json_renderer: T_Renderer = None,
54
55
  ) -> str:
55
56
  if err_type and not isinstance(err_type, str):
56
57
  err_type = err_type.__name__
@@ -78,6 +79,10 @@ def custom_error_page(
78
79
  # if client accepts 'application/json' - return json
79
80
  if re.search(REGEX_APPJSON, request.headers.get("accept", "")):
80
81
  response.status = code
82
+ response.headers["Content-Type"] = "application/json"
83
+ if json_renderer:
84
+ return json_renderer(context)
85
+
81
86
  return dump(context)
82
87
  # else - return html error-page
83
88
 
@@ -119,9 +124,12 @@ T_Logger = typing.Callable[[ErrorSnapshot], None]
119
124
  class patch_py4:
120
125
  # THIS IS ONLY CALLED ONCE, WHEN tools.enable IS CALLED OR py4web_debug.wsgi IS USED!
121
126
  renderer: T_Renderer | None = None
127
+ json_renderer: T_Renderer | None = None
122
128
  logger: T_Logger | typing.Literal[False] | None = None
123
129
 
124
- def __init__(_, renderer: T_Renderer = None, logger: T_Logger | bool | None = None):
130
+ def __init__(
131
+ _, renderer: T_Renderer = None, logger: T_Logger | bool | None = None, json_renderer: T_Renderer = None
132
+ ):
125
133
  # note: don't use self, use patch_py4 instead
126
134
  if logger is True:
127
135
  # use default logger:
@@ -169,6 +177,7 @@ class patch_py4:
169
177
  err_type=type(e),
170
178
  bare_exception=e,
171
179
  renderer=getattr(patch_py4, "renderer", None),
180
+ json_renderer=getattr(patch_py4, "json_renderer", None),
172
181
  ),
173
182
  status=500,
174
183
  )
@@ -181,5 +190,6 @@ class patch_py4:
181
190
 
182
191
  # allows swapping renderer on the fly with tools.set_renderer():
183
192
  patch_py4.renderer = renderer
193
+ patch_py4.json_renderer = json_renderer
184
194
  _logger = getattr(patch_py4, "logger", None) or logger
185
195
  patch_py4.logger = staticmethod(_logger) if callable(_logger) else _logger
@@ -1,8 +0,0 @@
1
- venv
2
- apps
3
- *.pyc
4
- build
5
- dist
6
- *.egg-info
7
- .idea
8
- .env
@@ -1,79 +0,0 @@
1
- # Changelog
2
-
3
- <!--next-version-placeholder-->
4
-
5
- ## v1.3.3 (2026-08-06)
6
-
7
-
8
-
9
- ## v1.3.2 (2025-12-12)
10
-
11
- ### Fix
12
-
13
- * Use existing debug-tools `dump` instead of py4web's `dumps`, hopefully to prevent circular import error ([`0d5afad`](https://github.com/trialandsuccess/py4web-debug-tools/commit/0d5afad2904834cf80d9252b89816b7ae360249a))
14
-
15
- ## v1.3.1 (2025-04-01)
16
-
17
- ### Fix
18
-
19
- * **dbg:** Detect more variable names ([`a80556c`](https://github.com/trialandsuccess/py4web-debug-tools/commit/a80556c2d35f89a5c0f900518074b7800dfeb844))
20
-
21
- ## v1.3.0 (2025-04-01)
22
-
23
- ### Feature
24
-
25
- * Add `dbg` helper ([`b7529e3`](https://github.com/trialandsuccess/py4web-debug-tools/commit/b7529e32bcaa88a237fa505d26afe3f013d0bdf2))
26
-
27
- ## v1.2.2 (2024-11-20)
28
-
29
- ### Fix
30
-
31
- * Replace 'self' with 'patch_py4' for better stability ([`6987942`](https://github.com/trialandsuccess/py4web-debug-tools/commit/6987942347b993cf767166b6f3dac90e75989130))
32
-
33
- ## v1.2.1 (2024-11-20)
34
-
35
- ### Fix
36
-
37
- * Allow logger to be set to False in case you're using py4webs default logger (PY4WEB_ERRORLOG=:stderr) ([`43639f3`](https://github.com/trialandsuccess/py4web-debug-tools/commit/43639f377d92b2f8b96bbdb31f61251fde95f831))
38
-
39
- ## v1.2.0 (2023-11-06)
40
- ### Feature
41
- * Allow custom `error_logger` for tools.enable ([`a8301bc`](https://github.com/trialandsuccess/py4web-debug-tools/commit/a8301bc1762984fc5af420075acdc09e97bcc378))
42
-
43
- ## v1.1.4 (2023-07-19)
44
-
45
-
46
- ## v1.1.3 (2023-07-19)
47
- ### Fix
48
- * Error page renderer can also be False (in addition to None or a method) ([`41d33aa`](https://github.com/trialandsuccess/py4web-debug-tools/commit/41d33aae9e307034b2818641c041f1dd6c8364fc))
49
-
50
- ## v1.1.2 (2023-07-19)
51
- ### Fix
52
- * Wrapper can also return an HTTP response! ([`4431d80`](https://github.com/trialandsuccess/py4web-debug-tools/commit/4431d8094162dc9100aab2798571b87e5a076ed4))
53
-
54
- ## v1.1.1 (2023-07-19)
55
- ### Fix
56
- * Bump `configurable-json` to new typed version ([`52fa55a`](https://github.com/trialandsuccess/py4web-debug-tools/commit/52fa55a3eb0d8e95064ddb9ecae820a2fa229a9a))
57
-
58
- ### Documentation
59
- * Updated changelog ([`15a9517`](https://github.com/trialandsuccess/py4web-debug-tools/commit/15a9517f149b9bff272306c64165e9e62845e8d0))
60
-
61
- ## v1.1.0 (2023-07-19)
62
-
63
- Moved to hatch build system.
64
-
65
- ## v1.0.0 (2023-07-17)
66
- ### Feature
67
- * **config:** Multiple minor changes: ([`bb14fec`](https://github.com/trialandsuccess/py4web-debug-tools/commit/bb14fec07780826eb7f550cb9c5d0ed24df1f8b3))
68
- * **debugbar:** Added required files ([`afc690f`](https://github.com/trialandsuccess/py4web-debug-tools/commit/afc690fb96ec2c9d54547434caffe7c1469302b1))
69
- * **p4w-debug:** Initial module setup ([`b36c53c`](https://github.com/trialandsuccess/py4web-debug-tools/commit/b36c53c30206189bfcfcbea1c7236f12ae8becab))
70
-
71
- ### Fix
72
- * Expose `is_debug` from the top-level library ([`dd73055`](https://github.com/trialandsuccess/py4web-debug-tools/commit/dd7305596a90db3b5902dcaa7d4e5f5c22c693c6))
73
- * Expose `is_debug` from the top-level library ([`ee835e8`](https://github.com/trialandsuccess/py4web-debug-tools/commit/ee835e84983e74aa03825b346074469563e58fe6))
74
- * **tools:** 'db' is only required when debugbar is used (debugbar_enabled=True) ([`dba49b8`](https://github.com/trialandsuccess/py4web-debug-tools/commit/dba49b85149b8aa8cf5992529be6942cd9b08de4))
75
-
76
- ### Documentation
77
- * **readme:** Updated for style and missing `wsgi` ([`9394747`](https://github.com/trialandsuccess/py4web-debug-tools/commit/9394747b2e48a30c1902f5852955f85f9524c6b3))
78
- * **tools:** Include all parameters of .enable() ([`90ccffc`](https://github.com/trialandsuccess/py4web-debug-tools/commit/90ccffcf6342b68434ad64ad945aea4c7c516cf1))
79
- * **README:** Updated explaination ([`3f3b6b3`](https://github.com/trialandsuccess/py4web-debug-tools/commit/3f3b6b3f2f2fca29f4ac15851082e2e49ae288f8))
@@ -1 +0,0 @@
1
- __version__ = "1.3.3"
@@ -1,99 +0,0 @@
1
- from types import SimpleNamespace
2
-
3
- from dotmap import DotMap
4
- from pytest import MonkeyPatch
5
-
6
- from src.py4web_debug.dumping import DDJsonEncoder, DumpDieError, dd, dump
7
-
8
-
9
- def _sample_dotmap() -> DotMap:
10
- data = DotMap()
11
- data.a = 1
12
- data.b.c = "nested"
13
- return data
14
-
15
-
16
- def test_dump_handles_dotmap():
17
- # dump() should serialize a DotMap without crashing.
18
- result = dump(_sample_dotmap(), with_headers=False)
19
-
20
- assert '"a": 1' in result
21
- assert '"c": "nested"' in result
22
-
23
-
24
- def test_dd_handles_dotmap(monkeypatch: MonkeyPatch):
25
- # dd() should not crash on a DotMap; in debug mode it raises a DumpDieError
26
- # (by design) instead of letting json serialization blow up.
27
- monkeypatch.setenv("PY4WEB_DEBUG_MODE", "1")
28
-
29
- try:
30
- dd(_sample_dotmap(), fancy=False)
31
- except DumpDieError as e:
32
- assert '"a": 1' in str(e)
33
- else:
34
- raise AssertionError("dd() should raise DumpDieError in debug mode")
35
-
36
-
37
- def test_default_crashes_on_bare_dotmap():
38
- """
39
- DotMap's __getattr__ returns an empty DotMap for ANY missing attribute
40
- instead of raising AttributeError, so hasattr(dotmap, "as_list") is
41
- always True even though `as_list` isn't a real method on it.
42
-
43
- Note: dump()/dd() never hit this for a *bare* DotMap in practice, because
44
- DotMap is a dict subclass and gets recognized (and recursed into) by
45
- configurablejson's encoder before _default() is ever called - see
46
- test_dump_handles_dotmap above, which passes fine. This test instead
47
- calls DDJsonEncoder._default() directly, to pin the underlying flaw that
48
- both this and the TypeDAL-model-class crash below share.
49
-
50
- FAILS until DDJsonEncoder._default() is fixed:
51
- TypeError: 'DotMap' object is not callable
52
- """
53
- d = DotMap({"label": "Dashboard"})
54
-
55
- DDJsonEncoder._default(d)
56
-
57
-
58
- class _TableMetaLike(type):
59
- """
60
- Reproduces TypeDAL's `TableMeta.__getattr__`: for an unbound model class,
61
- any attribute that isn't a real column falls through to `return None`
62
- instead of raising AttributeError (see typedal/tables.py TableMeta.__getattr__).
63
- """
64
-
65
- def __getattr__(cls, name: str) -> None:
66
- return None
67
-
68
-
69
- class Offering(metaclass=_TableMetaLike):
70
- """Stand-in for a real TypeDAL model class, e.g. used as menu["model"]."""
71
-
72
-
73
- def test_dump_reproduces_as_list_crash_on_model_class_value():
74
- """
75
- Real-world crash reproduction (whitelabel AdminMenuFixture):
76
- admin nav menu entries are plain dicts that carry a `"model"` key holding
77
- the *model class itself* (e.g. `Offering`, not an instance). Wrapping such
78
- an entry in a DotMap (for dot-access in templates) doesn't cause this —
79
- a plain dict crashes identically, see test below.
80
-
81
- FAILS until DDJsonEncoder._default() is fixed: it does
82
- `if hasattr(o, "as_list"): o.as_list()` without checking it's callable.
83
- TypeDAL's TableMeta.__getattr__ returns None for unknown attributes on a
84
- model class instead of raising AttributeError, so hasattr() lies and
85
- `o.as_list()` becomes `None()`. Matches production traceback exactly:
86
- dumping.py:42 in _default -> TypeError: 'NoneType' object is not callable.
87
- """
88
- entry = DotMap({"label": "Aanbod beheren", "icon": "fas fa-masks-theater", "model": Offering})
89
-
90
- dump(entry, with_headers=False)
91
-
92
-
93
- def test_dump_reproduces_as_list_crash_without_dotmap():
94
- # Same crash with a plain dict: DotMap is incidental, not the cause.
95
- entry1 = {"label": "Aanbod beheren", "model": Offering}
96
- dump(entry1, with_headers=False)
97
-
98
- entry2 = SimpleNamespace(label="Aanbod beheren", model=Offering)
99
- dump(entry2, with_headers=False)