oe-python-template-example 0.0.8__py3-none-any.whl → 0.0.10__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_example/api.py +95 -14
- oe_python_template_example/cli.py +28 -9
- oe_python_template_example/service.py +10 -0
- {oe_python_template_example-0.0.8.dist-info → oe_python_template_example-0.0.10.dist-info}/METADATA +21 -6
- oe_python_template_example-0.0.10.dist-info/RECORD +10 -0
- oe_python_template_example-0.0.8.dist-info/RECORD +0 -10
- {oe_python_template_example-0.0.8.dist-info → oe_python_template_example-0.0.10.dist-info}/WHEEL +0 -0
- {oe_python_template_example-0.0.8.dist-info → oe_python_template_example-0.0.10.dist-info}/entry_points.txt +0 -0
- {oe_python_template_example-0.0.8.dist-info → oe_python_template_example-0.0.10.dist-info}/licenses/LICENSE +0 -0
@@ -1,13 +1,18 @@
|
|
1
|
-
"""
|
1
|
+
"""Webservice API of OE Python Template Example.
|
2
2
|
|
3
|
-
This module provides a
|
3
|
+
This module provides a webservice API with several endpoints:
|
4
|
+
- A health/healthz endpoint that returns the health status of the service
|
4
5
|
- A hello-world endpoint that returns a greeting message
|
5
6
|
- An echo endpoint that echoes back the provided text
|
6
7
|
|
7
8
|
The endpoints use Pydantic models for request and response validation.
|
8
9
|
"""
|
9
10
|
|
10
|
-
from
|
11
|
+
from collections.abc import Generator
|
12
|
+
from enum import StrEnum
|
13
|
+
from typing import Annotated
|
14
|
+
|
15
|
+
from fastapi import Depends, FastAPI, Response, status
|
11
16
|
from pydantic import BaseModel, Field
|
12
17
|
|
13
18
|
from oe_python_template_example import Service
|
@@ -15,6 +20,20 @@ from oe_python_template_example import Service
|
|
15
20
|
HELLO_WORLD_EXAMPLE = "Hello, world!"
|
16
21
|
|
17
22
|
|
23
|
+
def get_service() -> Generator[Service, None, None]:
|
24
|
+
"""Get the service instance.
|
25
|
+
|
26
|
+
Yields:
|
27
|
+
Service: The service instance.
|
28
|
+
"""
|
29
|
+
service = Service()
|
30
|
+
try:
|
31
|
+
yield service
|
32
|
+
finally:
|
33
|
+
# Cleanup code if needed
|
34
|
+
pass
|
35
|
+
|
36
|
+
|
18
37
|
app = FastAPI(
|
19
38
|
version="1.0.0",
|
20
39
|
title="OE Python Template Example",
|
@@ -27,6 +46,68 @@ app = FastAPI(
|
|
27
46
|
)
|
28
47
|
|
29
48
|
|
49
|
+
class _HealthStatus(StrEnum):
|
50
|
+
"""Health status enumeration.
|
51
|
+
|
52
|
+
Args:
|
53
|
+
StrEnum (_type_): _description_
|
54
|
+
"""
|
55
|
+
|
56
|
+
UP = "UP"
|
57
|
+
DOWN = "DOWN"
|
58
|
+
|
59
|
+
|
60
|
+
class Health(BaseModel):
|
61
|
+
"""Health status model.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
BaseModel (_type_): _description_
|
65
|
+
"""
|
66
|
+
|
67
|
+
status: _HealthStatus
|
68
|
+
reason: str | None = None
|
69
|
+
|
70
|
+
|
71
|
+
class HealthResponse(BaseModel):
|
72
|
+
"""Response model for health endpoint."""
|
73
|
+
|
74
|
+
health: str = Field(
|
75
|
+
...,
|
76
|
+
description="The hello world message",
|
77
|
+
examples=[HELLO_WORLD_EXAMPLE],
|
78
|
+
)
|
79
|
+
|
80
|
+
|
81
|
+
@app.get("/healthz", tags=["Observability"])
|
82
|
+
@app.get("/health", tags=["Observability"])
|
83
|
+
async def health(service: Annotated[Service, Depends(get_service)], response: Response) -> Health:
|
84
|
+
"""Check the health of the service.
|
85
|
+
|
86
|
+
This endpoint returns the health status of the service.
|
87
|
+
The health status can be either UP or DOWN.
|
88
|
+
If the service is healthy, the status will be UP.
|
89
|
+
If the service is unhealthy, the status will be DOWN and a reason will be provided.
|
90
|
+
The response will have a 200 OK status code if the service is healthy,
|
91
|
+
and a 500 Internal Server Error status code if the service is unhealthy.
|
92
|
+
|
93
|
+
Args:
|
94
|
+
service (Annotated[Service, Depends): _description_
|
95
|
+
response (Response): _description_
|
96
|
+
|
97
|
+
Returns:
|
98
|
+
Health: The health status of the service.
|
99
|
+
"""
|
100
|
+
if service.healthy():
|
101
|
+
health_result = Health(status=_HealthStatus.UP)
|
102
|
+
else:
|
103
|
+
health_result = Health(status=_HealthStatus.DOWN, reason="Service is unhealthy")
|
104
|
+
|
105
|
+
if health_result.status == _HealthStatus.DOWN:
|
106
|
+
response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
107
|
+
|
108
|
+
return health_result
|
109
|
+
|
110
|
+
|
30
111
|
class HelloWorldResponse(BaseModel):
|
31
112
|
"""Response model for hello-world endpoint."""
|
32
113
|
|
@@ -37,6 +118,17 @@ class HelloWorldResponse(BaseModel):
|
|
37
118
|
)
|
38
119
|
|
39
120
|
|
121
|
+
@app.get("/hello-world", tags=["Basics"])
|
122
|
+
async def hello_world() -> HelloWorldResponse:
|
123
|
+
"""
|
124
|
+
Return a hello world message.
|
125
|
+
|
126
|
+
Returns:
|
127
|
+
HelloWorldResponse: A response containing the hello world message.
|
128
|
+
"""
|
129
|
+
return HelloWorldResponse(message=Service.get_hello_world())
|
130
|
+
|
131
|
+
|
40
132
|
class EchoResponse(BaseModel):
|
41
133
|
"""Response model for echo endpoint."""
|
42
134
|
|
@@ -59,17 +151,6 @@ class EchoRequest(BaseModel):
|
|
59
151
|
)
|
60
152
|
|
61
153
|
|
62
|
-
@app.get("/hello-world", tags=["Basics"])
|
63
|
-
async def hello_world() -> HelloWorldResponse:
|
64
|
-
"""
|
65
|
-
Return a hello world message.
|
66
|
-
|
67
|
-
Returns:
|
68
|
-
HelloWorldResponse: A response containing the hello world message.
|
69
|
-
"""
|
70
|
-
return HelloWorldResponse(message=Service.get_hello_world())
|
71
|
-
|
72
|
-
|
73
154
|
@app.post("/echo", tags=["Basics"])
|
74
155
|
async def echo(request: EchoRequest) -> EchoResponse:
|
75
156
|
"""
|
@@ -1,5 +1,6 @@
|
|
1
|
-
"""Command
|
1
|
+
"""CLI (Command Line Interface) of OE Python Template Example."""
|
2
2
|
|
3
|
+
from enum import StrEnum
|
3
4
|
from typing import Annotated
|
4
5
|
|
5
6
|
import typer
|
@@ -44,7 +45,7 @@ def hello_world() -> None:
|
|
44
45
|
def serve(
|
45
46
|
host: Annotated[str, typer.Option(help="Host to bind the server to")] = "127.0.0.1",
|
46
47
|
port: Annotated[int, typer.Option(help="Port to bind the server to")] = 8000,
|
47
|
-
|
48
|
+
watch: Annotated[bool, typer.Option(help="Enable auto-reload")] = True,
|
48
49
|
) -> None:
|
49
50
|
"""Start the API server."""
|
50
51
|
console.print(f"Starting API server at http://{host}:{port}")
|
@@ -52,22 +53,40 @@ def serve(
|
|
52
53
|
"oe_python_template_example.api:app",
|
53
54
|
host=host,
|
54
55
|
port=port,
|
55
|
-
reload=
|
56
|
+
reload=watch,
|
56
57
|
)
|
57
58
|
|
58
59
|
|
60
|
+
class OutputFormat(StrEnum):
|
61
|
+
"""
|
62
|
+
Enum representing the supported output formats.
|
63
|
+
|
64
|
+
This enum defines the possible formats for output data:
|
65
|
+
- YAML: Output data in YAML format
|
66
|
+
- JSON: Output data in JSON format
|
67
|
+
|
68
|
+
Usage:
|
69
|
+
format = OutputFormat.YAML
|
70
|
+
print(f"Using {format} format")
|
71
|
+
"""
|
72
|
+
|
73
|
+
YAML = "yaml"
|
74
|
+
JSON = "json"
|
75
|
+
|
76
|
+
|
59
77
|
@cli.command()
|
60
78
|
def openapi(
|
61
79
|
output_format: Annotated[
|
62
|
-
|
63
|
-
] =
|
80
|
+
OutputFormat, typer.Option(help="Output format", case_sensitive=False)
|
81
|
+
] = OutputFormat.YAML,
|
64
82
|
) -> None:
|
65
83
|
"""Dump the OpenAPI specification to stdout (YAML by default)."""
|
66
84
|
schema = api.openapi()
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
85
|
+
match output_format:
|
86
|
+
case OutputFormat.JSON:
|
87
|
+
console.print_json(data=schema)
|
88
|
+
case OutputFormat.YAML:
|
89
|
+
console.print(yaml.dump(schema, default_flow_style=False), end="")
|
71
90
|
|
72
91
|
|
73
92
|
def _apply_cli_settings(cli: typer.Typer, epilog: str) -> None:
|
@@ -13,6 +13,7 @@ class Service:
|
|
13
13
|
|
14
14
|
def __init__(self) -> None:
|
15
15
|
"""Initialize service."""
|
16
|
+
self.is_healthy = True
|
16
17
|
|
17
18
|
@staticmethod
|
18
19
|
def get_hello_world() -> str:
|
@@ -23,3 +24,12 @@ class Service:
|
|
23
24
|
str: Hello world message.
|
24
25
|
"""
|
25
26
|
return f"Hello, world! The value of THE_VAR is {THE_VAR}"
|
27
|
+
|
28
|
+
def healthy(self) -> bool:
|
29
|
+
"""
|
30
|
+
Check if the service is healthy.
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
bool: True if the service is healthy, False otherwise.
|
34
|
+
"""
|
35
|
+
return self.is_healthy
|
{oe_python_template_example-0.0.8.dist-info → oe_python_template_example-0.0.10.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: oe-python-template-example
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.10
|
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/
|
@@ -184,7 +184,7 @@ uvx oe-python-template-example hello-world --help # help for specific command
|
|
184
184
|
|
185
185
|
* Example project scaffolded and kept up to date with OE Python Template (oe-python-template).
|
186
186
|
* Various Examples:
|
187
|
-
- [Simple Python script]https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template-example/blob/main/examples/script.py)
|
187
|
+
- [Simple Python script](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template-example/blob/main/examples/script.py)
|
188
188
|
- [Streamlit web application](https://oe-python-template-example.streamlit.app/) deployed on [Streamlit Community Cloud](https://streamlit.io/cloud)
|
189
189
|
- [Jupyter](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template-example/blob/main/examples/notebook.ipynb) and [Marimo](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template-example/blob/main/examples/notebook.py) notebook
|
190
190
|
* [Complete reference documenation](https://oe-python-template-example.readthedocs.io/en/latest/reference.html) on Read the Docs
|
@@ -284,8 +284,12 @@ Execute commands:
|
|
284
284
|
|
285
285
|
```shell
|
286
286
|
uvx oe-python-template-example hello-world
|
287
|
-
uvx oe-python-template-example
|
288
|
-
uvx oe-python-template-example echo "Lorem
|
287
|
+
uvx oe-python-template-example echo --help
|
288
|
+
uvx oe-python-template-example echo "Lorem"
|
289
|
+
uvx oe-python-template-example echo "Lorem" --json
|
290
|
+
uvx oe-python-template-example openapi
|
291
|
+
uvx oe-python-template-example openapi --output-format=json
|
292
|
+
uvx oe-python-template-example serve
|
289
293
|
```
|
290
294
|
|
291
295
|
### Environment
|
@@ -306,8 +310,12 @@ You can as well run the CLI within Docker.
|
|
306
310
|
```shell
|
307
311
|
docker run helmuthva/oe-python-template-example --help
|
308
312
|
docker run helmuthva/oe-python-template-example hello-world
|
309
|
-
docker run helmuthva/oe-python-template-example
|
313
|
+
docker run helmuthva/oe-python-template-example echo --help
|
310
314
|
docker run helmuthva/oe-python-template-example echo "Lorem"
|
315
|
+
docker run helmuthva/oe-python-template-example echo "Lorem" --json
|
316
|
+
docker run helmuthva/oe-python-template-example openapi
|
317
|
+
docker run helmuthva/oe-python-template-example openapi --output-format=json
|
318
|
+
docker run helmuthva/oe-python-template-example serve
|
311
319
|
```
|
312
320
|
|
313
321
|
Execute command:
|
@@ -321,8 +329,15 @@ Or use docker compose
|
|
321
329
|
The .env is passed through from the host to the Docker container.
|
322
330
|
|
323
331
|
```shell
|
324
|
-
docker compose up
|
325
332
|
docker compose run oe-python-template-example --help
|
333
|
+
docker compose run oe-python-template-example hello-world
|
334
|
+
docker compose run oe-python-template-example echo --help
|
335
|
+
docker compose run oe-python-template-example echo "Lorem"
|
336
|
+
docker compose run oe-python-template-example echo "Lorem" --json
|
337
|
+
docker compose run oe-python-template-example openapi
|
338
|
+
docker compose run oe-python-template-example openapi --output-format=json
|
339
|
+
docker compose up
|
340
|
+
curl http://127.0.0.1 8000
|
326
341
|
```
|
327
342
|
|
328
343
|
## Extra: Lorem Ipsum
|
@@ -0,0 +1,10 @@
|
|
1
|
+
oe_python_template_example/__init__.py,sha256=-sCwS9lD6CvgWw88f7snBDF947PWIhEupJVebXL_w1M,314
|
2
|
+
oe_python_template_example/api.py,sha256=HMcssyqFSXXVxAxhXz8Mb-5TtVGxR09R-uS8-1c7mVw,4346
|
3
|
+
oe_python_template_example/cli.py,sha256=WL5Mn4U0jnORqjphfSxDlhFiPIAlz0RiSWPOLlB46jw,2824
|
4
|
+
oe_python_template_example/constants.py,sha256=6uQHr2CRgzWQWhUQCRRKiPuFhzKB2iblZk3dIRQ5dDc,358
|
5
|
+
oe_python_template_example/service.py,sha256=XlCrklSRy8_YaYvlVYiDFPUubHHm-J8BPx2f7_niGG4,760
|
6
|
+
oe_python_template_example-0.0.10.dist-info/METADATA,sha256=bYxQ6xq0kI3Qq3cUZ32esYtBhlTrDv9rWaSP1jBXXWg,20852
|
7
|
+
oe_python_template_example-0.0.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
8
|
+
oe_python_template_example-0.0.10.dist-info/entry_points.txt,sha256=S2eCPB45b1Wgj_GsDRFAN-e4h7dBA5UPxT8od98erDE,82
|
9
|
+
oe_python_template_example-0.0.10.dist-info/licenses/LICENSE,sha256=5H409K6xzz9U5eUaoAHQExNkoWJRlU0LEj6wL2QJ34s,1113
|
10
|
+
oe_python_template_example-0.0.10.dist-info/RECORD,,
|
@@ -1,10 +0,0 @@
|
|
1
|
-
oe_python_template_example/__init__.py,sha256=-sCwS9lD6CvgWw88f7snBDF947PWIhEupJVebXL_w1M,314
|
2
|
-
oe_python_template_example/api.py,sha256=3OmZFB_5bVdHS6wcGttfgiOmq86K5R6lXOzvTPXIQ94,2203
|
3
|
-
oe_python_template_example/cli.py,sha256=ptgFKScjYQAj364TCBqs23rVZqIpXUxUXEoc_4f1YJE,2399
|
4
|
-
oe_python_template_example/constants.py,sha256=6uQHr2CRgzWQWhUQCRRKiPuFhzKB2iblZk3dIRQ5dDc,358
|
5
|
-
oe_python_template_example/service.py,sha256=ZpsZFnnJm_3EqoVqGomfAyIjLVmWJFuJ3G9qatWj5yI,516
|
6
|
-
oe_python_template_example-0.0.8.dist-info/METADATA,sha256=E5qbQjBSvgDTEqxceoqIYvOMjekUwGc3LRUXYWEmxI4,20031
|
7
|
-
oe_python_template_example-0.0.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
8
|
-
oe_python_template_example-0.0.8.dist-info/entry_points.txt,sha256=S2eCPB45b1Wgj_GsDRFAN-e4h7dBA5UPxT8od98erDE,82
|
9
|
-
oe_python_template_example-0.0.8.dist-info/licenses/LICENSE,sha256=5H409K6xzz9U5eUaoAHQExNkoWJRlU0LEj6wL2QJ34s,1113
|
10
|
-
oe_python_template_example-0.0.8.dist-info/RECORD,,
|
{oe_python_template_example-0.0.8.dist-info → oe_python_template_example-0.0.10.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|
File without changes
|