webdev 1.1.0__tar.gz → 2.0.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 (38) hide show
  1. webdev-2.0.0/.gitignore +18 -0
  2. {webdev-1.1.0 → webdev-2.0.0}/LICENSE +1 -1
  3. webdev-2.0.0/PKG-INFO +240 -0
  4. webdev-2.0.0/README.rst +210 -0
  5. webdev-2.0.0/pyproject.toml +81 -0
  6. webdev-2.0.0/tests/conftest.py +28 -0
  7. webdev-2.0.0/tests/test_config.py +87 -0
  8. webdev-2.0.0/tests/test_index.py +68 -0
  9. webdev-2.0.0/tests/test_livereload.py +44 -0
  10. webdev-2.0.0/tests/test_net.py +53 -0
  11. webdev-2.0.0/tests/test_server.py +121 -0
  12. webdev-2.0.0/tests/test_static.py +70 -0
  13. webdev-2.0.0/webdev/__init__.py +30 -0
  14. webdev-2.0.0/webdev/__main__.py +29 -0
  15. webdev-2.0.0/webdev/cli.py +330 -0
  16. webdev-2.0.0/webdev/config.py +210 -0
  17. webdev-2.0.0/webdev/index.py +298 -0
  18. webdev-2.0.0/webdev/livereload.py +169 -0
  19. webdev-2.0.0/webdev/net.py +145 -0
  20. webdev-2.0.0/webdev/run.py +124 -0
  21. webdev-2.0.0/webdev/server.py +336 -0
  22. webdev-2.0.0/webdev/static.py +195 -0
  23. webdev-2.0.0/webdev/templates/dark/base.html.jinja2 +17 -0
  24. webdev-2.0.0/webdev/templates/dark/index.html.jinja2 +71 -0
  25. webdev-2.0.0/webdev/templates/dark/partials/sort.js.jinja2 +52 -0
  26. webdev-2.0.0/webdev/templates/dark/partials/styles.css.jinja2 +110 -0
  27. webdev-2.0.0/webdev/tls.py +159 -0
  28. webdev-1.1.0/MANIFEST.in +0 -2
  29. webdev-1.1.0/PKG-INFO +0 -123
  30. webdev-1.1.0/README.rst +0 -105
  31. webdev-1.1.0/setup.cfg +0 -5
  32. webdev-1.1.0/setup.py +0 -68
  33. webdev-1.1.0/webdev +0 -188
  34. webdev-1.1.0/webdev.egg-info/PKG-INFO +0 -123
  35. webdev-1.1.0/webdev.egg-info/SOURCES.txt +0 -10
  36. webdev-1.1.0/webdev.egg-info/dependency_links.txt +0 -1
  37. webdev-1.1.0/webdev.egg-info/pbr.json +0 -1
  38. webdev-1.1.0/webdev.egg-info/top_level.txt +0 -1
