Skip to content

Commit b53c9d2

Browse files
committed
5.7.6 - Minor fixes
* Better 404 and dual stack handling * fixes for windows
1 parent a9fa941 commit b53c9d2

File tree

16 files changed

+117
-48
lines changed

16 files changed

+117
-48
lines changed

.github/workflows/python-package.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jobs:
7777
7878
- run: |
7979
mk python-release owner=vkottler \
80-
repo=runtimepy version=5.7.5
80+
repo=runtimepy version=5.7.6
8181
if: |
8282
matrix.python-version == '3.12'
8383
&& matrix.system == 'ubuntu-latest'

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
=====================================
33
generator=datazen
44
version=3.1.4
5-
hash=c982a40a3245e84b1ae3b70a17cc6867
5+
hash=41ff8742602f69f2831092a4b666cddc
66
=====================================
77
-->
88

9-
# runtimepy ([5.7.5](https://pypi.org/project/runtimepy/))
9+
# runtimepy ([5.7.6](https://pypi.org/project/runtimepy/))
1010

1111
[![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
1212
![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg)

local/variables/package.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
22
major: 5
33
minor: 7
4-
patch: 5
4+
patch: 6
55
entry: runtimepy

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__"
44

55
[project]
66
name = "runtimepy"
7-
version = "5.7.5"
7+
version = "5.7.6"
88
description = "A framework for implementing Python services."
99
readme = "README.md"
1010
requires-python = ">=3.11"

runtimepy/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# =====================================
22
# generator=datazen
33
# version=3.1.4
4-
# hash=3797b5155c13d8d7422bdbfcb1248cca
4+
# hash=cab7cff2cb194a61178fa386b80eae85
55
# =====================================
66

77
"""
@@ -10,7 +10,7 @@
1010

1111
DESCRIPTION = "A framework for implementing Python services."
1212
PKG_NAME = "runtimepy"
13-
VERSION = "5.7.5"
13+
VERSION = "5.7.6"
1414

1515
# runtimepy-specific content.
1616
METRICS_NAME = "metrics"

runtimepy/data/404.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<html lang=en><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>runtimepy/5.7.6</title><style>@font-face{font-family:CascadiaCode;src:url(/static/woff2/CascadiaCode-Regular.woff2)}@font-face{font-family:CascadiaCode;src:url(/static/woff2/CascadiaCode-Bold.woff2);font-weight:700}@font-face{font-family:CascadiaCode;src:url(/static/woff2/CascadiaCode-BoldItalic.woff2);font-weight:700;font-style:italic}@font-face{font-family:CascadiaCode;src:url(/static/woff2/CascadiaCode-Italic.woff2);font-style:italic}@font-face{font-family:CascadiaMono;src:url(/static/woff2/CascadiaMono-Regular.woff2)}@font-face{font-family:CascadiaMono;src:url(/static/woff2/CascadiaMono-Bold.woff2);font-weight:700}@font-face{font-family:CascadiaMono;src:url(/static/woff2/CascadiaMono-BoldItalic.woff2);font-weight:700;font-style:italic}@font-face{font-family:CascadiaMono;src:url(/static/woff2/CascadiaMono-Italic.woff2);font-style:italic}</style><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css></link>
2+
<link href=https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css rel=stylesheet crossorigin=anonymous></link><style>html{height:100%}body{height:100%;margin:0;overflow:hidden}body>:first-child{height:100%}#runtimepy{height:100%}#runtimepy-tabs{width:min-content}#runtimepy-splash{position:fixed;top:0;left:0;width:100vw;height:100vh;opacity:1}.click-plot{cursor:pointer}.stale{color:var(--bs-warning-text-emphasis)!important}.slider{min-width:8em}.toggle-value{}.window-button{border-right:var(--bs-border-width)var(--bs-border-style)var(--bs-link-hover-color)!important}canvas:focus{outline:none}.flex-column-scroll-bodge{height:100%;flex-wrap:nowrap;overflow-y:scroll;flex-shrink:0}.scroll{overflow:scroll}.tab-content-bodge{width:100%;height:100%}.button-bodge{text-align:left}.collapsing{transition:none!important}.tab-pane.fade{transition:none!important}.modal.fade{transition:none!important}.modal-dialog{width:80%;max-width:80%}.table{margin-bottom:0;width:auto}.table-container{overflow-x:scroll;min-height:fit-content}.channel-column{overflow-y:scroll;overflow-x:hidden;flex-grow:0;flex-shrink:0}.channel-value{min-width:10em}.table>tbody>tr>td{vertical-align:middle}.collapse:not(.show){display:none!important}select.form-select{width:min-content}select.form-select:hover{cursor:pointer}textarea.text-logs{min-height:10em!important;padding-top:0!important;padding-bottom:0!important;border-top:0}.vertical-divider{flex-basis:.75em;flex-grow:0;flex-shrink:0}.vertical-divider:hover{cursor:col-resize;background-color:var(--bs-highlight-bg)!important}button:hover{background-color:var(--bs-tertiary-bg)}.channel-value-input{width:6em}:root,[data-bs-theme=dark],[data-bs-theme=light]{--bs-font-sans-serif:CascadiaCode, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--bs-font-monospace:CascadiaMono, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}</style><div id=runtimepy class="align-items-start bg-body d-flex" data-bs-theme=dark><div class="bg-dark-subtle h-100 d-flex flex-column"><button type=button id=theme-button class="btn btn-secondary rounded-0 font-monospace button-bodge text-nowrap has-tooltip" data-bs-title=" Toggle light/dark." data-bs-placement=right>
3+
<i class="bi bi-lightbulb"></i></button></div><div class="overflow-y-auto d-flex flex-grow-1 h-100 flex-column justify-content-between text-body"><div></div><div class="d-flex flex-row justify-content-between"><div></div><div class="text-body p-3 pb-0"><h1><a href=https://libre-embedded.com><img alt=logo src=https://libre-embedded.com/static/png/chip-circle-bootstrap/128x128.png></a> Resource Not Found (404) <a href=https://libre-embedded.com><img alt=logo src=https://libre-embedded.com/static/png/chip-circle-bootstrap/128x128.png></a></h1><p>(<a href=https://libre-embedded.com>home</a>)</div><div></div></div><div></div></div></div><script>let lightMode=!1;function lightDarkClick(){lightMode=!lightMode,document.getElementById("runtimepy").setAttribute("data-bs-theme",lightMode?"light":"dark"),window.location.hash=lightMode?"#light-mode":""}let lightDarkButton=document.getElementById("theme-button");if(lightDarkButton&&lightDarkButton.addEventListener("click",lightDarkClick),window.location.hash){let e=window.location.hash.slice(1).split(",");e.includes("light-mode")&&lightDarkButton.click()}</script><script src=https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js crossorigin=anonymous></script>

runtimepy/data/server_base.yaml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@ structs:
1313
init:
1414
- runtimepy.net.arbiter.housekeeping.init
1515

16+
config:
17+
# Default redirects.
18+
http_redirects:
19+
"/": "/app.html"
20+
"/index.html": "/app.html"
21+
1622
servers:
1723
- factory: runtimepy_http
1824
kwargs: {port: "$runtimepy_http_server"}
19-
2025
- factory: runtimepy_websocket_json
21-
kwargs: {host: "0.0.0.0", port: "$runtimepy_websocket_json_server"}
26+
kwargs: {port: "$runtimepy_websocket_json_server"}
2227
- factory: runtimepy_websocket_data
23-
kwargs: {host: "0.0.0.0", port: "$runtimepy_websocket_data_server"}
28+
kwargs: {port: "$runtimepy_websocket_data_server"}

runtimepy/net/server/__init__.py

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,18 @@ def init(self) -> None:
109109
with favicon.open("rb") as favicon_fd:
110110
type(self).favicon_data = favicon_fd.read()
111111

112+
def redirect_to(
113+
self,
114+
path: str,
115+
response: ResponseHeader,
116+
status: http.HTTPStatus = http.HTTPStatus.TEMPORARY_REDIRECT,
117+
) -> bytes:
118+
"""Handle responding with redirection status."""
119+
120+
response["Location"] = path
121+
response.status = status
122+
return bytes()
123+
112124
async def try_redirect(
113125
self, path: PathMaybeQuery, response: ResponseHeader
114126
) -> Optional[bytes]:
@@ -118,33 +130,39 @@ async def try_redirect(
118130

119131
curr = Path(path[0])
120132
if curr in self.class_redirect_paths:
121-
response["Location"] = str(self.class_redirect_paths[curr])
122-
response.status = http.HTTPStatus.TEMPORARY_REDIRECT
123-
124-
# No data payload, but signal to caller that a response is ready.
125-
result = bytes()
133+
result = self.redirect_to(
134+
str(self.class_redirect_paths[curr]), response
135+
)
126136

127137
return result
128138

129-
async def render_markdown(
130-
self, path: Path, response: ResponseHeader, **kwargs
139+
def render_markdown(
140+
self, content: str, response: ResponseHeader, **kwargs
131141
) -> bytes:
132-
"""Render a markdown file as HTML and return the result."""
142+
"""Return rendered markdown content."""
133143

134144
document = get_html()
135-
136-
async with aiofiles.open(path, mode="r") as path_fd:
137-
with IndentedFileWriter.string() as writer:
138-
writer.write_markdown(await path_fd.read(), **kwargs)
139-
full_markdown_page(
140-
document,
141-
writer.stream.getvalue(), # type: ignore
142-
)
145+
with IndentedFileWriter.string() as writer:
146+
writer.write_markdown(content, **kwargs)
147+
full_markdown_page(
148+
document,
149+
writer.stream.getvalue(), # type: ignore
150+
)
143151

144152
response["Content-Type"] = f"text/html; charset={DEFAULT_ENCODING}"
145153

146154
return document.encode_str().encode()
147155

156+
async def render_markdown_file(
157+
self, path: Path, response: ResponseHeader, **kwargs
158+
) -> bytes:
159+
"""Render a markdown file as HTML and return the result."""
160+
161+
async with aiofiles.open(path, mode="r") as path_fd:
162+
return self.render_markdown(
163+
await path_fd.read(), response, **kwargs
164+
)
165+
148166
async def try_file(
149167
self, path: PathMaybeQuery, response: ResponseHeader
150168
) -> Optional[bytes]:
@@ -157,9 +175,12 @@ async def try_file(
157175
candidate = search.joinpath(path[0][1:])
158176

159177
# Handle markdown sources.
160-
md_candidate = candidate.with_suffix(".md")
161-
if md_candidate.is_file():
162-
return await self.render_markdown(md_candidate, response)
178+
if candidate.name:
179+
md_candidate = candidate.with_suffix(".md")
180+
if md_candidate.is_file():
181+
return await self.render_markdown_file(
182+
md_candidate, response
183+
)
163184

164185
if candidate.is_file():
165186
mime, encoding = mimetypes.guess_type(candidate, strict=False)
@@ -239,6 +260,7 @@ async def get_handler(
239260
request.log(self.logger, False, level=logging.INFO)
240261

241262
result = None
263+
populated = False
242264

243265
with StringIO() as stream:
244266
if request.target.origin_form:
@@ -266,10 +288,11 @@ async def get_handler(
266288
request_data,
267289
self.json_data,
268290
)
291+
populated = True
269292

270293
# Serve the application.
271294
else:
272-
await html_handler(
295+
populated = await html_handler(
273296
type(self).apps,
274297
stream,
275298
request,
@@ -278,6 +301,7 @@ async def get_handler(
278301
default_app=type(self).default_app,
279302
)
280303

281-
result = stream.getvalue().encode()
304+
if populated:
305+
result = stream.getvalue().encode()
282306

283-
return result
307+
return result or self.redirect_to("/404.html", response)

runtimepy/net/server/app/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ async def setup(app: AppInfo) -> int:
6969
)
7070

7171
# Default application (environment tabs).
72-
web_app = create_app(app, getattr(_import_module(module), method))
73-
RuntimepyServerConnection.apps["/app.html"] = web_app
74-
RuntimepyServerConnection.default_app = web_app
72+
RuntimepyServerConnection.apps["/app.html"] = create_app(
73+
app, getattr(_import_module(module), method)
74+
)
7575

7676
# Register redirects.
7777
redirects: dict[str, str] = app.config_param("http_redirects", {})

runtimepy/net/server/html.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ async def html_handler(
3333
response: ResponseHeader,
3434
request_data: Optional[bytes],
3535
default_app: HtmlApp = None,
36-
) -> None:
36+
) -> bool:
3737
"""Render an HTML document in response to an HTTP request."""
3838

3939
# Set response headers.
@@ -43,3 +43,5 @@ async def html_handler(
4343
app = apps.get(request.target.path, default_app)
4444
if app is not None:
4545
(await app(get_html(), request, response, request_data)).render(stream)
46+
47+
return app is not None

0 commit comments

Comments
 (0)