runtimepy 5.10.1__py3-none-any.whl → 5.10.2__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.
runtimepy/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # =====================================
2
2
  # generator=datazen
3
3
  # version=3.1.4
4
- # hash=649474f8a3122bccd6febc4bb2156b11
4
+ # hash=9114f245a523f60ffbbfdd040548fc7c
5
5
  # =====================================
6
6
 
7
7
  """
@@ -10,7 +10,7 @@ Useful defaults and other package metadata.
10
10
 
11
11
  DESCRIPTION = "A framework for implementing Python services."
12
12
  PKG_NAME = "runtimepy"
13
- VERSION = "5.10.1"
13
+ VERSION = "5.10.2"
14
14
 
15
15
  # runtimepy-specific content.
16
16
  METRICS_NAME = "metrics"
Binary file
@@ -1,5 +1,5 @@
1
1
  <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
2
- <circle r="50.0" cx="50.0" cy="50.0" style="fill: #ADB5BD" />
2
+ <circle r="50.0" cx="50.0" cy="50.0" style="fill: #6C757D" />
3
3
  <rect x="16.666666666666664" y="33.333333333333336" width="16.666666666666668" height="8.333333333333334" rx="4.166666666666667" ry="4.166666666666667" style="fill: #495057" />
4
4
  <rect x="16.666666666666664" y="45.833333333333336" width="16.666666666666668" height="8.333333333333334" rx="4.166666666666667" ry="4.166666666666667" style="fill: #495057" />
5
5
  <rect x="16.666666666666664" y="58.333333333333336" width="16.666666666666668" height="8.333333333333334" rx="4.166666666666667" ry="4.166666666666667" style="fill: #495057" />
@@ -24,6 +24,7 @@ from runtimepy.net.http.request_target import PathMaybeQuery
24
24
  from runtimepy.net.http.response import ResponseHeader
25
25
  from runtimepy.net.server.html import HtmlApp, HtmlApps, get_html, html_handler
26
26
  from runtimepy.net.server.json import encode_json, json_handler
27
+ from runtimepy.net.server.markdown import markdown_for_dir
27
28
  from runtimepy.net.tcp.http import HttpConnection
28
29
  from runtimepy.util import normalize_root, path_has_part, read_binary
29
30
 
@@ -177,10 +178,20 @@ class RuntimepyServerConnection(HttpConnection):
177
178
 
178
179
  result = None
179
180
 
180
- # Try serving the path as a file.
181
+ # Keep track of directories encountered.
182
+ directories: list[Path] = []
183
+
184
+ # Build a list of all candidate files to check.
185
+ candidates: list[Path] = []
181
186
  for search in self.paths:
182
187
  candidate = search.joinpath(path[0][1:])
188
+ if candidate.is_dir():
189
+ directories.append(candidate)
190
+ candidates.append(candidate.joinpath("index.html"))
191
+ else:
192
+ candidates.append(candidate)
183
193
 
194
+ for candidate in candidates:
184
195
  # Handle markdown sources.
185
196
  if candidate.name:
186
197
  md_candidate = candidate.with_suffix(".md")
@@ -189,6 +200,7 @@ class RuntimepyServerConnection(HttpConnection):
189
200
  md_candidate, response, path[1]
190
201
  )
191
202
 
203
+ # Handle files.
192
204
  if candidate.is_file():
193
205
  mime, encoding = mimetypes.guess_type(candidate, strict=False)
194
206
 
@@ -203,9 +215,18 @@ class RuntimepyServerConnection(HttpConnection):
203
215
 
204
216
  # Return the file data.
205
217
  result = await read_binary(candidate)
206
-
207
218
  break
208
219
 
220
+ # Handle a directory as a last resort.
221
+ if not result and directories:
222
+ result = self.render_markdown(
223
+ markdown_for_dir(
224
+ directories[0], {"applications": self.apps.keys()}
225
+ ),
226
+ response,
227
+ path[1],
228
+ )
229
+
209
230
  return result
210
231
 
211
232
  def handle_command(
@@ -278,7 +299,7 @@ class RuntimepyServerConnection(HttpConnection):
278
299
  return self.favicon_data
279
300
 
280
301
  # Try serving a file and handling redirects.
281
- for handler in [self.try_file, self.try_redirect]:
302
+ for handler in [self.try_redirect, self.try_file]:
282
303
  result = await handler(
283
304
  request.target.origin_form, response
284
305
  )
@@ -0,0 +1,53 @@
1
+ """
2
+ A module implementing web server markdown interfaces.
3
+ """
4
+
5
+ # built-in
6
+ from io import StringIO
7
+ from pathlib import Path
8
+ from typing import Iterable, cast
9
+
10
+ # third-party
11
+ from vcorelib.io.file_writer import IndentedFileWriter
12
+ from vcorelib.paths import rel
13
+
14
+ LOGO_MARKDOWN = (
15
+ "[![logo](https://libre-embedded.com/static/"
16
+ "png/chip-circle-bootstrap/128x128.png)](https://libre-embedded.com)"
17
+ )
18
+
19
+
20
+ def markdown_for_dir(
21
+ path: Path, extra_links: dict[str, Iterable[str]] = None
22
+ ) -> str:
23
+ """Get markdown data for a directory."""
24
+
25
+ with IndentedFileWriter.string() as writer:
26
+ writer.write(f"# Directory {LOGO_MARKDOWN} Viewer")
27
+ with writer.padding():
28
+ writer.write("---")
29
+
30
+ if extra_links:
31
+ for category, apps in extra_links.items():
32
+ writer.write(f"## {category}")
33
+ with writer.padding():
34
+ for app in apps:
35
+ writer.write(f"* [{app}]({app})")
36
+
37
+ writer.write(f"## `{path}`")
38
+ writer.empty()
39
+
40
+ writer.write("* [..](..)")
41
+
42
+ for item in path.iterdir():
43
+ curr = rel(item, base=path)
44
+
45
+ name = f"`{curr}`"
46
+ if item.is_dir():
47
+ name = f"**{name}**"
48
+
49
+ writer.write(f"* [{name}]({curr})")
50
+
51
+ result: str = cast(StringIO, writer.stream).getvalue()
52
+
53
+ return result
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: runtimepy
3
- Version: 5.10.1
3
+ Version: 5.10.2
4
4
  Summary: A framework for implementing Python services.
5
5
  Home-page: https://github.com/vkottler/runtimepy
6
6
  Author: Vaughn Kottler
@@ -17,11 +17,11 @@ Classifier: License :: OSI Approved :: MIT License
17
17
  Requires-Python: >=3.12
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
+ Requires-Dist: psutil
20
21
  Requires-Dist: websockets
22
+ Requires-Dist: aiofiles
21
23
  Requires-Dist: vcorelib>=3.5.1
22
24
  Requires-Dist: svgen>=0.7.4
23
- Requires-Dist: psutil
24
- Requires-Dist: aiofiles
25
25
  Provides-Extra: test
26
26
  Requires-Dist: pylint; extra == "test"
27
27
  Requires-Dist: flake8; extra == "test"
@@ -42,6 +42,7 @@ Requires-Dist: types-setuptools; extra == "test"
42
42
  Requires-Dist: uvloop; (sys_platform != "win32" and sys_platform != "cygwin") and extra == "test"
43
43
  Dynamic: author
44
44
  Dynamic: home-page
45
+ Dynamic: license-file
45
46
  Dynamic: requires-dist
46
47
  Dynamic: requires-python
47
48
 
@@ -49,11 +50,11 @@ Dynamic: requires-python
49
50
  =====================================
50
51
  generator=datazen
51
52
  version=3.1.4
52
- hash=a8fb82d7f11d94c5c3ad4adea1026d9e
53
+ hash=887650b5b88ff596b3ab067e01de6fbd
53
54
  =====================================
54
55
  -->
55
56
 
56
- # runtimepy ([5.10.1](https://pypi.org/project/runtimepy/))
57
+ # runtimepy ([5.10.2](https://pypi.org/project/runtimepy/))
57
58
 
58
59
  [![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
59
60
  ![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg)
@@ -1,4 +1,4 @@
1
- runtimepy/__init__.py,sha256=uZ2pA4H_IPEEbU0jsR1ZRf1g4gyxUbUuexk35KLNE3g,391
1
+ runtimepy/__init__.py,sha256=UgkSoIgawdI6UkjuTTg7IIFglX0-IDgjuWRujjVjW_s,391
2
2
  runtimepy/__main__.py,sha256=OPAed6hggoQdw-6QAR62mqLC-rCkdDhOq0wyeS2vDRI,332
3
3
  runtimepy/app.py,sha256=sTvatbsGZ2Hdel36Si_WUbNMtg9CzsJyExr5xjIcxDE,970
4
4
  runtimepy/dev_requirements.txt,sha256=j0dh11ztJAzfaUL0iFheGjaZj9ppDzmTkclTT8YKO8c,230
@@ -45,7 +45,7 @@ runtimepy/data/404.html,sha256=sn0Mcntzb1-AekukYZAQqz43xfmZtTWa2n2HD2ItqDM,4697
45
45
  runtimepy/data/browser.yaml,sha256=oc5KEV1C1uAJ4MkhNo4hyVVfJtZvHelRNqzNvD313Ow,79
46
46
  runtimepy/data/dummy_load.yaml,sha256=PfKRXXgZnENRMSd68eznSMTV8xanVH5JY4FmoZRPFGY,1985
47
47
  runtimepy/data/factories.yaml,sha256=esuoouMre8w7siZfBoZKqC-5myghJ_WwOOCDIq135zg,1899
48
- runtimepy/data/favicon.ico,sha256=iPOtqnqrFVr5tNBM6kTsw7nh0KullEz4TpS6CEQ2HVM,362870
48
+ runtimepy/data/favicon.ico,sha256=boxAGaHbUjMFrOO2TZpsO0nIRC-LUgwHVQYOiG1YQnM,362870
49
49
  runtimepy/data/sample_telemetry.yaml,sha256=OpdFurkvtWJGaNl9LMlU2rKo15AaVVr-U_hoZfsbp-Y,695
50
50
  runtimepy/data/server.yaml,sha256=wS_Ceiu2TpkfPurpqoYoPlgzc9DAWtUd24MW7t-S5rU,97
51
51
  runtimepy/data/server_base.yaml,sha256=R_varVgGPGV4nxWYYwKUnHC9ufINi4V92YVhxCCC5wg,875
@@ -107,7 +107,7 @@ runtimepy/data/schemas/has_factory.yaml,sha256=Rsqrxv3HEBUGxPcW6CN7QczA96iBSgtm0
107
107
  runtimepy/data/schemas/has_markdown.yaml,sha256=LbnEQRYdk5KR_GgORmIOiKjRg-HF8wjhA6Dm6pSm66I,45
108
108
  runtimepy/data/schemas/has_name.yaml,sha256=I5YkXxQRYz-1-49CLyr_NA2zVqyRVQCAsb-icTaxF4s,59
109
109
  runtimepy/data/schemas/has_request_flag.yaml,sha256=S8ly8g-FkrCVNEizbXPqicg_hpGvtH7WRsHZAA4uhjc,66
110
- runtimepy/data/static/svg/chip-circle-bootstrap.svg,sha256=cGCiwiVWoO39vzdAt683Fk3W42XE6-RGT0QZtsDAv4o,3124
110
+ runtimepy/data/static/svg/chip-circle-bootstrap.svg,sha256=uwo45eymlKmV3AOQ217Etarjb0UBzJJNFLa8JgQx86Y,3124
111
111
  runtimepy/data/static/woff2/CascadiaCode-Bold.woff2,sha256=vnrwH_YbbVnq0_aedSECg-eieky43TOK3MxtCWIc_Ug,154520
112
112
  runtimepy/data/static/woff2/CascadiaCode-BoldItalic.woff2,sha256=OOWBMRXRJJKxEfEIlKKnT-tFqoZKqGw5hMa69t-gp1c,113236
113
113
  runtimepy/data/static/woff2/CascadiaCode-Italic.woff2,sha256=RbBc8ehMSl_ixGWnEe7zn841xDsyxdDJTo3rkYY-bf8,111628
@@ -178,9 +178,10 @@ runtimepy/net/http/request_target.py,sha256=EE1aI5VSARw1h93jyZvP56ir5O5fjd6orYK-
178
178
  runtimepy/net/http/response.py,sha256=Sup8W_A0ADNzR5olKrQsVNhsQXUwPOD-eJLlLOgYlAY,2316
179
179
  runtimepy/net/http/state.py,sha256=qCMN8aWfCRfU9XP-cIhSOo2RqfljTjbQRCflfcy2bfY,1626
180
180
  runtimepy/net/http/version.py,sha256=mp6rgIM7-VUVKLCA0Uw96CmBkL0ET860lDVVEewpZ7w,1098
181
- runtimepy/net/server/__init__.py,sha256=Kg1NenLXSDWYa7NFvQLu2hHU4p6sYXwspv2NSZ7VfSo,9647
181
+ runtimepy/net/server/__init__.py,sha256=RojwvQgqc0rTl36rXDSiM56tVXtD15T5OkpuJanlNjY,10438
182
182
  runtimepy/net/server/html.py,sha256=ufg0PQF_iUE7PT0n3Pn3jTcun7mspZUI6_ooblcNnvI,1217
183
183
  runtimepy/net/server/json.py,sha256=a7vM5yfq2er4DexzFqEMnxoMGDeuywKkVH4-uNJBAik,2522
184
+ runtimepy/net/server/markdown.py,sha256=DFjGbvIST4HXRhtTTvXVQ9ZAwrIfVzPlcU1dW8JYya8,1381
184
185
  runtimepy/net/server/app/__init__.py,sha256=beU67t7zoKGlO7aldjQMUwYLm9mSlc78eMQazri-otw,3012
185
186
  runtimepy/net/server/app/base.py,sha256=HF_Qa3ufrZNaYBVnBwGi-Siv3nneqEo01j5h5pK-ZTk,1871
186
187
  runtimepy/net/server/app/create.py,sha256=eRT8qxubht5A7149Xol3Z8rkdYd_pjNLqrlyMnXk-Zg,2660
@@ -284,9 +285,9 @@ runtimepy/tui/task.py,sha256=nUZo9fuOC-k1Wpqdzkv9v1tQirCI28fZVgcC13Ijvus,1093
284
285
  runtimepy/tui/channels/__init__.py,sha256=evDaiIn-YS9uGhdo8ZGtP9VK1ek6sr_P1nJ9JuSET0o,4536
285
286
  runtimepy/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
286
287
  runtimepy/ui/controls.py,sha256=yvT7h3thbYaitsakcIAJ90EwKzJ4b-jnc6p3UuVf_XE,1241
287
- runtimepy-5.10.1.dist-info/LICENSE,sha256=yKBRwbO-cOPBrlpsZmJkkSa33DfY31aE8t7lZ0DwlUo,1071
288
- runtimepy-5.10.1.dist-info/METADATA,sha256=XmFGbIXFW_oILfJuI2DX6HoAxjr92SGzxX07oiUJx1c,9373
289
- runtimepy-5.10.1.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
290
- runtimepy-5.10.1.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
291
- runtimepy-5.10.1.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
292
- runtimepy-5.10.1.dist-info/RECORD,,
288
+ runtimepy-5.10.2.dist-info/licenses/LICENSE,sha256=yKBRwbO-cOPBrlpsZmJkkSa33DfY31aE8t7lZ0DwlUo,1071
289
+ runtimepy-5.10.2.dist-info/METADATA,sha256=IOAwfPX8fSot0nOILOJsicLv4Go7fAl6DBQYl7qj-rQ,9395
290
+ runtimepy-5.10.2.dist-info/WHEEL,sha256=DK49LOLCYiurdXXOXwGJm6U4DkHkg4lcxjhqwRa0CP4,91
291
+ runtimepy-5.10.2.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
292
+ runtimepy-5.10.2.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
293
+ runtimepy-5.10.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (76.0.0)
2
+ Generator: setuptools (78.0.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5