oe-python-template-example 0.0.9__py3-none-any.whl → 0.1.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_example/api.py +166 -16
- oe_python_template_example/cli.py +54 -12
- oe_python_template_example/service.py +10 -0
- {oe_python_template_example-0.0.9.dist-info → oe_python_template_example-0.1.0.dist-info}/METADATA +57 -21
- oe_python_template_example-0.1.0.dist-info/RECORD +10 -0
- oe_python_template_example-0.0.9.dist-info/RECORD +0 -10
- {oe_python_template_example-0.0.9.dist-info → oe_python_template_example-0.1.0.dist-info}/WHEEL +0 -0
- {oe_python_template_example-0.0.9.dist-info → oe_python_template_example-0.1.0.dist-info}/entry_points.txt +0 -0
- {oe_python_template_example-0.0.9.dist-info → oe_python_template_example-0.1.0.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,7 +20,44 @@ from oe_python_template_example import Service
|
|
15
20
|
HELLO_WORLD_EXAMPLE = "Hello, world!"
|
16
21
|
|
17
22
|
|
18
|
-
|
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
|
+
|
37
|
+
api = FastAPI(
|
38
|
+
root_path="/api",
|
39
|
+
title="OE Python Template Example",
|
40
|
+
contact={
|
41
|
+
"name": "Helmut Hoffer von Ankershoffen",
|
42
|
+
"email": "helmuthva@gmail.com",
|
43
|
+
"url": "https://github.com/helmut-hoffer-von-ankershoffen",
|
44
|
+
},
|
45
|
+
terms_of_service="https://oe-python-template-example.readthedocs.io/en/latest/",
|
46
|
+
openapi_tags=[
|
47
|
+
{
|
48
|
+
"name": "v1",
|
49
|
+
"description": "API version 1, check link on the right",
|
50
|
+
"externalDocs": {"description": "sub-docs", "url": "/api/v1/docs"},
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"name": "v2",
|
54
|
+
"description": "API version 2, check link on the right",
|
55
|
+
"externalDocs": {"description": "sub-docs", "url": "/api/v2/docs"},
|
56
|
+
},
|
57
|
+
],
|
58
|
+
)
|
59
|
+
|
60
|
+
api_v1 = FastAPI(
|
19
61
|
version="1.0.0",
|
20
62
|
title="OE Python Template Example",
|
21
63
|
contact={
|
@@ -26,6 +68,81 @@ app = FastAPI(
|
|
26
68
|
terms_of_service="https://oe-python-template-example.readthedocs.io/en/latest/",
|
27
69
|
)
|
28
70
|
|
71
|
+
api_v2 = FastAPI(
|
72
|
+
version="2.0.0",
|
73
|
+
title="OE Python Template Example",
|
74
|
+
contact={
|
75
|
+
"name": "Helmut Hoffer von Ankershoffen",
|
76
|
+
"email": "helmuthva@gmail.com",
|
77
|
+
"url": "https://github.com/helmut-hoffer-von-ankershoffen",
|
78
|
+
},
|
79
|
+
terms_of_service="https://oe-python-template-example.readthedocs.io/en/latest/",
|
80
|
+
)
|
81
|
+
|
82
|
+
|
83
|
+
class _HealthStatus(StrEnum):
|
84
|
+
"""Health status enumeration.
|
85
|
+
|
86
|
+
Args:
|
87
|
+
StrEnum (_type_): _description_
|
88
|
+
"""
|
89
|
+
|
90
|
+
UP = "UP"
|
91
|
+
DOWN = "DOWN"
|
92
|
+
|
93
|
+
|
94
|
+
class Health(BaseModel):
|
95
|
+
"""Health status model.
|
96
|
+
|
97
|
+
Args:
|
98
|
+
BaseModel (_type_): _description_
|
99
|
+
"""
|
100
|
+
|
101
|
+
status: _HealthStatus
|
102
|
+
reason: str | None = None
|
103
|
+
|
104
|
+
|
105
|
+
class HealthResponse(BaseModel):
|
106
|
+
"""Response model for health endpoint."""
|
107
|
+
|
108
|
+
health: str = Field(
|
109
|
+
...,
|
110
|
+
description="The hello world message",
|
111
|
+
examples=[HELLO_WORLD_EXAMPLE],
|
112
|
+
)
|
113
|
+
|
114
|
+
|
115
|
+
@api_v1.get("/healthz", tags=["Observability"])
|
116
|
+
@api_v1.get("/health", tags=["Observability"])
|
117
|
+
@api_v2.get("/healthz", tags=["Observability"])
|
118
|
+
@api_v2.get("/health", tags=["Observability"])
|
119
|
+
async def health(service: Annotated[Service, Depends(get_service)], response: Response) -> Health:
|
120
|
+
"""Check the health of the service.
|
121
|
+
|
122
|
+
This endpoint returns the health status of the service.
|
123
|
+
The health status can be either UP or DOWN.
|
124
|
+
If the service is healthy, the status will be UP.
|
125
|
+
If the service is unhealthy, the status will be DOWN and a reason will be provided.
|
126
|
+
The response will have a 200 OK status code if the service is healthy,
|
127
|
+
and a 500 Internal Server Error status code if the service is unhealthy.
|
128
|
+
|
129
|
+
Args:
|
130
|
+
service (Annotated[Service, Depends): _description_
|
131
|
+
response (Response): _description_
|
132
|
+
|
133
|
+
Returns:
|
134
|
+
Health: The health status of the service.
|
135
|
+
"""
|
136
|
+
if service.healthy():
|
137
|
+
health_result = Health(status=_HealthStatus.UP)
|
138
|
+
else:
|
139
|
+
health_result = Health(status=_HealthStatus.DOWN, reason="Service is unhealthy")
|
140
|
+
|
141
|
+
if health_result.status == _HealthStatus.DOWN:
|
142
|
+
response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
143
|
+
|
144
|
+
return health_result
|
145
|
+
|
29
146
|
|
30
147
|
class HelloWorldResponse(BaseModel):
|
31
148
|
"""Response model for hello-world endpoint."""
|
@@ -37,6 +154,18 @@ class HelloWorldResponse(BaseModel):
|
|
37
154
|
)
|
38
155
|
|
39
156
|
|
157
|
+
@api_v1.get("/hello-world", tags=["Basics"])
|
158
|
+
@api_v2.get("/hello-world", tags=["Basics"])
|
159
|
+
async def hello_world() -> HelloWorldResponse:
|
160
|
+
"""
|
161
|
+
Return a hello world message.
|
162
|
+
|
163
|
+
Returns:
|
164
|
+
HelloWorldResponse: A response containing the hello world message.
|
165
|
+
"""
|
166
|
+
return HelloWorldResponse(message=Service.get_hello_world())
|
167
|
+
|
168
|
+
|
40
169
|
class EchoResponse(BaseModel):
|
41
170
|
"""Response model for echo endpoint."""
|
42
171
|
|
@@ -59,18 +188,7 @@ class EchoRequest(BaseModel):
|
|
59
188
|
)
|
60
189
|
|
61
190
|
|
62
|
-
@
|
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
|
-
@app.post("/echo", tags=["Basics"])
|
191
|
+
@api_v1.post("/echo", tags=["Basics"])
|
74
192
|
async def echo(request: EchoRequest) -> EchoResponse:
|
75
193
|
"""
|
76
194
|
Echo back the provided text.
|
@@ -85,3 +203,35 @@ async def echo(request: EchoRequest) -> EchoResponse:
|
|
85
203
|
422 Unprocessable Entity: If text is not provided or empty.
|
86
204
|
"""
|
87
205
|
return EchoResponse(message=request.text)
|
206
|
+
|
207
|
+
|
208
|
+
class Utterance(BaseModel):
|
209
|
+
"""Request model for echo endpoint."""
|
210
|
+
|
211
|
+
utterance: str = Field(
|
212
|
+
...,
|
213
|
+
min_length=1,
|
214
|
+
description="The utterance to echo back",
|
215
|
+
examples=[HELLO_WORLD_EXAMPLE],
|
216
|
+
)
|
217
|
+
|
218
|
+
|
219
|
+
@api_v2.post("/echo", tags=["Basics"])
|
220
|
+
async def echo_v2(request: Utterance) -> EchoResponse:
|
221
|
+
"""
|
222
|
+
Echo back the provided utterance.
|
223
|
+
|
224
|
+
Args:
|
225
|
+
request (EchoRequestV2): The request containing the utterance to echo back.
|
226
|
+
|
227
|
+
Returns:
|
228
|
+
EchoResponse: A response containing the echoed utterance.
|
229
|
+
|
230
|
+
Raises:
|
231
|
+
422 Unprocessable Entity: If utterance is not provided or empty.
|
232
|
+
"""
|
233
|
+
return EchoResponse(message=request.utterance)
|
234
|
+
|
235
|
+
|
236
|
+
api.mount("/v1", api_v1)
|
237
|
+
api.mount("/v2", api_v2)
|
@@ -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
|
@@ -8,7 +9,7 @@ import yaml
|
|
8
9
|
from rich.console import Console
|
9
10
|
|
10
11
|
from oe_python_template_example import Service, __version__
|
11
|
-
from oe_python_template_example.api import
|
12
|
+
from oe_python_template_example.api import api_v1, api_v2
|
12
13
|
|
13
14
|
console = Console()
|
14
15
|
|
@@ -44,30 +45,71 @@ 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}")
|
51
52
|
uvicorn.run(
|
52
|
-
"oe_python_template_example.api:
|
53
|
+
"oe_python_template_example.api:api",
|
53
54
|
host=host,
|
54
55
|
port=port,
|
55
|
-
reload=
|
56
|
+
reload=watch,
|
56
57
|
)
|
57
58
|
|
58
59
|
|
60
|
+
class APIVersion(StrEnum):
|
61
|
+
"""
|
62
|
+
Enum representing the API versions.
|
63
|
+
|
64
|
+
This enum defines the supported API verions:
|
65
|
+
- V1: Output doc for v1 API
|
66
|
+
- V2: Output doc for v2 API
|
67
|
+
|
68
|
+
Usage:
|
69
|
+
version = APIVersion.V1
|
70
|
+
print(f"Using {version} version")
|
71
|
+
|
72
|
+
"""
|
73
|
+
|
74
|
+
V1 = "v1"
|
75
|
+
V2 = "v2"
|
76
|
+
|
77
|
+
|
78
|
+
class OutputFormat(StrEnum):
|
79
|
+
"""
|
80
|
+
Enum representing the supported output formats.
|
81
|
+
|
82
|
+
This enum defines the possible formats for output data:
|
83
|
+
- YAML: Output data in YAML format
|
84
|
+
- JSON: Output data in JSON format
|
85
|
+
|
86
|
+
Usage:
|
87
|
+
format = OutputFormat.YAML
|
88
|
+
print(f"Using {format} format")
|
89
|
+
"""
|
90
|
+
|
91
|
+
YAML = "yaml"
|
92
|
+
JSON = "json"
|
93
|
+
|
94
|
+
|
59
95
|
@cli.command()
|
60
96
|
def openapi(
|
97
|
+
api_version: Annotated[APIVersion, typer.Option(help="API Version", case_sensitive=False)] = APIVersion.V1,
|
61
98
|
output_format: Annotated[
|
62
|
-
|
63
|
-
] =
|
99
|
+
OutputFormat, typer.Option(help="Output format", case_sensitive=False)
|
100
|
+
] = OutputFormat.YAML,
|
64
101
|
) -> None:
|
65
102
|
"""Dump the OpenAPI specification to stdout (YAML by default)."""
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
103
|
+
match api_version:
|
104
|
+
case APIVersion.V1:
|
105
|
+
schema = api_v1.openapi()
|
106
|
+
case APIVersion.V2:
|
107
|
+
schema = api_v2.openapi()
|
108
|
+
match output_format:
|
109
|
+
case OutputFormat.JSON:
|
110
|
+
console.print_json(data=schema)
|
111
|
+
case OutputFormat.YAML:
|
112
|
+
console.print(yaml.dump(schema, default_flow_style=False), end="")
|
71
113
|
|
72
114
|
|
73
115
|
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.9.dist-info → oe_python_template_example-0.1.0.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.1.0
|
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/
|
@@ -101,9 +101,9 @@ Use Cases:
|
|
101
101
|
2) Consistent update of already scaffolded projects to benefit from new and improved features.
|
102
102
|
3) Dummy CLI application and service demonstrating example usage of the generated directory structure and build pipeline
|
103
103
|
|
104
|
-
## Scaffolding
|
104
|
+
## Scaffolding
|
105
105
|
|
106
|
-
Step 1
|
106
|
+
**Step 1**: Install uv package manager and copier
|
107
107
|
```shell
|
108
108
|
if [[ "$OSTYPE" == "darwin"* ]]; then # Install dependencies for macOS X
|
109
109
|
if ! command -v brew &> /dev/null; then ## Install Homebrew if not present
|
@@ -119,31 +119,32 @@ fi
|
|
119
119
|
uv tool install copier # Install copier as global tool
|
120
120
|
```
|
121
121
|
|
122
|
-
Step 2
|
122
|
+
**Step 2**: Now create an empty repository on GitHubm, clone to your local machine, and change into it's directory.
|
123
123
|
|
124
|
-
Step 3
|
124
|
+
**Step 3**: Scaffold the project
|
125
125
|
```shell
|
126
126
|
copier copy gh:helmut-hoffer-von-ankershoffen/oe-python-template .
|
127
127
|
```
|
128
|
-
Step 4
|
128
|
+
**Step 4**: Setup the local environment
|
129
129
|
|
130
130
|
```shell
|
131
131
|
uv run nox -s setup_dev
|
132
132
|
```
|
133
133
|
|
134
|
-
Step 5
|
134
|
+
**Step 5**: Perform initial commit and push
|
135
135
|
```shell
|
136
136
|
git add .
|
137
137
|
git commit -m "feat: Initial commit"
|
138
|
+
git push
|
138
139
|
```
|
139
140
|
|
140
141
|
Visit your GitHub repository and check the Actions tab. The CI workflow should fail at the SonarQube step,
|
141
142
|
as this external service is not yet configured for our new repository.
|
142
143
|
|
143
|
-
Step 6
|
144
|
-
such as Cloudcov, SonarQube Cloud, Read The Docs, Docker.io, GHCR.io and Streamlit Community Cloud.
|
144
|
+
**Step 6**: Follow the [SERVICE_INSTRUCTIONS.md](instructions) to wire up
|
145
|
+
external services such as Cloudcov, SonarQube Cloud, Read The Docs, Docker.io, GHCR.io and Streamlit Community Cloud.
|
145
146
|
|
146
|
-
Step 7
|
147
|
+
**Step 7**: Release the first versions
|
147
148
|
```shell
|
148
149
|
./bump
|
149
150
|
```
|
@@ -166,25 +167,39 @@ If you don't have uv installed follow [these instructions](https://docs.astral.s
|
|
166
167
|
pip install oe-python-template-example # add dependency to your project
|
167
168
|
```
|
168
169
|
|
169
|
-
Executing the command line interface (CLI) is just as easy:
|
170
|
+
Executing the command line interface (CLI) in an isolated Python environment is just as easy:
|
170
171
|
|
171
172
|
```shell
|
172
|
-
uvx oe-python-template-example
|
173
|
+
uvx oe-python-template-example hello-world # prints "Hello, world! [..]"
|
174
|
+
uvx oe-python-template-example serve # serves webservice API
|
173
175
|
```
|
174
176
|
|
177
|
+
When serving the API, go to [http://127.0.0.1:8000/api/v1/hello-world](http://127.0.0.1:8000/api/v1/hello-world) to see the result.
|
178
|
+
|
179
|
+
The API is versioned and provides interactive documentation at [http://127.0.0.1:8000/api/v1/docs](http://127.0.0.1:8000/api/v1/docs) resp. [http://127.0.0.1:8000/api/v2/docs](http://127.0.0.1:8000/api/v2/docs)
|
180
|
+
|
181
|
+
|
182
|
+
```shell
|
183
|
+
|
184
|
+
When running the webservice API, goto http://127.0.0.1:8000/api/v1/docs
|
185
|
+
|
175
186
|
The CLI provides extensive help:
|
176
187
|
|
177
188
|
```shell
|
178
189
|
uvx oe-python-template-example --help # all CLI commands
|
179
190
|
uvx oe-python-template-example hello-world --help # help for specific command
|
191
|
+
uvx oe-python-template-example echo --help
|
192
|
+
uvx oe-python-template-example openapi --help
|
193
|
+
uvx oe-python-template-example serve --help
|
180
194
|
```
|
181
195
|
|
182
196
|
|
183
|
-
##
|
197
|
+
## Operational Excellence
|
184
198
|
|
185
|
-
|
186
|
-
|
187
|
-
|
199
|
+
This project is designed with operational excellence in mind, using modern Python tooling and practices. It includes:
|
200
|
+
|
201
|
+
* Various examples demonstrating usage:
|
202
|
+
- [Simple Python script](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template-example/blob/main/examples/script.py)
|
188
203
|
- [Streamlit web application](https://oe-python-template-example.streamlit.app/) deployed on [Streamlit Community Cloud](https://streamlit.io/cloud)
|
189
204
|
- [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
205
|
* [Complete reference documenation](https://oe-python-template-example.readthedocs.io/en/latest/reference.html) on Read the Docs
|
@@ -199,6 +214,9 @@ uvx oe-python-template-example hello-world --help # help for specific command
|
|
199
214
|
|
200
215
|
## Usage Examples
|
201
216
|
|
217
|
+
The following examples run from source. Clone this repository first using
|
218
|
+
`git clone git@github.com:helmut-hoffer-von-ankershoffen/oe-python-template-example.git`.
|
219
|
+
|
202
220
|
### Minimal Python Script:
|
203
221
|
|
204
222
|
```python
|
@@ -240,7 +258,7 @@ uv run streamlit run examples/streamlit.py # Serve on localhost:8501, o
|
|
240
258
|
... or run within VSCode
|
241
259
|
|
242
260
|
```shell
|
243
|
-
uv sync --all-extras # Install
|
261
|
+
uv sync --all-extras # Install dependencies required for examples such as Juypyter kernel, see pyproject.toml
|
244
262
|
```
|
245
263
|
Install the [Jupyter extension for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter)
|
246
264
|
|
@@ -284,8 +302,12 @@ Execute commands:
|
|
284
302
|
|
285
303
|
```shell
|
286
304
|
uvx oe-python-template-example hello-world
|
287
|
-
uvx oe-python-template-example
|
288
|
-
uvx oe-python-template-example echo "Lorem
|
305
|
+
uvx oe-python-template-example echo --help
|
306
|
+
uvx oe-python-template-example echo "Lorem"
|
307
|
+
uvx oe-python-template-example echo "Lorem" --json
|
308
|
+
uvx oe-python-template-example openapi
|
309
|
+
uvx oe-python-template-example openapi --output-format=json
|
310
|
+
uvx oe-python-template-example serve
|
289
311
|
```
|
290
312
|
|
291
313
|
### Environment
|
@@ -306,8 +328,12 @@ You can as well run the CLI within Docker.
|
|
306
328
|
```shell
|
307
329
|
docker run helmuthva/oe-python-template-example --help
|
308
330
|
docker run helmuthva/oe-python-template-example hello-world
|
309
|
-
docker run helmuthva/oe-python-template-example
|
331
|
+
docker run helmuthva/oe-python-template-example echo --help
|
310
332
|
docker run helmuthva/oe-python-template-example echo "Lorem"
|
333
|
+
docker run helmuthva/oe-python-template-example echo "Lorem" --json
|
334
|
+
docker run helmuthva/oe-python-template-example openapi
|
335
|
+
docker run helmuthva/oe-python-template-example openapi --output-format=json
|
336
|
+
docker run helmuthva/oe-python-template-example serve
|
311
337
|
```
|
312
338
|
|
313
339
|
Execute command:
|
@@ -321,8 +347,18 @@ Or use docker compose
|
|
321
347
|
The .env is passed through from the host to the Docker container.
|
322
348
|
|
323
349
|
```shell
|
324
|
-
docker compose up
|
325
350
|
docker compose run oe-python-template-example --help
|
351
|
+
docker compose run oe-python-template-example hello-world
|
352
|
+
docker compose run oe-python-template-example echo --help
|
353
|
+
docker compose run oe-python-template-example echo "Lorem"
|
354
|
+
docker compose run oe-python-template-example echo "Lorem" --json
|
355
|
+
docker compose run oe-python-template-example openapi
|
356
|
+
docker compose run oe-python-template-example openapi --output-format=json
|
357
|
+
docker compose up
|
358
|
+
curl http://127.0.0.1:8000/api/v1/hello-world
|
359
|
+
curl http://127.0.0.1:8000/api/v1/docs
|
360
|
+
curl http://127.0.0.1:8000/api/v2/hello-world
|
361
|
+
curl http://127.0.0.1:8000/api/v2/docs
|
326
362
|
```
|
327
363
|
|
328
364
|
## 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=Z5ZBrz7Ayjoi_h2UfD0Y_v3mlaHAkkXZYWonzB9L2hc,6356
|
3
|
+
oe_python_template_example/cli.py,sha256=PF4B8XygvY72obDFBu4idxNDntNvFbhSi-5Bl3vs4gU,3383
|
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.1.0.dist-info/METADATA,sha256=6tmPpUMuhnNOlQYfR1zHL0sACY0Hdw7EYR_5iyZ6ekw,21925
|
7
|
+
oe_python_template_example-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
8
|
+
oe_python_template_example-0.1.0.dist-info/entry_points.txt,sha256=S2eCPB45b1Wgj_GsDRFAN-e4h7dBA5UPxT8od98erDE,82
|
9
|
+
oe_python_template_example-0.1.0.dist-info/licenses/LICENSE,sha256=5H409K6xzz9U5eUaoAHQExNkoWJRlU0LEj6wL2QJ34s,1113
|
10
|
+
oe_python_template_example-0.1.0.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.9.dist-info/METADATA,sha256=XKogVxOI9bpkzWJfRstwyDquVsJRPfxZirfBVSq2p0k,20031
|
7
|
-
oe_python_template_example-0.0.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
8
|
-
oe_python_template_example-0.0.9.dist-info/entry_points.txt,sha256=S2eCPB45b1Wgj_GsDRFAN-e4h7dBA5UPxT8od98erDE,82
|
9
|
-
oe_python_template_example-0.0.9.dist-info/licenses/LICENSE,sha256=5H409K6xzz9U5eUaoAHQExNkoWJRlU0LEj6wL2QJ34s,1113
|
10
|
-
oe_python_template_example-0.0.9.dist-info/RECORD,,
|
{oe_python_template_example-0.0.9.dist-info → oe_python_template_example-0.1.0.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|
File without changes
|