oe-python-template-example 0.4.1__py3-none-any.whl → 0.4.3__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.
@@ -60,3 +60,17 @@ __all__ = [
60
60
  "prepare_cli",
61
61
  "strip_to_none_before_validator",
62
62
  ]
63
+
64
+ from importlib.util import find_spec
65
+
66
+ if find_spec("nicegui"):
67
+ from ._gui import BasePageBuilder, GUILocalFilePicker, gui_register_pages, gui_run
68
+
69
+ __all__ += ["BasePageBuilder", "GUILocalFilePicker", "gui_register_pages", "gui_run"]
70
+
71
+ if find_spec("marimo"):
72
+ from ._notebook import create_marimo_app
73
+
74
+ __all__ += [
75
+ "create_marimo_app",
76
+ ]
@@ -14,7 +14,21 @@ __project_path__ = str(Path(__file__).parent.parent.parent)
14
14
  __version__ = metadata.version(__project_name__)
15
15
  __is_development_mode__ = "uvx" not in sys.argv[0].lower()
16
16
  __is_running_in_container__ = os.getenv(f"{__project_name__.upper()}_RUNNING_IN_CONTAINER")
17
- __env__ = os.getenv("ENV", os.getenv("VERCEL_ENV", "local"))
17
+
18
+ # Determine environment we are deployed on
19
+ ENV_VAR_MAPPINGS = {
20
+ "ENV": lambda env: env,
21
+ "VERCEL_ENV": lambda env: env, # See https://vercel.com/docs/environment-variables/system-environment-variables
22
+ "RAILWAY_ENVIRONMENT": lambda env: env, # See https://docs.railway.com/reference/variables#railway-provided-variables
23
+ }
24
+ __env__ = "local" # Default
25
+ for env_var, mapper in ENV_VAR_MAPPINGS.items():
26
+ env_value = os.getenv(env_var)
27
+ if env_value:
28
+ __env__ = mapper(env_value) # type: ignore[no-untyped-call]
29
+ break
30
+
31
+ # Define environment file paths
18
32
  __env_file__ = [
19
33
  Path.home() / f".{__project_name__}" / ".env",
20
34
  Path.home() / f".{__project_name__}" / f".env.{__env__}",
@@ -25,12 +39,18 @@ env_file_path = os.getenv(f"{__project_name__.upper()}_ENV_FILE")
25
39
  if env_file_path:
26
40
  __env_file__.insert(2, Path(env_file_path))
27
41
 
28
- vercel_base_url = os.getenv("VERCEL_URL", None)
29
- if vercel_base_url:
30
- vercel_base_url = "https://" + vercel_base_url
31
- __base__url__ = os.getenv(__project_name__.upper() + "_BASE_URL", None)
32
- if not __base__url__ and vercel_base_url:
33
- __base__url__ = vercel_base_url
42
+ # Determine __base_url__
43
+ PLATFORM_URL_MAPPINGS = {
44
+ "VERCEL_URL": lambda url: f"https://{url}", # See https://vercel.com/docs/environment-variables/system-environment-variables
45
+ "RAILWAY_PUBLIC_DOMAIN": lambda url: f"https://{url}", # See https://docs.railway.com/reference/variables#railway-provided-variables
46
+ }
47
+ __base__url__ = os.getenv(f"{__project_name__.upper()}_BASE_URL")
48
+ if not __base__url__:
49
+ for env_var, mappers in PLATFORM_URL_MAPPINGS.items():
50
+ env_value = os.getenv(env_var)
51
+ if env_value:
52
+ __base__url__ = mappers(env_value) # type: ignore[no-untyped-call]
53
+ break
34
54
 
35
55
 
36
56
  def get_project_url_by_label(prefix: str) -> str:
@@ -47,7 +67,6 @@ def get_project_url_by_label(prefix: str) -> str:
47
67
  for url_entry in metadata.metadata(__project_name__).get_all("Project-URL", []):
48
68
  if url_entry.startswith(prefix):
49
69
  return str(url_entry.split(", ", 1)[1])
50
-
51
70
  return ""
52
71
 
53
72
 
@@ -0,0 +1,174 @@
1
+ import platform
2
+ from abc import ABC, abstractmethod
3
+ from pathlib import Path
4
+ from types import EllipsisType
5
+
6
+ from nicegui import app, events, ui
7
+ from nicegui import native as native_app
8
+
9
+ from ._constants import __project_name__
10
+ from ._di import locate_subclasses
11
+ from ._log import get_logger
12
+
13
+ logger = get_logger(__name__)
14
+
15
+
16
+ class BasePageBuilder(ABC):
17
+ """Base class for all page builders."""
18
+
19
+ @staticmethod
20
+ @abstractmethod
21
+ def register_pages() -> None:
22
+ """Register pages."""
23
+
24
+
25
+ def gui_register_pages() -> None:
26
+ """Register pages.
27
+
28
+ This function is called by the GUI to register all pages.
29
+ """
30
+ page_builders = locate_subclasses(BasePageBuilder)
31
+ for page_builder in page_builders:
32
+ page_builder.register_pages()
33
+
34
+
35
+ def gui_run( # noqa: PLR0913, PLR0917
36
+ native: bool = True,
37
+ show: bool = False,
38
+ host: str | None = None,
39
+ port: int | None = None,
40
+ title: str = __project_name__,
41
+ icon: str = "",
42
+ watch: bool = False,
43
+ with_api: bool = False,
44
+ ) -> None:
45
+ """Start the GUI.
46
+
47
+ Args:
48
+ native: Whether to run the GUI in native mode.
49
+ show: Whether to show the GUI.
50
+ host: Host to run the GUI on.
51
+ port: Port to run the GUI on.
52
+ title: Title of the GUI.
53
+ icon: Icon for the GUI.
54
+ watch: Whether to watch for changes and reload the GUI.
55
+ with_api: Whether to mount the API.
56
+
57
+ Raises:
58
+ ValueError: If with_notebook is True but notebook_path is None.
59
+ """
60
+ if with_api:
61
+ from ..api import api # noqa: PLC0415, TID252
62
+
63
+ app.mount("/api", api)
64
+
65
+ gui_register_pages()
66
+ ui.run(
67
+ title=title,
68
+ favicon=icon,
69
+ native=native,
70
+ reload=watch,
71
+ dark=False,
72
+ host=host,
73
+ port=port or native_app.find_open_port(),
74
+ frameless=False,
75
+ show_welcome_message=True,
76
+ show=show,
77
+ )
78
+
79
+
80
+ class GUILocalFilePicker(ui.dialog):
81
+ def __init__(
82
+ self,
83
+ directory: str,
84
+ *,
85
+ upper_limit: str | EllipsisType | None = ...,
86
+ multiple: bool = False,
87
+ show_hidden_files: bool = False,
88
+ ) -> None:
89
+ """Local File Picker.
90
+
91
+ A simple file picker that allows selecting files from the local filesystem where NiceGUI is running.
92
+
93
+ Args:
94
+ directory: The directory to start in.
95
+ upper_limit: The directory to stop at. None for no limit, default is same as starting directory.
96
+ multiple: Whether to allow multiple files to be selected.
97
+ show_hidden_files: Whether to show hidden files.
98
+ """
99
+ super().__init__()
100
+
101
+ self.path = Path(directory).expanduser()
102
+ if upper_limit is None:
103
+ self.upper_limit = None
104
+ elif upper_limit is ...:
105
+ self.upper_limit = Path(directory).expanduser()
106
+ else:
107
+ self.upper_limit = Path(upper_limit).expanduser()
108
+ self.show_hidden_files = show_hidden_files
109
+
110
+ with self, ui.card():
111
+ self.add_drives_toggle()
112
+ self.grid = (
113
+ ui.aggrid(
114
+ {
115
+ "columnDefs": [{"field": "name", "headerName": "File"}],
116
+ "rowSelection": "multiple" if multiple else "single",
117
+ },
118
+ html_columns=[0],
119
+ )
120
+ .classes("w-96")
121
+ .on("cellDoubleClicked", self.handle_double_click)
122
+ )
123
+ with ui.row().classes("w-full justify-end"):
124
+ ui.button("Cancel", on_click=self.close).props("outline").mark("BUTTON_CANCEL")
125
+ ui.button("Ok", on_click=self._handle_ok).mark("BUTTON_OK")
126
+ self.update_grid()
127
+
128
+ def add_drives_toggle(self) -> None:
129
+ if platform.system() == "Windows":
130
+ import win32api # noqa: PLC0415
131
+
132
+ drives = win32api.GetLogicalDriveStrings().split("\000")[:-1]
133
+ self.drives_toggle = ui.toggle(drives, value=drives[0], on_change=self.update_drive)
134
+
135
+ def update_drive(self) -> None:
136
+ self.path = Path(self.drives_toggle.value).expanduser()
137
+ self.update_grid()
138
+
139
+ def update_grid(self) -> None:
140
+ paths = list(self.path.glob("*"))
141
+ if not self.show_hidden_files:
142
+ paths = [p for p in paths if not p.name.startswith(".")]
143
+ paths.sort(key=lambda p: p.name.lower())
144
+ paths.sort(key=lambda p: not p.is_dir())
145
+
146
+ self.grid.options["rowData"] = [
147
+ {
148
+ "name": f"📁 <strong>{p.name}</strong>" if p.is_dir() else p.name,
149
+ "path": str(p),
150
+ }
151
+ for p in paths
152
+ ]
153
+ if (self.upper_limit is None and self.path != self.path.parent) or (
154
+ self.upper_limit is not None and self.path != self.upper_limit
155
+ ):
156
+ self.grid.options["rowData"].insert(
157
+ 0,
158
+ {
159
+ "name": "📁 <strong>..</strong>",
160
+ "path": str(self.path.parent),
161
+ },
162
+ )
163
+ self.grid.update()
164
+
165
+ def handle_double_click(self, e: events.GenericEventArguments) -> None:
166
+ self.path = Path(e.args["data"]["path"])
167
+ if self.path.is_dir():
168
+ self.update_grid()
169
+ else:
170
+ self.submit([str(self.path)])
171
+
172
+ async def _handle_ok(self) -> None:
173
+ rows = await self.grid.get_selected_rows()
174
+ self.submit([r["path"] for r in rows])
@@ -0,0 +1,61 @@
1
+ """System service."""
2
+
3
+ from collections.abc import Callable
4
+
5
+ import marimo
6
+ from fastapi import APIRouter, FastAPI
7
+
8
+ from ..constants import NOTEBOOK_APP, NOTEBOOK_FOLDER # noqa: TID252
9
+ from ._health import Health
10
+ from ._log import get_logger
11
+
12
+ logger = get_logger(__name__)
13
+
14
+
15
+ def register_health_endpoint(router: APIRouter) -> Callable[..., Health]:
16
+ """Register health endpoint to the given router.
17
+
18
+ Args:
19
+ router: The router to register the health endpoint to.
20
+
21
+ Returns:
22
+ Callable[..., Health]: The health endpoint function.
23
+ """
24
+
25
+ @router.get("/healthz")
26
+ def health_endpoint() -> Health:
27
+ """Determine health of the app.
28
+
29
+ Returns:
30
+ Health: Health.
31
+ """
32
+ return Health(status=Health.Code.UP)
33
+
34
+ return health_endpoint
35
+
36
+
37
+ def create_marimo_app() -> FastAPI:
38
+ """Create a FastAPI app with marimo notebook server.
39
+
40
+ Returns:
41
+ FastAPI: FastAPI app with marimo notebook server.
42
+
43
+ Raises:
44
+ ValueError: If the notebook directory does not exist.
45
+ """
46
+ server = marimo.create_asgi_app(include_code=True)
47
+ if not NOTEBOOK_FOLDER.is_dir():
48
+ logger.critical(
49
+ "Directory %s does not exist. Please create the directory and add your notebooks.",
50
+ NOTEBOOK_FOLDER,
51
+ )
52
+ message = f"Directory {NOTEBOOK_FOLDER} does not exist. Please create and add your notebooks."
53
+ raise ValueError(message)
54
+ server = server.with_app(path="/", root=str(NOTEBOOK_APP))
55
+ # .with_dynamic_directory(path="/dashboard", directory=str(self._settings.directory))
56
+ app = FastAPI()
57
+ router = APIRouter(tags=["marimo"])
58
+ register_health_endpoint(router)
59
+ app.include_router(router)
60
+ app.mount("/", server.build())
61
+ return app
@@ -2,6 +2,7 @@
2
2
 
3
3
  import os
4
4
  import sys
5
+ from pathlib import Path
5
6
 
6
7
  from ._log import logging_initialize
7
8
  from ._logfire import logfire_initialize
@@ -9,6 +10,11 @@ from ._sentry import sentry_initialize
9
10
 
10
11
  _boot_called = False
11
12
 
13
+ # Import vendored dependencies
14
+ vendored_dir = Path(__file__).parent.absolute() / ".vendored"
15
+ if vendored_dir.is_dir() and str(vendored_dir) not in sys.path:
16
+ sys.path.insert(0, str(vendored_dir))
17
+
12
18
 
13
19
  def boot(modules_to_instrument: list[str]) -> None:
14
20
  """Boot the application.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oe-python-template-example
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: 🧠 Example project scaffolded and kept up to date with OE Python Template (oe-python-template).
5
5
  Project-URL: Homepage, https://oe-python-template-example.readthedocs.io/en/latest/
6
6
  Project-URL: Documentation, https://oe-python-template-example.readthedocs.io/en/latest/
@@ -50,6 +50,7 @@ Classifier: Typing :: Typed
50
50
  Requires-Python: <4.0,>=3.11
51
51
  Requires-Dist: fastapi[all,standard]>=0.115.12
52
52
  Requires-Dist: logfire[system-metrics]>=3.13.1
53
+ Requires-Dist: nicegui[native]>=2.15.0
53
54
  Requires-Dist: opentelemetry-instrumentation-fastapi>=0.53b0
54
55
  Requires-Dist: opentelemetry-instrumentation-httpx>=0.53b0
55
56
  Requires-Dist: opentelemetry-instrumentation-jinja2>=0.53b0
@@ -59,15 +60,15 @@ Requires-Dist: opentelemetry-instrumentation-tornado>=0.53b0
59
60
  Requires-Dist: opentelemetry-instrumentation-urllib3>=0.53b0
60
61
  Requires-Dist: opentelemetry-instrumentation-urllib>=0.53b0
61
62
  Requires-Dist: psutil>=7.0.0
62
- Requires-Dist: pydantic-settings>=2.8.1
63
- Requires-Dist: pydantic>=2.11.3
64
- Requires-Dist: sentry-sdk>=2.25.1
63
+ Requires-Dist: pydantic-settings>=2.9.1
64
+ Requires-Dist: sentry-sdk>=2.26.1
65
65
  Requires-Dist: typer>=0.15.1
66
66
  Requires-Dist: uptime>=3.0.1
67
67
  Provides-Extra: examples
68
68
  Requires-Dist: jinja2>=3.1.6; extra == 'examples'
69
69
  Requires-Dist: jupyter>=1.1.1; extra == 'examples'
70
- Requires-Dist: marimo>=0.12.8; extra == 'examples'
70
+ Requires-Dist: marimo>=0.13.0; extra == 'examples'
71
+ Requires-Dist: matplotlib>=3.10.1; extra == 'examples'
71
72
  Requires-Dist: streamlit>=1.44.1; extra == 'examples'
72
73
  Description-Content-Type: text/markdown
73
74
 
@@ -151,13 +152,15 @@ Projects generated with this template come with a comprehensive development tool
151
152
  17. Changelog and release notes generated with [git-cliff](https://git-cliff.org/)
152
153
  18. Documentation generated with [Sphinx](https://www.sphinx-doc.org/en/master/) including reference documentation for the library, CLI, and API
153
154
  19. Documentation published to [Read The Docs](https://readthedocs.org/) including generation of PDF and single page HTML versions
154
- 20. Interactive OpenAPI specification with [Swagger](https://swagger.io/)
155
- 21. Python package published to [PyPI](https://pypi.org/)
156
- 22. Docker images published to [Docker.io](https://hub.docker.com/) and [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) with [artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds)
157
- 23. One-click development environments with [Dev Containers](https://code.visualstudio.com/docs/devcontainers/containers) and [GitHub Codespaces](https://github.com/features/codespaces)
158
- 24. Settings for use with [VSCode](https://code.visualstudio.com/)
159
- 25. Settings and custom instructions for use with [GitHub Copilot](https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot)
160
- 26. API deployed as serverless function to [Vercel](https://vercel.com/) (optional)
155
+ 20. Documentation including dynamic badges, setup instructions, contribution guide and security policy
156
+ 21. Interactive OpenAPI specification with [Swagger](https://swagger.io/)
157
+ 22. Python package published to [PyPI](https://pypi.org/)
158
+ 23. Multi-stage build of fat and slim (no-extras) Docker images, app running nonroot
159
+ 24. Mult-arch Docker images published to [Docker.io](https://hub.docker.com/) and [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) with [artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds)
160
+ 25. One-click development environments with [Dev Containers](https://code.visualstudio.com/docs/devcontainers/containers) and [GitHub Codespaces](https://github.com/features/codespaces)
161
+ 26. Settings for use with [VSCode](https://code.visualstudio.com/)
162
+ 27. Settings and custom instructions for use with [GitHub Copilot](https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot)
163
+ 28. API deployed as serverless function to [Vercel](https://vercel.com/) (optional)
161
164
 
162
165
  ### Application Features
163
166
 
@@ -165,17 +168,20 @@ Beyond development tooling, projects generated with this template include the co
165
168
 
166
169
  1. Usable as library with "Hello" module exposing a simple service that can say "Hello, world!" and echo utterances.
167
170
  2. Command-line interface (CLI) with [Typer](https://typer.tiangolo.com/)
168
- 3. Versioned webservice API with [FastAPI](https://fastapi.tiangolo.com/)
169
- 4. [Interactive Jupyter notebook](https://jupyter.org/) and [reactive Marimo notebook](https://marimo.io/)
170
- 5. Simple Web UI with [Streamlit](https://streamlit.io/)
171
- 6. Configuration to run the CLI and API in a Docker container including setup for [Docker Compose](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-docker-compose/)
171
+ 2. Versioned webservice API with [FastAPI](https://fastapi.tiangolo.com/)
172
+ 3. Comfortable command-line interface (CLI) with
173
+ [Typer](https://typer.tiangolo.com/)
174
+ 4. Cross-platform Graphical User Interface (GUI) with
175
+ [NiceGUI](https://nicegui.io/) running in native window
176
+ 5. [Interactive Jupyter notebook](https://jupyter.org/) and [reactive Marimo notebook](https://marimo.io/)
177
+ 6. Simple Web UI with [Streamlit](https://streamlit.io/)
172
178
  7. Validation and settings management with [pydantic](https://docs.pydantic.dev/)
173
- 8. Info command enabling to inspect the runtime, compiled settings, and further info provided dynamically by modules
174
- 9. Health endpoint exposing system health dynamically aggregated from all modules and dependencies
175
- 10. Flexible logging and instrumentation, including support for [Sentry](https://sentry.io/) and [Logfire](https://logfire.dev/)
176
- 11. Hello service demonstrates use of custom real time metrics collected via Logfire
177
- 12. Modular architecture including auto-registration of services, CLI commands and API routes exposed by modules
178
- 13. Documentation including dynamic badges, setup instructions, contribution guide and security policy
179
+ 8. Flexible logging and instrumentation, including support for [Sentry](https://sentry.io/) and [Logfire](https://logfire.dev/)
180
+ 9. Modular architecture including auto-registration of services, CLI commands, API routes and GUI pages exposed by domain modules
181
+ 10. System module providing aggregate health and info to the runtime, compiled settings, and further info provided by domain modules
182
+ 11. Health and Info available via command, webservice API (info passsword protected) and GUI
183
+ 12. Hello service demonstrates use of custom real time metrics collected via Logfire
184
+ 13. Configuration to run the CLI and API in a Docker container including setup for [Docker Compose](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-docker-compose/)
179
185
 
180
186
  Explore [here](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template-example) for what's generated out of the box.
181
187
 
@@ -242,9 +248,13 @@ pip install oe-python-template-example # add dependency to your project
242
248
  Executing the command line interface (CLI) in an isolated Python environment is just as easy:
243
249
 
244
250
  ```shell
245
- uvx oe-python-template-example hello-world # prints "Hello, world! [..]"
246
- uvx oe-python-template-example serve # serves web API
247
- uvx oe-python-template-example serve --port=4711 # serves web API on port 4711
251
+ uvx oe-python-template-example hello world # prints "Hello, world! [..]"
252
+ uvx oe-python-template-example hello echo "Lorem Ipsum" # echos "Lorem Ipsum"
253
+ uvx oe-python-template-example gui # opens the graphical user interface (GUI)
254
+ uvx --with "oe-python-template-example[examples]" oe-python-template-example gui # opens the graphical user interface (GUI) with support for scientific computing
255
+ uvx oe-python-template-example system serve # serves web API
256
+ uvx oe-python-template-example system serve --port=4711 # serves web API on port 4711
257
+ uvx oe-python-template-example system openapi # serves web API on port 4711
248
258
  ```
249
259
 
250
260
  Notes:
@@ -257,10 +267,11 @@ The CLI provides extensive help:
257
267
 
258
268
  ```shell
259
269
  uvx oe-python-template-example --help # all CLI commands
260
- uvx oe-python-template-example hello-world --help # help for specific command
261
- uvx oe-python-template-example echo --help
262
- uvx oe-python-template-example openapi --help
263
- uvx oe-python-template-example serve --help
270
+ uvx oe-python-template-example hello world --help # help for specific command
271
+ uvx oe-python-template-example hello echo --help
272
+ uvx oe-python-template-example gui --help
273
+ uvx oe-python-template-example system serve --help
274
+ uvx oe-python-template-example system openapi --help
264
275
  ```
265
276
 
266
277
 
@@ -388,6 +399,8 @@ uvx oe-python-template-example hello world
388
399
  uvx oe-python-template-example hello echo --help
389
400
  uvx oe-python-template-example hello echo "Lorem"
390
401
  uvx oe-python-template-example hello echo "Lorem" --json
402
+ uvx oe-python-template-example gui
403
+ uvx --with "oe-python-template-example[examples]" oe-python-template-example gui # opens the graphical user interface (GUI) with support for scientific computing
391
404
  uvx oe-python-template-example system info
392
405
  uvx oe-python-template-example system health
393
406
  uvx oe-python-template-example system openapi
@@ -472,6 +485,15 @@ echo "Shutting down the API container ..."
472
485
  docker compose down
473
486
  ```
474
487
 
488
+ #### Slim
489
+
490
+ The default Docker image includes all extras. Additionally a slim image is provided, with no extras. Run as follows
491
+
492
+ ```shell
493
+ docker compose run --remove-orphans oe-python-template-example-slim --help
494
+ ```
495
+
496
+
475
497
  * See the [reference documentation of the API](https://oe-python-template-example.readthedocs.io/en/latest/api_reference_v1.html) for detailed documentation of all API operations and parameters.
476
498
 
477
499
 
@@ -1,35 +1,40 @@
1
1
  oe_python_template_example/__init__.py,sha256=_Z3Xb-x95UODU66avOiwROVaouk_s0ZNB25KFnPoS40,226
2
- oe_python_template_example/api.py,sha256=cuGqjxfJocXZBZzz4UhUeUxH4Sm79tpzc6t5G-9Lp_E,2206
3
- oe_python_template_example/cli.py,sha256=rNcaCyYkm5_e-ITJJLUKvJfJ_RsGXQk0dnfiFCifTq8,679
4
- oe_python_template_example/constants.py,sha256=fm8J1LIYF9B4Ur3r5PoUdHyPSjIXWuNydHVoCbz9Jx8,171
5
- oe_python_template_example/hello/__init__.py,sha256=7C7gIdxkydk79-4QPyVqyUHhqP3RMFShSptzE_2J9jo,289
2
+ oe_python_template_example/api.py,sha256=gwUAW_pWUABFtAAiuIg3-X-dG25m1l0rqpnMqlaGEJo,2206
3
+ oe_python_template_example/cli.py,sha256=fnE06WJFcks5N4LwWlgIDHICciKY1FM944aAdJM6Ju0,1644
4
+ oe_python_template_example/constants.py,sha256=eRFVkR2qwafucpx5ppJSohr5wpSRm4yh1deLpOTep9A,346
5
+ oe_python_template_example/hello/__init__.py,sha256=F7aJ_uhTPPnFLT_4GjI2GzjAmKrUhfe4KxqzrljuCVo,485
6
6
  oe_python_template_example/hello/_api.py,sha256=B4gCojmEkvh-ScKPz0rXW70r4gVvg7SX2dfbZpUd2vU,2302
7
7
  oe_python_template_example/hello/_cli.py,sha256=D1RZyz8sk7wpH1a9VDx1QtsNorOr9owxK8N7SxaaMWM,1200
8
8
  oe_python_template_example/hello/_constants.py,sha256=6aRleAIcdgC13TeTzI07YwjoSwqGb2g131dw8aEoM4I,109
9
+ oe_python_template_example/hello/_gui.py,sha256=cYzag6lLD-zQGgTBcgXRDfbuxjtF927OVrvLAcFfpHs,1470
9
10
  oe_python_template_example/hello/_models.py,sha256=JtI7wGT72u23NOxFa-oeWzdyiMg7PnHL5eg22im2_yQ,574
10
11
  oe_python_template_example/hello/_service.py,sha256=o9sgk-yFX5zRi06sAOdSPqkgT93naxn-JRknwK2Nnvs,3183
11
12
  oe_python_template_example/hello/_settings.py,sha256=Q14SqSvBJYFuofA-tbvBwO30sVygSaXsgaYC7x1uCfo,1562
12
- oe_python_template_example/system/__init__.py,sha256=NNgODkr7AyJjTTJiv3pys7o2z6xi1G96g0vnsxVhlI4,427
13
+ oe_python_template_example/system/__init__.py,sha256=7e2z8HATzy3dAIBXy5PM9rlCC7Rbu8m8NapROdrf3Wk,624
13
14
  oe_python_template_example/system/_api.py,sha256=rE9Aau3IIHXdEkOBUXOwJ7SxN3cZpgtYEuojnSWfT_4,3687
14
- oe_python_template_example/system/_cli.py,sha256=J_4upBBdbSxbONPYmOZPbuhZcKjfnPIUeZpp0L7lY-Q,4846
15
- oe_python_template_example/system/_service.py,sha256=zdnO6e8ShgrsIK3W2KbIEYpRIp9lQw35f1UFDUn1md8,6334
15
+ oe_python_template_example/system/_cli.py,sha256=D19TuXtGGmxzQ-VkmCD7fmuqKQ5TQoJq9O0VzJKvWVE,6960
16
+ oe_python_template_example/system/_gui.py,sha256=uKI-tlBSJXMaxY79wgdYtMttEyu8BLQC1UQLEPOcoZg,653
17
+ oe_python_template_example/system/_service.py,sha256=f-EtWEAanajo4S2KeGm26K9_RezYgPza1qVqPRBOFvY,6376
16
18
  oe_python_template_example/system/_settings.py,sha256=MwMAJYifJ6jGImeSh4e9shmIXmiUSuQGHXz_Ts0mSdk,901
17
- oe_python_template_example/utils/__init__.py,sha256=MVy9A4miNYBFkNc0f24-KGG25mftb8BzDmyBb43lvQE,1559
19
+ oe_python_template_example/utils/__init__.py,sha256=ggcc7xvH6MJ3kP45lfjmp8yZSPDTeQRaQYbZbG0M7yM,1924
18
20
  oe_python_template_example/utils/_api.py,sha256=w3hPQK1pL2gBI4_1qNWNa2b4S_oH-8mY-ckRX0KrCWM,617
19
21
  oe_python_template_example/utils/_cli.py,sha256=J_mFtXZ1gGeovGrE5i3wlokTOBfiTTKEz5magiRP7GA,2091
20
22
  oe_python_template_example/utils/_console.py,sha256=u0-utcdRmVu4rabrYUyNOx8yPxLhxB3E92m22kSCwPQ,293
21
- oe_python_template_example/utils/_constants.py,sha256=AP1a4kJqyf3HG6YFupUREvlkTX1b-Xx1H4u0O1nMfzc,2054
23
+ oe_python_template_example/utils/_constants.py,sha256=FRe5ZNaBwpBPwOHZVWYOlI-ijamfzdBVr8gl7gHMGT0,2932
22
24
  oe_python_template_example/utils/_di.py,sha256=KdjiD4xZ_QSfbddkKWwsPJmG5YrIg6dzuBrlsd-FhxA,2189
25
+ oe_python_template_example/utils/_gui.py,sha256=RrvhaIi62X0E_lYou5GdB6LpQcCta2MeSvOKemh6ua4,5460
23
26
  oe_python_template_example/utils/_health.py,sha256=35QOWe2r5InrEpGtuVMym9dI5aRHS0HWf4BHBRAUIj0,4102
24
27
  oe_python_template_example/utils/_log.py,sha256=ZW4gs540SdjVK-2KeheLfDY15d_3xpO5FyGn7wTXyaM,3592
25
28
  oe_python_template_example/utils/_logfire.py,sha256=wZYNVowQx7kh3XJoJ59FjUKdrta7tp6cXOJRUT6lDU8,2128
29
+ oe_python_template_example/utils/_notebook.py,sha256=oBQw9IBcXjuhzd1ECfOEPN4WJHGOm9xiPtrs11GtWG4,1777
26
30
  oe_python_template_example/utils/_process.py,sha256=40R0NZMqJUn0iUPERzohSUpJgU1HcJApIg1HipIxFCw,941
27
31
  oe_python_template_example/utils/_sentry.py,sha256=2sXrDSZSYoDEM87v7CakJ6eGBtcIhDI48PsQCLwOHgg,3319
28
32
  oe_python_template_example/utils/_service.py,sha256=atHAejvBucKXjzhsMSdOBBFa7rRD74zcV70Pp0pl0Tg,1038
29
33
  oe_python_template_example/utils/_settings.py,sha256=owFoaHEzJnVD3EVyOWF4rfIY7g6eLnU6rN0m4VHhCbA,2464
30
- oe_python_template_example/utils/boot.py,sha256=TBgmqbtIryQz0cAozYzxhYQRIldfbJ6v9R-rH6sO9mY,2696
31
- oe_python_template_example-0.4.1.dist-info/METADATA,sha256=oMX2A7I4UAdWVADa4DIdC3utBEbq0in_9OD_eJ7Kgc8,33326
32
- oe_python_template_example-0.4.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
33
- oe_python_template_example-0.4.1.dist-info/entry_points.txt,sha256=S2eCPB45b1Wgj_GsDRFAN-e4h7dBA5UPxT8od98erDE,82
34
- oe_python_template_example-0.4.1.dist-info/licenses/LICENSE,sha256=5H409K6xzz9U5eUaoAHQExNkoWJRlU0LEj6wL2QJ34s,1113
35
- oe_python_template_example-0.4.1.dist-info/RECORD,,
34
+ oe_python_template_example/utils/boot.py,sha256=Qgq1sjT8d3fcfibnMyx5CVUJ_KKXgFI3ozZUIJbhh4I,2921
35
+ oe_python_template_example/utils/.vendored/bottle.py,sha256=D9YkHqkgvKkknd3ZQ-tgflUF6F9Kr2_jpzhod5WqfAM,175461
36
+ oe_python_template_example-0.4.3.dist-info/METADATA,sha256=d-7MUnXzb7WLnxp0YXs6GCxWD85_ziJjvkdba-H3tek,34643
37
+ oe_python_template_example-0.4.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
+ oe_python_template_example-0.4.3.dist-info/entry_points.txt,sha256=S2eCPB45b1Wgj_GsDRFAN-e4h7dBA5UPxT8od98erDE,82
39
+ oe_python_template_example-0.4.3.dist-info/licenses/LICENSE,sha256=5H409K6xzz9U5eUaoAHQExNkoWJRlU0LEj6wL2QJ34s,1113
40
+ oe_python_template_example-0.4.3.dist-info/RECORD,,