oe-python-template 0.14.3__py3-none-any.whl → 0.15.0__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.
- oe_python_template/api.py +7 -6
- oe_python_template/hello/_api.py +3 -3
- oe_python_template/hello/_gui.py +5 -3
- oe_python_template/system/_api.py +1 -1
- oe_python_template/system/_gui.py +2 -2
- oe_python_template/utils/__init__.py +2 -1
- oe_python_template/utils/_api.py +53 -6
- oe_python_template/utils/_di.py +6 -0
- oe_python_template/utils/_gui.py +119 -85
- oe_python_template/utils/_notebook.py +12 -7
- {oe_python_template-0.14.3.dist-info → oe_python_template-0.15.0.dist-info}/METADATA +1 -1
- {oe_python_template-0.14.3.dist-info → oe_python_template-0.15.0.dist-info}/RECORD +15 -15
- {oe_python_template-0.14.3.dist-info → oe_python_template-0.15.0.dist-info}/WHEEL +0 -0
- {oe_python_template-0.14.3.dist-info → oe_python_template-0.15.0.dist-info}/entry_points.txt +0 -0
- {oe_python_template-0.14.3.dist-info → oe_python_template-0.15.0.dist-info}/licenses/LICENSE +0 -0
oe_python_template/api.py
CHANGED
|
@@ -16,7 +16,7 @@ from .utils import (
|
|
|
16
16
|
__base__url__,
|
|
17
17
|
__documentation__url__,
|
|
18
18
|
__repository_url__,
|
|
19
|
-
|
|
19
|
+
load_modules,
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
TITLE = "OE Python Template"
|
|
@@ -67,12 +67,13 @@ for version, semver in API_VERSIONS.items():
|
|
|
67
67
|
terms_of_service=TERMS_OF_SERVICE_URL,
|
|
68
68
|
)
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
load_modules()
|
|
71
|
+
|
|
72
|
+
# Register routers with appropriate API versions using the tracked instances
|
|
73
|
+
for router in VersionedAPIRouter.get_instances():
|
|
74
|
+
version = router.version # type: ignore
|
|
74
75
|
if version in API_VERSIONS:
|
|
75
|
-
api_instances[version].include_router(
|
|
76
|
+
api_instances[version].include_router(router) # type: ignore
|
|
76
77
|
|
|
77
78
|
# Mount all API versions to the main app
|
|
78
79
|
for version in API_VERSIONS:
|
oe_python_template/hello/_api.py
CHANGED
|
@@ -8,7 +8,7 @@ This module provides a webservice API with several operations:
|
|
|
8
8
|
from collections.abc import Generator
|
|
9
9
|
from typing import Annotated
|
|
10
10
|
|
|
11
|
-
from fastapi import Depends
|
|
11
|
+
from fastapi import APIRouter, Depends
|
|
12
12
|
from pydantic import BaseModel, Field
|
|
13
13
|
|
|
14
14
|
from oe_python_template.utils import VersionedAPIRouter
|
|
@@ -20,8 +20,8 @@ HELLO_WORLD_EXAMPLE = "Hello, world!"
|
|
|
20
20
|
|
|
21
21
|
# VersionedAPIRouters exported by modules via their __init__.py are automatically registered
|
|
22
22
|
# and injected into the main API app, see ../api.py.
|
|
23
|
-
api_v1 = VersionedAPIRouter("v1", prefix="/hello", tags=["hello"])
|
|
24
|
-
api_v2 = VersionedAPIRouter("v2", prefix="/hello", tags=["hello"])
|
|
23
|
+
api_v1: APIRouter = VersionedAPIRouter("v1", prefix="/hello", tags=["hello"]) # type: ignore
|
|
24
|
+
api_v2: APIRouter = VersionedAPIRouter("v2", prefix="/hello", tags=["hello"]) # type: ignore
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def get_service() -> Generator[Service, None, None]:
|
oe_python_template/hello/_gui.py
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from nicegui import ui
|
|
6
|
-
|
|
7
5
|
from oe_python_template.utils import BasePageBuilder, GUILocalFilePicker
|
|
8
6
|
|
|
9
7
|
from ._service import Service
|
|
@@ -11,13 +9,17 @@ from ._service import Service
|
|
|
11
9
|
|
|
12
10
|
async def pick_file() -> None:
|
|
13
11
|
"""Open a file picker dialog and show notifier when closed again."""
|
|
14
|
-
|
|
12
|
+
from nicegui import ui # noqa: PLC0415
|
|
13
|
+
|
|
14
|
+
result = await GUILocalFilePicker(str(Path.cwd() / "examples"), multiple=True) # type: ignore
|
|
15
15
|
ui.notify(f"You chose {result}")
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class PageBuilder(BasePageBuilder):
|
|
19
19
|
@staticmethod
|
|
20
20
|
def register_pages() -> None:
|
|
21
|
+
from nicegui import ui # noqa: PLC0415
|
|
22
|
+
|
|
21
23
|
@ui.page("/")
|
|
22
24
|
def page_index() -> None:
|
|
23
25
|
"""Homepage of GUI."""
|
|
@@ -110,7 +110,7 @@ def register_info_endpoint(router: APIRouter) -> Callable[..., dict[str, Any]]:
|
|
|
110
110
|
|
|
111
111
|
api_routers = {}
|
|
112
112
|
for version in API_VERSIONS:
|
|
113
|
-
router = VersionedAPIRouter(version, tags=["system"])
|
|
113
|
+
router: APIRouter = VersionedAPIRouter(version, tags=["system"]) # type: ignore
|
|
114
114
|
api_routers[version] = router
|
|
115
115
|
health = register_health_endpoint(api_routers[version])
|
|
116
116
|
info = register_info_endpoint(api_routers[version])
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""Homepage (index) of GUI."""
|
|
2
2
|
|
|
3
|
-
from nicegui import ui
|
|
4
|
-
|
|
5
3
|
from ..utils import BasePageBuilder, __project_name__, __version__ # noqa: TID252
|
|
6
4
|
from ._service import Service
|
|
7
5
|
|
|
@@ -9,6 +7,8 @@ from ._service import Service
|
|
|
9
7
|
class PageBuilder(BasePageBuilder):
|
|
10
8
|
@staticmethod
|
|
11
9
|
def register_pages() -> None:
|
|
10
|
+
from nicegui import ui # noqa: PLC0415
|
|
11
|
+
|
|
12
12
|
@ui.page("/info")
|
|
13
13
|
def page_info() -> None:
|
|
14
14
|
"""Homepage of GUI."""
|
|
@@ -18,7 +18,7 @@ from ._constants import (
|
|
|
18
18
|
__repository_url__,
|
|
19
19
|
__version__,
|
|
20
20
|
)
|
|
21
|
-
from ._di import locate_implementations, locate_subclasses
|
|
21
|
+
from ._di import load_modules, locate_implementations, locate_subclasses
|
|
22
22
|
from ._health import Health
|
|
23
23
|
from ._log import LogSettings, get_logger
|
|
24
24
|
from ._logfire import LogfireSettings
|
|
@@ -56,6 +56,7 @@ __all__ = [
|
|
|
56
56
|
"console",
|
|
57
57
|
"get_logger",
|
|
58
58
|
"get_process_info",
|
|
59
|
+
"load_modules",
|
|
59
60
|
"load_settings",
|
|
60
61
|
"locate_implementations",
|
|
61
62
|
"locate_subclasses",
|
oe_python_template/utils/_api.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
"""API router utilities for versioned FastAPI routers."""
|
|
2
2
|
|
|
3
|
+
from typing import ClassVar
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
class VersionedAPIRouter:
|
|
5
7
|
"""APIRouter with version attribute.
|
|
6
8
|
|
|
7
9
|
- Use this class to create versioned routers for your FastAPI application
|
|
@@ -11,8 +13,53 @@ class VersionedAPIRouter(APIRouter):
|
|
|
11
13
|
- See constants.por versions defined for this system.
|
|
12
14
|
"""
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
# Class variable to track all created instances
|
|
17
|
+
_instances: ClassVar[list["VersionedAPIRouter"]] = []
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def get_instances(cls) -> list["VersionedAPIRouter"]:
|
|
21
|
+
"""Get all created router instances.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
A list of all router instances created.
|
|
25
|
+
"""
|
|
26
|
+
return cls._instances.copy()
|
|
27
|
+
|
|
28
|
+
def __new__(cls, version: str, *args, **kwargs) -> "VersionedAPIRouter": # type: ignore[no-untyped-def]
|
|
29
|
+
"""Create a new instance with lazy-loaded dependencies.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
version: The API version this router belongs to.
|
|
33
|
+
*args: Arguments to pass to the FastAPI APIRouter.
|
|
34
|
+
**kwargs: Keyword arguments to pass to the FastAPI APIRouter.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
An instance of VersionedAPIRouter with lazy-loaded dependencies.
|
|
38
|
+
"""
|
|
39
|
+
from fastapi import APIRouter # Import only when creating an instance # noqa: PLC0415
|
|
40
|
+
|
|
41
|
+
# Define the actual implementation class with the imports available
|
|
42
|
+
class VersionedAPIRouterImpl(APIRouter):
|
|
43
|
+
"""Implementation of VersionedAPIRouter with lazy-loaded dependencies."""
|
|
44
|
+
|
|
45
|
+
version: str
|
|
46
|
+
|
|
47
|
+
def __init__(self, version: str, *args, **kwargs) -> None: # type: ignore[no-untyped-def]
|
|
48
|
+
"""Initialize the router.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
version: The API version this router belongs to.
|
|
52
|
+
*args: Arguments to pass to the FastAPI APIRouter.
|
|
53
|
+
**kwargs: Keyword arguments to pass to the FastAPI APIRouter.
|
|
54
|
+
"""
|
|
55
|
+
super().__init__(*args, **kwargs)
|
|
56
|
+
self.version = version
|
|
57
|
+
|
|
58
|
+
# Create an instance
|
|
59
|
+
instance = VersionedAPIRouterImpl(version, *args, **kwargs)
|
|
60
|
+
|
|
61
|
+
# Add to registry of instances
|
|
62
|
+
cls._instances.append(instance) # type: ignore
|
|
15
63
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
self.version = version
|
|
64
|
+
# Return the instance but tell mypy it's a VersionedAPIRouter
|
|
65
|
+
return instance # type: ignore[return-value]
|
oe_python_template/utils/_di.py
CHANGED
|
@@ -11,6 +11,12 @@ _implementation_cache: dict[Any, list[Any]] = {}
|
|
|
11
11
|
_subclass_cache: dict[Any, list[Any]] = {}
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def load_modules() -> None:
|
|
15
|
+
package = importlib.import_module(__project_name__)
|
|
16
|
+
for _, name, _ in pkgutil.iter_modules(package.__path__):
|
|
17
|
+
importlib.import_module(f"{__project_name__}.{name}")
|
|
18
|
+
|
|
19
|
+
|
|
14
20
|
def locate_implementations(_class: type[Any]) -> list[Any]:
|
|
15
21
|
"""
|
|
16
22
|
Dynamically discover all instances of some class.
|
oe_python_template/utils/_gui.py
CHANGED
|
@@ -3,9 +3,6 @@ from abc import ABC, abstractmethod
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from types import EllipsisType
|
|
5
5
|
|
|
6
|
-
from nicegui import app, events, ui
|
|
7
|
-
from nicegui import native as native_app
|
|
8
|
-
|
|
9
6
|
from ._constants import __is_running_in_container__, __project_name__
|
|
10
7
|
from ._di import locate_subclasses
|
|
11
8
|
from ._log import get_logger
|
|
@@ -58,6 +55,9 @@ def gui_run( # noqa: PLR0913, PLR0917
|
|
|
58
55
|
ValueError: If with_notebook is True but notebook_path is None,
|
|
59
56
|
or trying to run native within container.
|
|
60
57
|
"""
|
|
58
|
+
from nicegui import app, ui # noqa: PLC0415
|
|
59
|
+
from nicegui import native as native_app # noqa: PLC0415
|
|
60
|
+
|
|
61
61
|
if __is_running_in_container__ and native:
|
|
62
62
|
message = "Native GUI cannot be run in a container. Please run with uvx or in browser."
|
|
63
63
|
raise ValueError(message)
|
|
@@ -81,98 +81,132 @@ def gui_run( # noqa: PLR0913, PLR0917
|
|
|
81
81
|
)
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
class GUILocalFilePicker
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
class GUILocalFilePicker:
|
|
85
|
+
"""Local File Picker dialog class that lazy-loads NiceGUI dependencies."""
|
|
86
|
+
|
|
87
|
+
def __new__( # noqa: C901
|
|
88
|
+
cls,
|
|
87
89
|
directory: str,
|
|
88
90
|
*,
|
|
89
91
|
upper_limit: str | EllipsisType | None = ...,
|
|
90
92
|
multiple: bool = False,
|
|
91
93
|
show_hidden_files: bool = False,
|
|
92
|
-
) ->
|
|
93
|
-
"""
|
|
94
|
-
|
|
95
|
-
A simple file picker that allows selecting files from the local filesystem where NiceGUI is running.
|
|
94
|
+
) -> "GUILocalFilePicker":
|
|
95
|
+
"""Create a new instance with lazy-loaded dependencies.
|
|
96
96
|
|
|
97
97
|
Args:
|
|
98
98
|
directory: The directory to start in.
|
|
99
99
|
upper_limit: The directory to stop at. None for no limit, default is same as starting directory.
|
|
100
100
|
multiple: Whether to allow multiple files to be selected.
|
|
101
101
|
show_hidden_files: Whether to show hidden files.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
An instance of the dialog with lazy-loaded dependencies.
|
|
102
105
|
"""
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
106
|
+
from nicegui import events, ui # noqa: PLC0415
|
|
107
|
+
# Lazy import ui only when actually creating an instance
|
|
108
|
+
|
|
109
|
+
# Define the actual implementation class with the imports available
|
|
110
|
+
class GUILocalFilePickerImpl(ui.dialog):
|
|
111
|
+
def __init__(
|
|
112
|
+
self,
|
|
113
|
+
directory: str,
|
|
114
|
+
*,
|
|
115
|
+
upper_limit: str | EllipsisType | None = ...,
|
|
116
|
+
multiple: bool = False,
|
|
117
|
+
show_hidden_files: bool = False,
|
|
118
|
+
) -> None:
|
|
119
|
+
"""Local File Picker.
|
|
120
|
+
|
|
121
|
+
A simple file picker that allows selecting files from the local filesystem where NiceGUI is running.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
directory: The directory to start in.
|
|
125
|
+
upper_limit: The directory to stop at. None for no limit, default is same as starting directory.
|
|
126
|
+
multiple: Whether to allow multiple files to be selected.
|
|
127
|
+
show_hidden_files: Whether to show hidden files.
|
|
128
|
+
"""
|
|
129
|
+
super().__init__()
|
|
130
|
+
|
|
131
|
+
self.path = Path(directory).expanduser()
|
|
132
|
+
if upper_limit is None:
|
|
133
|
+
self.upper_limit = None
|
|
134
|
+
elif upper_limit is ...:
|
|
135
|
+
self.upper_limit = Path(directory).expanduser()
|
|
136
|
+
else:
|
|
137
|
+
self.upper_limit = Path(upper_limit).expanduser()
|
|
138
|
+
self.show_hidden_files = show_hidden_files
|
|
139
|
+
|
|
140
|
+
with self, ui.card():
|
|
141
|
+
self.add_drives_toggle()
|
|
142
|
+
self.grid = (
|
|
143
|
+
ui.aggrid(
|
|
144
|
+
{
|
|
145
|
+
"columnDefs": [{"field": "name", "headerName": "File"}],
|
|
146
|
+
"rowSelection": "multiple" if multiple else "single",
|
|
147
|
+
},
|
|
148
|
+
html_columns=[0],
|
|
149
|
+
)
|
|
150
|
+
.classes("w-96")
|
|
151
|
+
.on("cellDoubleClicked", self.handle_double_click)
|
|
152
|
+
)
|
|
153
|
+
with ui.row().classes("w-full justify-end"):
|
|
154
|
+
ui.button("Cancel", on_click=self.close).props("outline").mark("BUTTON_CANCEL")
|
|
155
|
+
ui.button("Ok", on_click=self._handle_ok).mark("BUTTON_OK")
|
|
156
|
+
self.update_grid()
|
|
157
|
+
|
|
158
|
+
def add_drives_toggle(self) -> None:
|
|
159
|
+
if platform.system() == "Windows":
|
|
160
|
+
import win32api # noqa: PLC0415
|
|
161
|
+
|
|
162
|
+
drives = win32api.GetLogicalDriveStrings().split("\000")[:-1]
|
|
163
|
+
self.drives_toggle = ui.toggle(drives, value=drives[0], on_change=self.update_drive)
|
|
164
|
+
|
|
165
|
+
def update_drive(self) -> None:
|
|
166
|
+
self.path = Path(self.drives_toggle.value).expanduser()
|
|
167
|
+
self.update_grid()
|
|
168
|
+
|
|
169
|
+
def update_grid(self) -> None:
|
|
170
|
+
paths = list(self.path.glob("*"))
|
|
171
|
+
if not self.show_hidden_files:
|
|
172
|
+
paths = [p for p in paths if not p.name.startswith(".")]
|
|
173
|
+
paths.sort(key=lambda p: p.name.lower())
|
|
174
|
+
paths.sort(key=lambda p: not p.is_dir())
|
|
175
|
+
|
|
176
|
+
self.grid.options["rowData"] = [
|
|
118
177
|
{
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
for p in paths
|
|
156
|
-
]
|
|
157
|
-
if (self.upper_limit is None and self.path != self.path.parent) or (
|
|
158
|
-
self.upper_limit is not None and self.path != self.upper_limit
|
|
159
|
-
):
|
|
160
|
-
self.grid.options["rowData"].insert(
|
|
161
|
-
0,
|
|
162
|
-
{
|
|
163
|
-
"name": "📁 <strong>..</strong>",
|
|
164
|
-
"path": str(self.path.parent),
|
|
165
|
-
},
|
|
166
|
-
)
|
|
167
|
-
self.grid.update()
|
|
168
|
-
|
|
169
|
-
def handle_double_click(self, e: events.GenericEventArguments) -> None:
|
|
170
|
-
self.path = Path(e.args["data"]["path"])
|
|
171
|
-
if self.path.is_dir():
|
|
172
|
-
self.update_grid()
|
|
173
|
-
else:
|
|
174
|
-
self.submit([str(self.path)])
|
|
175
|
-
|
|
176
|
-
async def _handle_ok(self) -> None:
|
|
177
|
-
rows = await self.grid.get_selected_rows()
|
|
178
|
-
self.submit([r["path"] for r in rows])
|
|
178
|
+
"name": f"📁 <strong>{p.name}</strong>" if p.is_dir() else p.name,
|
|
179
|
+
"path": str(p),
|
|
180
|
+
}
|
|
181
|
+
for p in paths
|
|
182
|
+
]
|
|
183
|
+
if (self.upper_limit is None and self.path != self.path.parent) or (
|
|
184
|
+
self.upper_limit is not None and self.path != self.upper_limit
|
|
185
|
+
):
|
|
186
|
+
self.grid.options["rowData"].insert(
|
|
187
|
+
0,
|
|
188
|
+
{
|
|
189
|
+
"name": "📁 <strong>..</strong>",
|
|
190
|
+
"path": str(self.path.parent),
|
|
191
|
+
},
|
|
192
|
+
)
|
|
193
|
+
self.grid.update()
|
|
194
|
+
|
|
195
|
+
def handle_double_click(self, e: events.GenericEventArguments) -> None:
|
|
196
|
+
self.path = Path(e.args["data"]["path"])
|
|
197
|
+
if self.path.is_dir():
|
|
198
|
+
self.update_grid()
|
|
199
|
+
else:
|
|
200
|
+
self.submit([str(self.path)])
|
|
201
|
+
|
|
202
|
+
async def _handle_ok(self) -> None:
|
|
203
|
+
rows = await self.grid.get_selected_rows()
|
|
204
|
+
self.submit([r["path"] for r in rows])
|
|
205
|
+
|
|
206
|
+
# Create and return an instance but tell mypy it's a GUILocalFilePicker
|
|
207
|
+
return GUILocalFilePickerImpl( # type: ignore[return-value]
|
|
208
|
+
directory=directory,
|
|
209
|
+
upper_limit=upper_limit,
|
|
210
|
+
multiple=multiple,
|
|
211
|
+
show_hidden_files=show_hidden_files,
|
|
212
|
+
)
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Notebook server utilities."""
|
|
2
2
|
|
|
3
3
|
from collections.abc import Callable
|
|
4
|
-
|
|
5
|
-
import marimo
|
|
6
|
-
from fastapi import APIRouter, FastAPI
|
|
4
|
+
from typing import Any
|
|
7
5
|
|
|
8
6
|
from ..constants import NOTEBOOK_APP, NOTEBOOK_FOLDER # noqa: TID252
|
|
9
7
|
from ._health import Health
|
|
@@ -12,7 +10,7 @@ from ._log import get_logger
|
|
|
12
10
|
logger = get_logger(__name__)
|
|
13
11
|
|
|
14
12
|
|
|
15
|
-
def register_health_endpoint(router:
|
|
13
|
+
def register_health_endpoint(router: Any) -> Callable[..., Health]: # noqa: ANN401
|
|
16
14
|
"""Register health endpoint to the given router.
|
|
17
15
|
|
|
18
16
|
Args:
|
|
@@ -21,6 +19,7 @@ def register_health_endpoint(router: APIRouter) -> Callable[..., Health]:
|
|
|
21
19
|
Returns:
|
|
22
20
|
Callable[..., Health]: The health endpoint function.
|
|
23
21
|
"""
|
|
22
|
+
# We accept 'Any' instead of APIRouter to avoid importing fastapi at module level
|
|
24
23
|
|
|
25
24
|
@router.get("/healthz")
|
|
26
25
|
def health_endpoint() -> Health:
|
|
@@ -31,10 +30,12 @@ def register_health_endpoint(router: APIRouter) -> Callable[..., Health]:
|
|
|
31
30
|
"""
|
|
32
31
|
return Health(status=Health.Code.UP)
|
|
33
32
|
|
|
34
|
-
return
|
|
33
|
+
# Explicitly type the return value to satisfy mypy
|
|
34
|
+
result: Callable[..., Health] = health_endpoint
|
|
35
|
+
return result
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
def create_marimo_app() ->
|
|
38
|
+
def create_marimo_app() -> Any: # noqa: ANN401
|
|
38
39
|
"""Create a FastAPI app with marimo notebook server.
|
|
39
40
|
|
|
40
41
|
Returns:
|
|
@@ -43,6 +44,10 @@ def create_marimo_app() -> FastAPI:
|
|
|
43
44
|
Raises:
|
|
44
45
|
ValueError: If the notebook directory does not exist.
|
|
45
46
|
"""
|
|
47
|
+
# Import dependencies only when function is called
|
|
48
|
+
import marimo # noqa: PLC0415
|
|
49
|
+
from fastapi import APIRouter, FastAPI # noqa: PLC0415
|
|
50
|
+
|
|
46
51
|
server = marimo.create_asgi_app(include_code=True)
|
|
47
52
|
if not NOTEBOOK_FOLDER.is_dir():
|
|
48
53
|
logger.critical(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oe-python-template
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: 🧠 Copier template to scaffold Python projects compliant with best practices and modern tooling.
|
|
5
5
|
Project-URL: Homepage, https://oe-python-template.readthedocs.io/en/latest/
|
|
6
6
|
Project-URL: Documentation, https://oe-python-template.readthedocs.io/en/latest/
|
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
oe_python_template/__init__.py,sha256=_Z3Xb-x95UODU66avOiwROVaouk_s0ZNB25KFnPoS40,226
|
|
2
|
-
oe_python_template/api.py,sha256=
|
|
2
|
+
oe_python_template/api.py,sha256=tNPSbBDYq8x1WMs4pAw6I5MLlV6NgJetQ6FNPRLho5o,2181
|
|
3
3
|
oe_python_template/cli.py,sha256=371khUxULDDmYkwf05MXNrgzpIoSgGY9CWp_DCICdxQ,1677
|
|
4
4
|
oe_python_template/constants.py,sha256=tIWRWxG7Ob-MwH8ucPplOMO3mCgXPo97acRjydpHiyg,330
|
|
5
5
|
oe_python_template/hello/__init__.py,sha256=F7aJ_uhTPPnFLT_4GjI2GzjAmKrUhfe4KxqzrljuCVo,485
|
|
6
|
-
oe_python_template/hello/_api.py,sha256=
|
|
6
|
+
oe_python_template/hello/_api.py,sha256=wW3gI6OpLMP3u2hVuaKxpuGF230rS6JiCnsRnGFPdpc,2359
|
|
7
7
|
oe_python_template/hello/_cli.py,sha256=mSNmRj_VRoRfCAR1I8tssZqZCTmT6jgzsNWrTtlFP7Y,1184
|
|
8
8
|
oe_python_template/hello/_constants.py,sha256=6aRleAIcdgC13TeTzI07YwjoSwqGb2g131dw8aEoM4I,109
|
|
9
|
-
oe_python_template/hello/_gui.py,sha256=
|
|
9
|
+
oe_python_template/hello/_gui.py,sha256=gB_UcMurVVj50VQH3jKrKRVDIGVbgtDA6uSn75UGjfw,1552
|
|
10
10
|
oe_python_template/hello/_models.py,sha256=JtI7wGT72u23NOxFa-oeWzdyiMg7PnHL5eg22im2_yQ,574
|
|
11
11
|
oe_python_template/hello/_service.py,sha256=22-IqNpra43IQcdeZNnzY4uhdPZmlXL6mCsLTCm6Zp4,3175
|
|
12
12
|
oe_python_template/hello/_settings.py,sha256=_85PIdq8UuS4orCZ0qFJ22RTQTooOTB0USRxBapBK3M,1533
|
|
13
13
|
oe_python_template/system/__init__.py,sha256=7e2z8HATzy3dAIBXy5PM9rlCC7Rbu8m8NapROdrf3Wk,624
|
|
14
|
-
oe_python_template/system/_api.py,sha256=
|
|
14
|
+
oe_python_template/system/_api.py,sha256=pBBCL1gXM7W5dgJqBLKuNFWES0pQF8mbUgQ7H7oHwh8,3714
|
|
15
15
|
oe_python_template/system/_cli.py,sha256=0cGwSicK0j7P53SMm9nSyQqj0WYpHKvFvkISCcjeTPU,6959
|
|
16
|
-
oe_python_template/system/_gui.py,sha256=
|
|
16
|
+
oe_python_template/system/_gui.py,sha256=9U4QMkl1mVJargg_gQ7U8tMlvec4b1cqkTgRcmmyTTc,678
|
|
17
17
|
oe_python_template/system/_service.py,sha256=f-EtWEAanajo4S2KeGm26K9_RezYgPza1qVqPRBOFvY,6376
|
|
18
18
|
oe_python_template/system/_settings.py,sha256=MwMAJYifJ6jGImeSh4e9shmIXmiUSuQGHXz_Ts0mSdk,901
|
|
19
|
-
oe_python_template/utils/__init__.py,sha256=
|
|
20
|
-
oe_python_template/utils/_api.py,sha256=
|
|
19
|
+
oe_python_template/utils/__init__.py,sha256=oM8v1ozMOeBQG-EbBtEGbnuGk52mYNDAmNoR7vNQ7MY,2050
|
|
20
|
+
oe_python_template/utils/_api.py,sha256=GSI-HPtSdTFv0qP5aTDPa2WS3ewVa1slusG6dvxSu0Y,2474
|
|
21
21
|
oe_python_template/utils/_cli.py,sha256=J_mFtXZ1gGeovGrE5i3wlokTOBfiTTKEz5magiRP7GA,2091
|
|
22
22
|
oe_python_template/utils/_console.py,sha256=u0-utcdRmVu4rabrYUyNOx8yPxLhxB3E92m22kSCwPQ,293
|
|
23
23
|
oe_python_template/utils/_constants.py,sha256=fPS1leVqg7NRo_FFE3Xs3QI7un5_YRrY8cKQAfThwgg,3247
|
|
24
|
-
oe_python_template/utils/_di.py,sha256=
|
|
25
|
-
oe_python_template/utils/_gui.py,sha256=
|
|
24
|
+
oe_python_template/utils/_di.py,sha256=J1-f6xtbnR88VfRAsRMl5m6eqt-Lf3c7ABSJOxNzlvU,2399
|
|
25
|
+
oe_python_template/utils/_gui.py,sha256=mm7-PFPZsVAYISENM91ezlefs_Ax5dEOPzT7-u5xQe0,7749
|
|
26
26
|
oe_python_template/utils/_health.py,sha256=35QOWe2r5InrEpGtuVMym9dI5aRHS0HWf4BHBRAUIj0,4102
|
|
27
27
|
oe_python_template/utils/_log.py,sha256=9aalm3FzkIgx38Em4a49m5SrXvg0DiTzodTPTICUNuc,5042
|
|
28
28
|
oe_python_template/utils/_logfire.py,sha256=wZYNVowQx7kh3XJoJ59FjUKdrta7tp6cXOJRUT6lDU8,2128
|
|
29
|
-
oe_python_template/utils/_notebook.py,sha256=
|
|
29
|
+
oe_python_template/utils/_notebook.py,sha256=IZOQTRKVV5htwXZIpZIZbAAOo86NK1L8vYkg1o7HW1Q,2114
|
|
30
30
|
oe_python_template/utils/_process.py,sha256=40R0NZMqJUn0iUPERzohSUpJgU1HcJApIg1HipIxFCw,941
|
|
31
31
|
oe_python_template/utils/_sentry.py,sha256=6SZknbGBpdZHYUpF-11qk6UYBM-c79U8D9sF4517kdU,6369
|
|
32
32
|
oe_python_template/utils/_service.py,sha256=atHAejvBucKXjzhsMSdOBBFa7rRD74zcV70Pp0pl0Tg,1038
|
|
33
33
|
oe_python_template/utils/_settings.py,sha256=owFoaHEzJnVD3EVyOWF4rfIY7g6eLnU6rN0m4VHhCbA,2464
|
|
34
34
|
oe_python_template/utils/boot.py,sha256=Qgq1sjT8d3fcfibnMyx5CVUJ_KKXgFI3ozZUIJbhh4I,2921
|
|
35
35
|
oe_python_template/utils/.vendored/bottle.py,sha256=kZAZmh3nRzCUf-9IKGpv0yqlMciZMA_DNaaMDdcQmt0,175437
|
|
36
|
-
oe_python_template-0.
|
|
37
|
-
oe_python_template-0.
|
|
38
|
-
oe_python_template-0.
|
|
39
|
-
oe_python_template-0.
|
|
40
|
-
oe_python_template-0.
|
|
36
|
+
oe_python_template-0.15.0.dist-info/METADATA,sha256=g8zwZhYViUjme4lHnQ0bhM4xThkFfQiGcEyZqN9yN1k,34475
|
|
37
|
+
oe_python_template-0.15.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
38
|
+
oe_python_template-0.15.0.dist-info/entry_points.txt,sha256=IroSSWhLGxus9rxcashkYQda39TTvf7LbUMYtOKXUBE,66
|
|
39
|
+
oe_python_template-0.15.0.dist-info/licenses/LICENSE,sha256=5H409K6xzz9U5eUaoAHQExNkoWJRlU0LEj6wL2QJ34s,1113
|
|
40
|
+
oe_python_template-0.15.0.dist-info/RECORD,,
|
|
File without changes
|
{oe_python_template-0.14.3.dist-info → oe_python_template-0.15.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{oe_python_template-0.14.3.dist-info → oe_python_template-0.15.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|