takibi-swagger-ui 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 - present, takibi-swagger-ui Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.4
2
+ Name: takibi-swagger-ui
3
+ Version: 0.1.0
4
+ Summary: Swagger UI middleware for a takibiapi app
5
+ Keywords: openapi,swagger-ui,asgi,takibiapi
6
+ Author: nakita628
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
17
+ Classifier: Typing :: Typed
18
+ Requires-Dist: takibiapi>=0.1.0,<0.2
19
+ Requires-Python: >=3.12
20
+ Project-URL: Repository, https://github.com/nakita628/takibi
21
+ Project-URL: Issues, https://github.com/nakita628/takibi/issues
22
+ Description-Content-Type: text/markdown
23
+
24
+ # takibi-swagger-ui
25
+
26
+ Swagger UI for a [TakibiAPI](../takibiapi) app (or its [pydantic](../pydantic-takibiapi) / [zopy](../zopy-takibiapi) flavors), as Hono's [`@hono/swagger-ui`](https://github.com/honojs/middleware/tree/main/packages/swagger-ui).
27
+
28
+ ```sh
29
+ pip install takibi-swagger-ui
30
+ ```
31
+
32
+ ```python
33
+ from takibiapi import TakibiAPI
34
+ from takibi_swagger_ui import swaggerUI
35
+
36
+ app = TakibiAPI()
37
+ # app.openapi(route, handler) ...
38
+ app.doc("/doc", {"openapi": "3.1.0", "info": {"title": "My API", "version": "1.0.0"}})
39
+ app.use(swaggerUI({"url": "/doc"}), prefix="/ui") # GET /ui: the Swagger UI page
40
+ ```
41
+
42
+ The page loads `swagger-ui-dist` from jsDelivr (`"version": "5.17.14"` pins it) and the document from `url`.
43
+
44
+ ## Options
45
+
46
+ - `title`: the page's `<title>` (default `SwaggerUI`).
47
+ - `version`: the `swagger-ui-dist` version.
48
+ - `manuallySwaggerUIHtml`: `(asset) -> str`, the page body built from `asset["css"]` / `asset["js"]`.
49
+ - Everything else is [Swagger UI's configuration](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/) (`url`, `urls`, `spec`, `deepLinking`, `docExpansion`, `persistAuthorization`, ...), written as JSON. `presets`, `plugins`, `requestInterceptor`, `responseInterceptor`, `onComplete`, `operationsSorter`, `tagsSorter`, `modelPropertyMacro` and `parameterMacro` are JavaScript source and written as they are:
50
+
51
+ ```python
52
+ swaggerUI({"url": "/doc", "requestInterceptor": "(req) => { req.headers['x-api-key'] = 'dev'; return req }"})
53
+ ```
54
+
55
+ `SwaggerUI(options)` returns the markup alone, to put in a page of your own.
56
+
57
+ ## Development
58
+
59
+ From the repository root: `bazel test //pypi/takibi-swagger-ui:test`. See the root README. `apps/examples/src/examples/todo.py` serves it (`just serve`, then http://localhost:8000/ui).
60
+
61
+ ## License
62
+
63
+ Distributed under the MIT License. See [LICENSE](https://github.com/nakita628/takibi/blob/main/pypi/takibi-swagger-ui/LICENSE) for more information.
@@ -0,0 +1,40 @@
1
+ # takibi-swagger-ui
2
+
3
+ Swagger UI for a [TakibiAPI](../takibiapi) app (or its [pydantic](../pydantic-takibiapi) / [zopy](../zopy-takibiapi) flavors), as Hono's [`@hono/swagger-ui`](https://github.com/honojs/middleware/tree/main/packages/swagger-ui).
4
+
5
+ ```sh
6
+ pip install takibi-swagger-ui
7
+ ```
8
+
9
+ ```python
10
+ from takibiapi import TakibiAPI
11
+ from takibi_swagger_ui import swaggerUI
12
+
13
+ app = TakibiAPI()
14
+ # app.openapi(route, handler) ...
15
+ app.doc("/doc", {"openapi": "3.1.0", "info": {"title": "My API", "version": "1.0.0"}})
16
+ app.use(swaggerUI({"url": "/doc"}), prefix="/ui") # GET /ui: the Swagger UI page
17
+ ```
18
+
19
+ The page loads `swagger-ui-dist` from jsDelivr (`"version": "5.17.14"` pins it) and the document from `url`.
20
+
21
+ ## Options
22
+
23
+ - `title`: the page's `<title>` (default `SwaggerUI`).
24
+ - `version`: the `swagger-ui-dist` version.
25
+ - `manuallySwaggerUIHtml`: `(asset) -> str`, the page body built from `asset["css"]` / `asset["js"]`.
26
+ - Everything else is [Swagger UI's configuration](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/) (`url`, `urls`, `spec`, `deepLinking`, `docExpansion`, `persistAuthorization`, ...), written as JSON. `presets`, `plugins`, `requestInterceptor`, `responseInterceptor`, `onComplete`, `operationsSorter`, `tagsSorter`, `modelPropertyMacro` and `parameterMacro` are JavaScript source and written as they are:
27
+
28
+ ```python
29
+ swaggerUI({"url": "/doc", "requestInterceptor": "(req) => { req.headers['x-api-key'] = 'dev'; return req }"})
30
+ ```
31
+
32
+ `SwaggerUI(options)` returns the markup alone, to put in a page of your own.
33
+
34
+ ## Development
35
+
36
+ From the repository root: `bazel test //pypi/takibi-swagger-ui:test`. See the root README. `apps/examples/src/examples/todo.py` serves it (`just serve`, then http://localhost:8000/ui).
37
+
38
+ ## License
39
+
40
+ Distributed under the MIT License. See [LICENSE](https://github.com/nakita628/takibi/blob/main/pypi/takibi-swagger-ui/LICENSE) for more information.
@@ -0,0 +1,44 @@
1
+ [project]
2
+ name = "takibi-swagger-ui"
3
+ version = "0.1.0"
4
+ description = "Swagger UI middleware for a takibiapi app"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ keywords = [
10
+ "openapi",
11
+ "swagger-ui",
12
+ "asgi",
13
+ "takibiapi",
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
24
+ "Typing :: Typed",
25
+ ]
26
+ dependencies = ["takibiapi>=0.1.0,<0.2"]
27
+
28
+ [[project.authors]]
29
+ name = "nakita628"
30
+
31
+ [project.urls]
32
+ Repository = "https://github.com/nakita628/takibi"
33
+ Issues = "https://github.com/nakita628/takibi/issues"
34
+
35
+ [build-system]
36
+ requires = ["uv_build>=0.8,<0.14"]
37
+ build-backend = "uv_build"
38
+
39
+ [tool.uv.build-backend]
40
+ module-root = "python"
41
+ module-name = "takibi_swagger_ui"
42
+
43
+ [tool.uv.sources.takibiapi]
44
+ workspace = true
@@ -0,0 +1,37 @@
1
+ [project]
2
+ name = "takibi-swagger-ui"
3
+ version = "0.1.0"
4
+ description = "Swagger UI middleware for a takibiapi app"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "nakita628" }]
10
+ keywords = ["openapi", "swagger-ui", "asgi", "takibiapi"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Intended Audience :: Developers",
14
+ "Operating System :: OS Independent",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
19
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
20
+ "Typing :: Typed",
21
+ ]
22
+ dependencies = ["takibiapi>=0.1.0,<0.2"]
23
+
24
+ [project.urls]
25
+ Repository = "https://github.com/nakita628/takibi"
26
+ Issues = "https://github.com/nakita628/takibi/issues"
27
+
28
+ [build-system]
29
+ requires = ["uv_build>=0.8,<0.14"]
30
+ build-backend = "uv_build"
31
+
32
+ [tool.uv.build-backend]
33
+ module-root = "python"
34
+ module-name = "takibi_swagger_ui"
35
+
36
+ [tool.uv.sources]
37
+ takibiapi = { workspace = true }
@@ -0,0 +1,162 @@
1
+ """takibi-swagger-ui: Swagger UI for a takibiapi app, as Hono's ``@hono/swagger-ui``.
2
+
3
+ from takibi_swagger_ui import swaggerUI
4
+
5
+ app.doc("/doc", {"openapi": "3.1.0", "info": {"title": "My API", "version": "1.0.0"}})
6
+ app.use(swaggerUI({"url": "/doc"}), prefix="/ui")
7
+
8
+ ``GET /ui`` answers the Swagger UI page, which loads ``swagger-ui-dist`` from jsDelivr and the
9
+ document from ``url``. Other methods pass through to the app.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import html
15
+ import json
16
+ from collections.abc import Callable, Sequence
17
+ from typing import Any, TypedDict
18
+
19
+ from takibiapi import Context, Middleware, Next, Response
20
+
21
+
22
+ class Asset(TypedDict):
23
+ css: list[str]
24
+ js: list[str]
25
+
26
+
27
+ class SwaggerUIOptions(TypedDict, total=False):
28
+ """``title`` and ``version`` are this package's; the other keys are Swagger UI's
29
+ configuration (https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/)."""
30
+
31
+ title: str # <title> of the page (default "SwaggerUI")
32
+ version: str # swagger-ui-dist version on jsDelivr (default: latest)
33
+ manuallySwaggerUIHtml: Callable[[Asset], str] # the page body, built from the asset URLs
34
+ url: str
35
+ urls: list[dict[str, str]]
36
+ spec: dict[str, Any]
37
+ configUrl: str
38
+ layout: str
39
+ docExpansion: str
40
+ deepLinking: bool
41
+ displayOperationId: bool
42
+ defaultModelsExpandDepth: int
43
+ defaultModelExpandDepth: int
44
+ defaultModelRendering: str
45
+ displayRequestDuration: bool
46
+ filter: bool | str
47
+ maxDisplayedTags: int
48
+ showExtensions: bool
49
+ showCommonExtensions: bool
50
+ queryConfigEnabled: bool
51
+ persistAuthorization: bool
52
+ tryItOutEnabled: bool
53
+ requestSnippetsEnabled: bool
54
+ requestSnippets: dict[str, Any]
55
+ syntaxHighlight: bool | dict[str, Any]
56
+ supportedSubmitMethods: list[str]
57
+ validatorUrl: str | None
58
+ withCredentials: bool
59
+ oauth2RedirectUrl: str
60
+ # JavaScript, written into the page as it is:
61
+ presets: Sequence[str] # e.g. ["SwaggerUIBundle.presets.apis", "SwaggerUIStandalonePreset"]
62
+ plugins: Sequence[str]
63
+ requestInterceptor: str # e.g. "(req) => req"
64
+ responseInterceptor: str
65
+ onComplete: str
66
+ operationsSorter: str
67
+ tagsSorter: str
68
+ modelPropertyMacro: str
69
+ parameterMacro: str
70
+
71
+
72
+ # Keys whose values are JavaScript source (a list of expressions, or one expression) rather than data.
73
+ _JS_LISTS = frozenset({"presets", "plugins"})
74
+ _JS = frozenset(
75
+ {
76
+ "requestInterceptor",
77
+ "responseInterceptor",
78
+ "onComplete",
79
+ "operationsSorter",
80
+ "tagsSorter",
81
+ "modelPropertyMacro",
82
+ "parameterMacro",
83
+ }
84
+ )
85
+ _OWN = frozenset({"title", "version", "manuallySwaggerUIHtml"})
86
+
87
+
88
+ def remote_assets(version: str | None = None) -> Asset:
89
+ """The swagger-ui-dist stylesheet and script on jsDelivr."""
90
+ base = f"https://cdn.jsdelivr.net/npm/swagger-ui-dist{'@' + version if version else ''}"
91
+ return {"css": [f"{base}/swagger-ui.css"], "js": [f"{base}/swagger-ui-bundle.js"]}
92
+
93
+
94
+ def _js_value(value: Any) -> str:
95
+ # JSON inside <script>: "<" escaped so a value cannot close the tag.
96
+ return json.dumps(value, ensure_ascii=False).replace("<", "\\u003c")
97
+
98
+
99
+ def render_options(options: SwaggerUIOptions) -> str:
100
+ """The options as the properties of the ``SwaggerUIBundle({...})`` object literal."""
101
+ parts: list[str] = []
102
+ items: dict[str, Any] = dict(options)
103
+ for key, value in items.items():
104
+ if key in _OWN:
105
+ continue
106
+ if value is None:
107
+ if key == "validatorUrl": # null turns the validator badge off
108
+ parts.append("validatorUrl: null")
109
+ continue
110
+ if key in _JS_LISTS:
111
+ rendered = "[" + ",".join(str(v) for v in value) + "]"
112
+ elif key in _JS and isinstance(value, str):
113
+ rendered = value
114
+ else:
115
+ rendered = _js_value(value)
116
+ parts.append(f"{key}: {rendered}")
117
+ return ",\n ".join(parts)
118
+
119
+
120
+ def SwaggerUI(options: SwaggerUIOptions) -> str: # noqa: N802
121
+ """The Swagger UI markup (the page's body)."""
122
+ asset = remote_assets(options.get("version"))
123
+ manual = options.get("manuallySwaggerUIHtml")
124
+ if manual is not None:
125
+ return manual(asset)
126
+ links = "".join(f'<link rel="stylesheet" href="{html.escape(u)}" />' for u in asset["css"])
127
+ scripts = "".join(f'<script src="{html.escape(u)}" crossorigin="anonymous"></script>' for u in asset["js"])
128
+ rendered = render_options(options)
129
+ return f"""<div>
130
+ <div id="swagger-ui"></div>
131
+ {links}
132
+ {scripts}
133
+ <script>
134
+ window.onload = () => {{
135
+ window.ui = SwaggerUIBundle({{
136
+ dom_id: '#swagger-ui',{"" if not rendered else chr(10) + " " + rendered},
137
+ }})
138
+ }}
139
+ </script>
140
+ </div>"""
141
+
142
+
143
+ def swaggerUI(options: SwaggerUIOptions) -> Middleware: # noqa: N802
144
+ """The Swagger UI page as middleware: ``app.use(swaggerUI({"url": "/doc"}), prefix="/ui")``."""
145
+ title = html.escape(options.get("title", "SwaggerUI"))
146
+ page = (
147
+ '<!doctype html><html lang="en"><head><meta charset="utf-8" />'
148
+ '<meta name="viewport" content="width=device-width, initial-scale=1" />'
149
+ '<meta name="description" content="SwaggerUI" />'
150
+ f"<title>{title}</title></head><body>{SwaggerUI(options)}</body></html>"
151
+ ).encode()
152
+
153
+ async def middleware(c: Context[Any], next: Next) -> Response:
154
+ if c.req.method not in ("GET", "HEAD"):
155
+ return await next()
156
+ return Response(page, 200, {"content-type": "text/html; charset=utf-8"})
157
+
158
+ return middleware
159
+
160
+
161
+ __all__ = ["Asset", "SwaggerUI", "SwaggerUIOptions", "remote_assets", "render_options", "swaggerUI"]
162
+ __version__ = "0.1.0"