@@ -0,0 +1,18 @@
1
+ *.py[cod]
2
+ __pycache__
3
+
4
+ # Packages
5
+ *.egg
6
+ *.egg-info
7
+ dist/*
8
+ build/*
9
+
10
+ # Virtual environments & tooling caches
11
+ .venv/
12
+ .tox/
13
+ .pytest_cache/
14
+ .mypy_cache/
15
+ .ruff_cache/
16
+
17
+ # Misc
18
+ *~
@@ -1,4 +1,4 @@
1
- Apache License
1
+ Apache License
2
2
  Version 2.0, January 2004
3
3
  http://www.apache.org/licenses/
4
4
 
webdev-2.0.0/PKG-INFO ADDED
@@ -0,0 +1,240 @@
1
+ Metadata-Version: 2.5
2
+ Name: webdev
3
+ Version: 2.0.0
4
+ Summary: Small, fast development web server with a modern dark directory index
5
+ Project-URL: Homepage, https://github.com/kuralabs/webdev
6
+ Project-URL: Repository, https://github.com/kuralabs/webdev
7
+ Project-URL: Issues, https://github.com/kuralabs/webdev/issues
8
+ Author-email: "KuraLabs S.R.L" <info@kuralabs.io>, Carlos Jenkins <carlos@jenkins.co.cr>
9
+ License-Expression: Apache-2.0
10
+ License-File: LICENSE
11
+ Keywords: dev,development,http,server,static,web
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: colorlog>=6.8
23
+ Requires-Dist: jinja2>=3.1
24
+ Requires-Dist: qrcode>=7.4
25
+ Requires-Dist: starlette>=0.37
26
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
27
+ Requires-Dist: uvicorn[standard]>=0.29
28
+ Requires-Dist: watchfiles>=0.21
29
+ Description-Content-Type: text/x-rst
30
+
31
+ ======================
32
+ Development Web Server
33
+ ======================
34
+
35
+ About
36
+ =====
37
+
38
+ ``webdev`` is a small, fast development web server. It serves any local
39
+ directory over HTTP (or HTTPS) with developer-friendly conveniences, and ships
40
+ a modern, dark-themed directory index.
41
+
42
+ It is built on `Starlette <https://www.starlette.io/>`_ and
43
+ `Uvicorn <https://www.uvicorn.org/>`_ (an ASGI stack), and requires Python 3.10+.
44
+
45
+ Features
46
+ ========
47
+
48
+ - **Serve any directory** — passed as the first positional argument; defaults to
49
+ the current directory.
50
+ - **Graceful when the folder disappears** — responds with ``404`` instead of
51
+ crashing if the served directory is removed, and **recovers automatically**
52
+ the moment it is recreated. Paths are resolved against the live filesystem on
53
+ every request, which is great with build systems that perform clean/rebuild
54
+ actions.
55
+ - **Automatic port selection** — if the base port is busy, the next free port in
56
+ a range is used. Can be forced to a single port.
57
+ - **Serve a not-yet-existing folder** — ``--future`` assumes the folder will be
58
+ created later.
59
+ - **Modern dark directory index** — breadcrumbs, file-type icons, human-readable
60
+ sizes, modified dates and client-side sortable columns. Themable: drop a new
61
+ directory under ``webdev/templates/<theme>/``.
62
+ - **Live reload** — connected browsers refresh automatically on file changes
63
+ (via a WebSocket; a tiny auto-reconnecting snippet is injected into HTML).
64
+ - **No-cache headers** for always-fresh development content.
65
+ - **SPA fallback** — optionally serve ``index.html`` for unknown routes.
66
+ - **Gzip**, **CORS**, optional **HTTPS** (self-signed if no cert/key is given),
67
+ and a startup **QR code / LAN URL** for quick mobile testing.
68
+ - **Config file** — every option can be set via ``webdev.toml`` (or a
69
+ ``[tool.webdev]`` table). Precedence: CLI flag > config file > default.
70
+
71
+ Installation
72
+ ============
73
+
74
+ ::
75
+
76
+ pip install webdev
77
+
78
+ Usage
79
+ =====
80
+
81
+ ::
82
+
83
+ $ webdev --help
84
+ usage: webdev [-h] [-v] [--version] [-i IP] [-p PORT] [-f] [-r RANGE] [-u]
85
+ [--reload | --no-reload] [--cache] [--no-cache]
86
+ [--spa | --no-spa] [--gzip | --no-gzip] [--cors | --no-cors]
87
+ [--qr | --no-qr] [--color | --no-color]
88
+ [--https | --no-https] [--cert CERT]
89
+ [--key KEY] [--theme THEME] [--config CONFIG]
90
+ [path]
91
+
92
+ Examples::
93
+
94
+ # Serve the current directory on http://localhost:8080
95
+ webdev
96
+
97
+ # Serve ./public on a forced port, over HTTPS, without live reload
98
+ webdev -p 3000 -f --https --no-reload ./public
99
+
100
+ # Single-page app: fall back to index.html for client-side routes
101
+ webdev --spa ./dist
102
+
103
+ Options and config keys
104
+ -----------------------
105
+
106
+ Every option has both a CLI flag and a config-file key. Booleans accept
107
+ ``--x`` / ``--no-x``.
108
+
109
+ ===================== ======================== ================== ==========
110
+ Feature CLI flag Config key Default
111
+ ===================== ======================== ================== ==========
112
+ Serve path positional ``path`` ``path`` CWD
113
+ Bind IP ``-i/--ip`` ``ip`` 0.0.0.0
114
+ Base port ``-p/--port`` ``port`` 8080
115
+ Force exact port ``-f/--force-port`` ``force_port`` false
116
+ Port range ``-r/--range`` ``range`` 20
117
+ Serve missing folder ``-u/--future`` ``future`` false
118
+ No-cache headers ``--no-cache/--cache`` ``no_cache`` enabled
119
+ Live reload ``--reload/--no-reload`` ``reload`` enabled
120
+ SPA fallback ``--spa/--no-spa`` ``spa`` disabled
121
+ Gzip ``--gzip/--no-gzip`` ``gzip`` enabled
122
+ CORS ``--cors/--no-cors`` ``cors`` disabled
123
+ QR / LAN URL ``--qr/--no-qr`` ``qr`` enabled
124
+ Colorized logs ``--color/--no-color`` ``color`` enabled
125
+ HTTPS/TLS ``--https/--no-https`` ``https`` disabled
126
+ TLS cert / key ``--cert`` / ``--key`` ``cert`` / ``key`` self-signed
127
+ Index theme ``--theme`` ``theme`` dark
128
+ ===================== ======================== ================== ==========
129
+
130
+ Example ``webdev.toml``::
131
+
132
+ port = 3000
133
+ force_port = true
134
+ spa = true
135
+ cors = true
136
+
137
+ Design notes
138
+ ============
139
+
140
+ - **Framework:** Starlette + Uvicorn was chosen over a full framework such as
141
+ Sanic because this is a static file server: Starlette provides exactly the
142
+ primitives needed (routing, static serving, WebSockets, middleware) with a
143
+ small surface, and Uvicorn handles fast binding, HTTP keep-alive, HTTPS and
144
+ graceful shutdown.
145
+ - **The legacy reload hack is gone.** The old server patched ``send_head`` to
146
+ ``chdir`` back into the served folder; modern ``http.server`` captures the
147
+ working directory in the handler constructor, so that hook stopped running and
148
+ a removed folder crashed the request. ``webdev`` now resolves every request
149
+ against the live filesystem, so missing folders yield a real ``404`` and
150
+ recover automatically.
151
+
152
+ Development
153
+ ===========
154
+
155
+ The development workflow is driven by `tox <https://tox.wiki/>`_ using
156
+ `uv <https://docs.astral.sh/uv/>`_ as the backend (via ``tox-uv``). ``uv`` is the
157
+ only tool you need preinstalled::
158
+
159
+ # Lint, format-check and type-check
160
+ tox -e style
161
+
162
+ # Run the test suite
163
+ tox -e test
164
+
165
+ # Build and verify the distribution
166
+ tox -e build
167
+
168
+ Each environment declares its own tooling dependencies in ``tox.ini`` so it
169
+ installs only what it needs.
170
+
171
+ Changelog
172
+ =========
173
+
174
+ 2.0.0
175
+ -----
176
+
177
+ **Reimplemented** on a modern async stack (Starlette + Uvicorn), Python 3.10+.
178
+
179
+ **New**
180
+
181
+ - Modern dark, themable directory index (breadcrumbs, icons, sizes, dates,
182
+ sortable columns), rendered with Jinja2.
183
+ - Live reload over WebSocket.
184
+ - No-cache headers, SPA fallback, gzip, CORS, optional HTTPS (self-signed),
185
+ startup QR code / LAN URL.
186
+ - Colorized, level-aware log output (``--color/--no-color``, via colorlog).
187
+ - Config-file support (``webdev.toml`` / ``[tool.webdev]``); every option has a
188
+ CLI flag and a config key (precedence CLI > config > default).
189
+ - ``pyproject.toml`` packaging; tox + uv development workflow; Ruff, mypy and a
190
+ pytest suite.
191
+
192
+ **Fixed**
193
+
194
+ - The directory auto-recovery feature, which silently broke on modern Python
195
+ when ``http.server`` began capturing the working directory in the request
196
+ handler constructor. Path resolution is now per-request against the live
197
+ filesystem.
198
+
199
+ **Changed**
200
+
201
+ - Dropped Python 2 support.
202
+
203
+ 1.1.0
204
+ -----
205
+
206
+ **New**
207
+
208
+ - Added support for Python 3 (issue #5).
209
+ - Added support for incremental automatic port selection (issue #2).
210
+ - Added the ``--future`` flag that allows the webserver to serve a non-existent
211
+ folder, assuming it will be created in the future (issue #3).
212
+
213
+ **Changed**
214
+
215
+ - Ctrl+C is now handled (issue #4).
216
+
217
+ 1.0.0
218
+ -----
219
+
220
+ - Initial release.
221
+
222
+ License
223
+ =======
224
+
225
+ ::
226
+
227
+ Copyright (C) 2015-2026 Carlos Jenkins <carlos@jenkins.co.cr>
228
+
229
+ Licensed under the Apache License, Version 2.0 (the "License");
230
+ you may not use this file except in compliance with the License.
231
+ You may obtain a copy of the License at
232
+
233
+ http://www.apache.org/licenses/LICENSE-2.0
234
+
235
+ Unless required by applicable law or agreed to in writing,
236
+ software distributed under the License is distributed on an
237
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
238
+ KIND, either express or implied. See the License for the
239
+ specific language governing permissions and limitations
240
+ under the License.
@@ -0,0 +1,210 @@
1
+ ======================
2
+ Development Web Server
3
+ ======================
4
+
5
+ About
6
+ =====
7
+
8
+ ``webdev`` is a small, fast development web server. It serves any local
9
+ directory over HTTP (or HTTPS) with developer-friendly conveniences, and ships
10
+ a modern, dark-themed directory index.
11
+
12
+ It is built on `Starlette <https://www.starlette.io/>`_ and
13
+ `Uvicorn <https://www.uvicorn.org/>`_ (an ASGI stack), and requires Python 3.10+.
14
+
15
+ Features
16
+ ========
17
+
18
+ - **Serve any directory** — passed as the first positional argument; defaults to
19
+ the current directory.
20
+ - **Graceful when the folder disappears** — responds with ``404`` instead of
21
+ crashing if the served directory is removed, and **recovers automatically**
22
+ the moment it is recreated. Paths are resolved against the live filesystem on
23
+ every request, which is great with build systems that perform clean/rebuild
24
+ actions.
25
+ - **Automatic port selection** — if the base port is busy, the next free port in
26
+ a range is used. Can be forced to a single port.
27
+ - **Serve a not-yet-existing folder** — ``--future`` assumes the folder will be
28
+ created later.
29
+ - **Modern dark directory index** — breadcrumbs, file-type icons, human-readable
30
+ sizes, modified dates and client-side sortable columns. Themable: drop a new
31
+ directory under ``webdev/templates/<theme>/``.
32
+ - **Live reload** — connected browsers refresh automatically on file changes
33
+ (via a WebSocket; a tiny auto-reconnecting snippet is injected into HTML).
34
+ - **No-cache headers** for always-fresh development content.
35
+ - **SPA fallback** — optionally serve ``index.html`` for unknown routes.
36
+ - **Gzip**, **CORS**, optional **HTTPS** (self-signed if no cert/key is given),
37
+ and a startup **QR code / LAN URL** for quick mobile testing.
38
+ - **Config file** — every option can be set via ``webdev.toml`` (or a
39
+ ``[tool.webdev]`` table). Precedence: CLI flag > config file > default.
40
+
41
+ Installation
42
+ ============
43
+
44
+ ::
45
+
46
+ pip install webdev
47
+
48
+ Usage
49
+ =====
50
+
51
+ ::
52
+
53
+ $ webdev --help
54
+ usage: webdev [-h] [-v] [--version] [-i IP] [-p PORT] [-f] [-r RANGE] [-u]
55
+ [--reload | --no-reload] [--cache] [--no-cache]
56
+ [--spa | --no-spa] [--gzip | --no-gzip] [--cors | --no-cors]
57
+ [--qr | --no-qr] [--color | --no-color]
58
+ [--https | --no-https] [--cert CERT]
59
+ [--key KEY] [--theme THEME] [--config CONFIG]
60
+ [path]
61
+
62
+ Examples::
63
+
64
+ # Serve the current directory on http://localhost:8080
65
+ webdev
66
+
67
+ # Serve ./public on a forced port, over HTTPS, without live reload
68
+ webdev -p 3000 -f --https --no-reload ./public
69
+
70
+ # Single-page app: fall back to index.html for client-side routes
71
+ webdev --spa ./dist
72
+
73
+ Options and config keys
74
+ -----------------------
75
+
76
+ Every option has both a CLI flag and a config-file key. Booleans accept
77
+ ``--x`` / ``--no-x``.
78
+
79
+ ===================== ======================== ================== ==========
80
+ Feature CLI flag Config key Default
81
+ ===================== ======================== ================== ==========
82
+ Serve path positional ``path`` ``path`` CWD
83
+ Bind IP ``-i/--ip`` ``ip`` 0.0.0.0
84
+ Base port ``-p/--port`` ``port`` 8080
85
+ Force exact port ``-f/--force-port`` ``force_port`` false
86
+ Port range ``-r/--range`` ``range`` 20
87
+ Serve missing folder ``-u/--future`` ``future`` false
88
+ No-cache headers ``--no-cache/--cache`` ``no_cache`` enabled
89
+ Live reload ``--reload/--no-reload`` ``reload`` enabled
90
+ SPA fallback ``--spa/--no-spa`` ``spa`` disabled
91
+ Gzip ``--gzip/--no-gzip`` ``gzip`` enabled
92
+ CORS ``--cors/--no-cors`` ``cors`` disabled
93
+ QR / LAN URL ``--qr/--no-qr`` ``qr`` enabled
94
+ Colorized logs ``--color/--no-color`` ``color`` enabled
95
+ HTTPS/TLS ``--https/--no-https`` ``https`` disabled
96
+ TLS cert / key ``--cert`` / ``--key`` ``cert`` / ``key`` self-signed
97
+ Index theme ``--theme`` ``theme`` dark
98
+ ===================== ======================== ================== ==========
99
+
100
+ Example ``webdev.toml``::
101
+
102
+ port = 3000
103
+ force_port = true
104
+ spa = true
105
+ cors = true
106
+
107
+ Design notes
108
+ ============
109
+
110
+ - **Framework:** Starlette + Uvicorn was chosen over a full framework such as
111
+ Sanic because this is a static file server: Starlette provides exactly the
112
+ primitives needed (routing, static serving, WebSockets, middleware) with a
113
+ small surface, and Uvicorn handles fast binding, HTTP keep-alive, HTTPS and
114
+ graceful shutdown.
115
+ - **The legacy reload hack is gone.** The old server patched ``send_head`` to
116
+ ``chdir`` back into the served folder; modern ``http.server`` captures the
117
+ working directory in the handler constructor, so that hook stopped running and
118
+ a removed folder crashed the request. ``webdev`` now resolves every request
119
+ against the live filesystem, so missing folders yield a real ``404`` and
120
+ recover automatically.
121
+
122
+ Development
123
+ ===========
124
+
125
+ The development workflow is driven by `tox <https://tox.wiki/>`_ using
126
+ `uv <https://docs.astral.sh/uv/>`_ as the backend (via ``tox-uv``). ``uv`` is the
127
+ only tool you need preinstalled::
128
+
129
+ # Lint, format-check and type-check
130
+ tox -e style
131
+
132
+ # Run the test suite
133
+ tox -e test
134
+
135
+ # Build and verify the distribution
136
+ tox -e build
137
+
138
+ Each environment declares its own tooling dependencies in ``tox.ini`` so it
139
+ installs only what it needs.
140
+
141
+ Changelog
142
+ =========
143
+
144
+ 2.0.0
145
+ -----
146
+
147
+ **Reimplemented** on a modern async stack (Starlette + Uvicorn), Python 3.10+.
148
+
149
+ **New**
150
+
151
+ - Modern dark, themable directory index (breadcrumbs, icons, sizes, dates,
152
+ sortable columns), rendered with Jinja2.
153
+ - Live reload over WebSocket.
154
+ - No-cache headers, SPA fallback, gzip, CORS, optional HTTPS (self-signed),
155
+ startup QR code / LAN URL.
156
+ - Colorized, level-aware log output (``--color/--no-color``, via colorlog).
157
+ - Config-file support (``webdev.toml`` / ``[tool.webdev]``); every option has a
158
+ CLI flag and a config key (precedence CLI > config > default).
159
+ - ``pyproject.toml`` packaging; tox + uv development workflow; Ruff, mypy and a
160
+ pytest suite.
161
+
162
+ **Fixed**
163
+
164
+ - The directory auto-recovery feature, which silently broke on modern Python
165
+ when ``http.server`` began capturing the working directory in the request
166
+ handler constructor. Path resolution is now per-request against the live
167
+ filesystem.
168
+
169
+ **Changed**
170
+
171
+ - Dropped Python 2 support.
172
+
173
+ 1.1.0
174
+ -----
175
+
176
+ **New**
177
+
178
+ - Added support for Python 3 (issue #5).
179
+ - Added support for incremental automatic port selection (issue #2).
180
+ - Added the ``--future`` flag that allows the webserver to serve a non-existent
181
+ folder, assuming it will be created in the future (issue #3).
182
+
183
+ **Changed**
184
+
185
+ - Ctrl+C is now handled (issue #4).
186
+
187
+ 1.0.0
188
+ -----
189
+
190
+ - Initial release.
191
+
192
+ License
193
+ =======
194
+
195
+ ::
196
+
197
+ Copyright (C) 2015-2026 Carlos Jenkins <carlos@jenkins.co.cr>
198
+
199
+ Licensed under the Apache License, Version 2.0 (the "License");
200
+ you may not use this file except in compliance with the License.
201
+ You may obtain a copy of the License at
202
+
203
+ http://www.apache.org/licenses/LICENSE-2.0
204
+
205
+ Unless required by applicable law or agreed to in writing,
206
+ software distributed under the License is distributed on an
207
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
208
+ KIND, either express or implied. See the License for the
209
+ specific language governing permissions and limitations
210
+ under the License.
@@ -0,0 +1,81 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "webdev"
7
+ dynamic = ["version"]
8
+ description = "Small, fast development web server with a modern dark directory index"
9
+ readme = "README.rst"
10
+ requires-python = ">=3.10"
11
+ license = "Apache-2.0"
12
+ license-files = ["LICENSE"]
13
+ authors = [
14
+ { name = "KuraLabs S.R.L", email = "info@kuralabs.io" },
15
+ { name = "Carlos Jenkins", email = "carlos@jenkins.co.cr" },
16
+ ]
17
+ keywords = ["development", "web", "server", "static", "http", "dev"]
18
+ classifiers = [
19
+ "Development Status :: 5 - Production/Stable",
20
+ "Intended Audience :: Developers",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Topic :: Utilities",
27
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
28
+ ]
29
+ dependencies = [
30
+ "starlette>=0.37",
31
+ "uvicorn[standard]>=0.29",
32
+ "jinja2>=3.1",
33
+ "watchfiles>=0.21",
34
+ "qrcode>=7.4",
35
+ "colorlog>=6.8",
36
+ "tomli>=2.0; python_version < '3.11'",
37
+ ]
38
+
39
+ [project.scripts]
40
+ webdev = "webdev.cli:main"
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/kuralabs/webdev"
44
+ Repository = "https://github.com/kuralabs/webdev"
45
+ Issues = "https://github.com/kuralabs/webdev/issues"
46
+
47
+ [tool.hatch.version]
48
+ path = "webdev/__init__.py"
49
+
50
+ [tool.hatch.build.targets.wheel]
51
+ packages = ["webdev"]
52
+
53
+ [tool.hatch.build.targets.sdist]
54
+ include = [
55
+ "/webdev",
56
+ "/tests",
57
+ "/README.rst",
58
+ "/LICENSE",
59
+ ]
60
+
61
+ [tool.ruff]
62
+ line-length = 88
63
+ target-version = "py310"
64
+
65
+ [tool.ruff.lint]
66
+ # flake8-equivalent rule set: pycodestyle (E, W), pyflakes (F) and
67
+ # pep8-naming (N). Import sorting (I) is intentionally left out.
68
+ select = ["E", "W", "F", "N"]
69
+
70
+ [tool.ruff.format]
71
+ quote-style = "single"
72
+
73
+ [tool.mypy]
74
+ python_version = "3.10"
75
+ warn_unused_ignores = true
76
+ warn_redundant_casts = true
77
+ ignore_missing_imports = true
78
+
79
+ [tool.pytest.ini_options]
80
+ testpaths = ["tests"]
81
+ addopts = "-ra"
@@ -0,0 +1,28 @@
1
+ """Shared pytest fixtures."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+
9
+ from webdev.config import Settings
10
+
11
+
12
+ @pytest.fixture
13
+ def served_dir(tmp_path: Path) -> Path:
14
+ (tmp_path / 'hello.txt').write_text('hello world')
15
+ (tmp_path / 'page.html').write_text('<html><body>Hi</body></html>')
16
+ (tmp_path / 'sub').mkdir()
17
+ (tmp_path / 'sub' / 'nested.txt').write_text('nested')
18
+ return tmp_path
19
+
20
+
21
+ @pytest.fixture
22
+ def settings(served_dir: Path) -> Settings:
23
+ return Settings(
24
+ path=str(served_dir),
25
+ reload=False,
26
+ qr=False,
27
+ gzip=False,
28
+ ).normalized()
@@ -0,0 +1,87 @@
1
+ """Tests for config-file loading and CLI/config precedence."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+
9
+ from webdev.cli import resolve_settings
10
+ from webdev.config import Settings, load_config_file
11
+
12
+
13
+ def test_defaults() -> None:
14
+ s = Settings()
15
+ assert s.port == 8080
16
+ assert s.no_cache is True
17
+ assert s.reload is True
18
+ assert s.spa is False
19
+ assert s.color is True
20
+ assert s.theme == 'dark'
21
+
22
+
23
+ def test_force_port_zeroes_range() -> None:
24
+ s = Settings(force_port=True, range=20).normalized()
25
+ assert s.range == 0
26
+
27
+
28
+ def test_load_flat_config(tmp_path: Path) -> None:
29
+ cfg = tmp_path / 'webdev.toml'
30
+ cfg.write_text('port = 9999\nspa = true\ntheme = "dark"\n')
31
+ values = load_config_file(cfg, tmp_path)
32
+ assert values == {'port': 9999, 'spa': True, 'theme': 'dark'}
33
+
34
+
35
+ def test_load_tool_table(tmp_path: Path) -> None:
36
+ cfg = tmp_path / 'pyproject.toml'
37
+ cfg.write_text('[tool.webdev]\nport = 7000\ncors = true\n')
38
+ values = load_config_file(cfg, tmp_path)
39
+ assert values == {'port': 7000, 'cors': True}
40
+
41
+
42
+ def test_unknown_keys_ignored(tmp_path: Path) -> None:
43
+ cfg = tmp_path / 'webdev.toml'
44
+ cfg.write_text('port = 1234\nbogus = "x"\n')
45
+ values = load_config_file(cfg, tmp_path)
46
+ assert values == {'port': 1234}
47
+
48
+
49
+ def test_missing_config_raises(tmp_path: Path) -> None:
50
+ with pytest.raises(FileNotFoundError):
51
+ load_config_file(tmp_path / 'nope.toml', tmp_path)
52
+
53
+
54
+ def test_cli_overrides_config(tmp_path: Path) -> None:
55
+ (tmp_path / 'webdev.toml').write_text('port = 9999\n')
56
+ s = resolve_settings(['-p', '8081', str(tmp_path)])
57
+ assert s.port == 8081 # CLI wins over config
58
+
59
+
60
+ def test_config_used_when_no_cli(tmp_path: Path) -> None:
61
+ (tmp_path / 'webdev.toml').write_text('port = 9999\n')
62
+ s = resolve_settings([str(tmp_path)])
63
+ assert s.port == 9999 # config wins over default
64
+
65
+
66
+ def test_boolean_optional_flags() -> None:
67
+ s = resolve_settings(['--no-reload', '--spa', '--no-gzip', '/tmp'])
68
+ assert s.reload is False
69
+ assert s.spa is True
70
+ assert s.gzip is False
71
+
72
+
73
+ def test_no_color_flag() -> None:
74
+ s = resolve_settings(['--no-color', '/tmp'])
75
+ assert s.color is False
76
+
77
+
78
+ def test_verbose_counts_from_cli() -> None:
79
+ # Regression: ``-v`` with the unset sentinel used to crash argparse.
80
+ assert resolve_settings(['/tmp']).verbose == 0
81
+ assert resolve_settings(['-v', '/tmp']).verbose == 1
82
+ assert resolve_settings(['-vvv', '/tmp']).verbose == 3
83
+
84
+
85
+ def test_verbose_config_used_when_no_cli(tmp_path: Path) -> None:
86
+ (tmp_path / 'webdev.toml').write_text('verbose = 2\n')
87
+ assert resolve_settings([str(tmp_path)]).verbose == 2