oe-python-template-example 0.3.5__py3-none-any.whl → 0.3.6__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/__init__.py +4 -18
- oe_python_template_example/api.py +42 -148
- oe_python_template_example/cli.py +13 -141
- oe_python_template_example/constants.py +6 -9
- oe_python_template_example/hello/__init__.py +17 -0
- oe_python_template_example/hello/_api.py +94 -0
- oe_python_template_example/hello/_cli.py +47 -0
- oe_python_template_example/hello/_constants.py +4 -0
- oe_python_template_example/hello/_models.py +28 -0
- oe_python_template_example/hello/_service.py +96 -0
- oe_python_template_example/{settings.py → hello/_settings.py} +6 -4
- oe_python_template_example/system/__init__.py +19 -0
- oe_python_template_example/system/_api.py +116 -0
- oe_python_template_example/system/_cli.py +165 -0
- oe_python_template_example/system/_service.py +182 -0
- oe_python_template_example/system/_settings.py +31 -0
- oe_python_template_example/utils/__init__.py +59 -0
- oe_python_template_example/utils/_api.py +18 -0
- oe_python_template_example/utils/_cli.py +68 -0
- oe_python_template_example/utils/_console.py +14 -0
- oe_python_template_example/utils/_constants.py +48 -0
- oe_python_template_example/utils/_di.py +70 -0
- oe_python_template_example/utils/_health.py +107 -0
- oe_python_template_example/utils/_log.py +122 -0
- oe_python_template_example/utils/_logfire.py +68 -0
- oe_python_template_example/utils/_process.py +41 -0
- oe_python_template_example/utils/_sentry.py +97 -0
- oe_python_template_example/utils/_service.py +39 -0
- oe_python_template_example/utils/_settings.py +80 -0
- oe_python_template_example/utils/boot.py +86 -0
- {oe_python_template_example-0.3.5.dist-info → oe_python_template_example-0.3.6.dist-info}/METADATA +77 -51
- oe_python_template_example-0.3.6.dist-info/RECORD +35 -0
- oe_python_template_example/models.py +0 -44
- oe_python_template_example/service.py +0 -68
- oe_python_template_example-0.3.5.dist-info/RECORD +0 -12
- {oe_python_template_example-0.3.5.dist-info → oe_python_template_example-0.3.6.dist-info}/WHEEL +0 -0
- {oe_python_template_example-0.3.5.dist-info → oe_python_template_example-0.3.6.dist-info}/entry_points.txt +0 -0
- {oe_python_template_example-0.3.5.dist-info → oe_python_template_example-0.3.6.dist-info}/licenses/LICENSE +0 -0
@@ -1,20 +1,6 @@
|
|
1
|
-
"""
|
1
|
+
"""Copier template to scaffold Python projects compliant with best practices and modern tooling."""
|
2
2
|
|
3
|
-
from .constants import
|
4
|
-
|
5
|
-
__project_path__,
|
6
|
-
__version__,
|
7
|
-
)
|
8
|
-
from .models import Echo, Health, HealthStatus, Utterance
|
9
|
-
from .service import Service
|
3
|
+
from .constants import MODULES_TO_INSTRUMENT
|
4
|
+
from .utils.boot import boot
|
10
5
|
|
11
|
-
|
12
|
-
"Echo",
|
13
|
-
"Health",
|
14
|
-
"HealthStatus",
|
15
|
-
"Service",
|
16
|
-
"Utterance",
|
17
|
-
"__project_name__",
|
18
|
-
"__project_path__",
|
19
|
-
"__version__",
|
20
|
-
]
|
6
|
+
boot(modules_to_instrument=MODULES_TO_INSTRUMENT)
|
@@ -1,44 +1,30 @@
|
|
1
1
|
"""Webservice API of OE Python Template Example.
|
2
2
|
|
3
|
-
|
4
|
-
-
|
5
|
-
- A hello-world endpoint that returns a greeting message
|
6
|
-
- An echo endpoint that echoes back the provided text
|
7
|
-
|
8
|
-
The endpoints use Pydantic models for request and response validation.
|
3
|
+
- Provides a versioned API
|
4
|
+
- Automatically registers APIs of modules and mounts them to the main API.
|
9
5
|
"""
|
10
6
|
|
11
7
|
import os
|
12
|
-
from collections.abc import Generator
|
13
|
-
from typing import Annotated
|
14
8
|
|
15
|
-
from fastapi import
|
16
|
-
from pydantic import BaseModel, Field
|
9
|
+
from fastapi import FastAPI
|
17
10
|
|
18
|
-
from . import
|
11
|
+
from .constants import API_VERSIONS
|
12
|
+
from .utils import (
|
13
|
+
VersionedAPIRouter,
|
14
|
+
__author_email__,
|
15
|
+
__author_name__,
|
16
|
+
__documentation__url__,
|
17
|
+
__repository_url__,
|
18
|
+
locate_implementations,
|
19
|
+
)
|
19
20
|
|
20
21
|
TITLE = "OE Python Template Example"
|
21
|
-
HELLO_WORLD_EXAMPLE = "Hello, world!"
|
22
22
|
UVICORN_HOST = os.environ.get("UVICORN_HOST", "127.0.0.1")
|
23
23
|
UVICORN_PORT = os.environ.get("UVICORN_PORT", "8000")
|
24
|
-
CONTACT_NAME =
|
25
|
-
CONTACT_EMAIL =
|
26
|
-
CONTACT_URL =
|
27
|
-
TERMS_OF_SERVICE_URL =
|
28
|
-
|
29
|
-
|
30
|
-
def get_service() -> Generator[Service, None, None]:
|
31
|
-
"""Get the service instance.
|
32
|
-
|
33
|
-
Yields:
|
34
|
-
Service: The service instance.
|
35
|
-
"""
|
36
|
-
service = Service()
|
37
|
-
try:
|
38
|
-
yield service
|
39
|
-
finally:
|
40
|
-
# Cleanup code if needed
|
41
|
-
pass
|
24
|
+
CONTACT_NAME = __author_name__
|
25
|
+
CONTACT_EMAIL = __author_email__
|
26
|
+
CONTACT_URL = __repository_url__
|
27
|
+
TERMS_OF_SERVICE_URL = __documentation__url__
|
42
28
|
|
43
29
|
|
44
30
|
app = FastAPI(
|
@@ -52,130 +38,38 @@ app = FastAPI(
|
|
52
38
|
terms_of_service=TERMS_OF_SERVICE_URL,
|
53
39
|
openapi_tags=[
|
54
40
|
{
|
55
|
-
"name":
|
56
|
-
"description": "API version
|
41
|
+
"name": version,
|
42
|
+
"description": f"API version {version.lstrip('v')}, check link on the right",
|
57
43
|
"externalDocs": {
|
58
44
|
"description": "sub-docs",
|
59
|
-
"url": f"http://{UVICORN_HOST}:{UVICORN_PORT}/api/
|
45
|
+
"url": f"http://{UVICORN_HOST}:{UVICORN_PORT}/api/{version}/docs",
|
60
46
|
},
|
61
|
-
}
|
62
|
-
|
63
|
-
"name": "v2",
|
64
|
-
"description": "API version 2, check link on the right",
|
65
|
-
"externalDocs": {
|
66
|
-
"description": "sub-docs",
|
67
|
-
"url": f"http://{UVICORN_HOST}:{UVICORN_PORT}/api/v2/docs",
|
68
|
-
},
|
69
|
-
},
|
47
|
+
}
|
48
|
+
for version, _ in API_VERSIONS.items()
|
70
49
|
],
|
71
50
|
)
|
72
51
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
version="2.0.0",
|
86
|
-
title=TITLE,
|
87
|
-
contact={
|
88
|
-
"name": CONTACT_NAME,
|
89
|
-
"email": CONTACT_EMAIL,
|
90
|
-
"url": CONTACT_URL,
|
91
|
-
},
|
92
|
-
terms_of_service=TERMS_OF_SERVICE_URL,
|
93
|
-
)
|
94
|
-
|
95
|
-
|
96
|
-
@api_v1.get("/healthz", tags=["Observability"])
|
97
|
-
@api_v1.get("/health", tags=["Observability"])
|
98
|
-
@api_v2.get("/healthz", tags=["Observability"])
|
99
|
-
@api_v2.get("/health", tags=["Observability"])
|
100
|
-
async def health(service: Annotated[Service, Depends(get_service)], response: Response) -> Health:
|
101
|
-
"""Check the health of the service.
|
102
|
-
|
103
|
-
This endpoint returns the health status of the service.
|
104
|
-
The health status can be either UP or DOWN.
|
105
|
-
If the service is healthy, the status will be UP.
|
106
|
-
If the service is unhealthy, the status will be DOWN and a reason will be provided.
|
107
|
-
The response will have a 200 OK status code if the service is healthy,
|
108
|
-
and a 500 Internal Server Error status code if the service is unhealthy.
|
109
|
-
|
110
|
-
Returns:
|
111
|
-
Health: The health status of the service.
|
112
|
-
"""
|
113
|
-
if service.healthy():
|
114
|
-
health_result = Health(status=HealthStatus.UP)
|
115
|
-
else:
|
116
|
-
health_result = Health(status=HealthStatus.DOWN, reason="Service is unhealthy")
|
117
|
-
|
118
|
-
if health_result.status == HealthStatus.DOWN:
|
119
|
-
response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
120
|
-
|
121
|
-
return health_result
|
122
|
-
|
123
|
-
|
124
|
-
class _HelloWorldResponse(BaseModel):
|
125
|
-
"""Response model for hello-world endpoint."""
|
126
|
-
|
127
|
-
message: str = Field(
|
128
|
-
...,
|
129
|
-
description="The hello world message",
|
130
|
-
examples=[HELLO_WORLD_EXAMPLE],
|
52
|
+
# Create API instances for each version
|
53
|
+
api_instances: dict["str", FastAPI] = {}
|
54
|
+
for version, semver in API_VERSIONS.items():
|
55
|
+
api_instances[version] = FastAPI(
|
56
|
+
version=semver,
|
57
|
+
title=TITLE,
|
58
|
+
contact={
|
59
|
+
"name": CONTACT_NAME,
|
60
|
+
"email": CONTACT_EMAIL,
|
61
|
+
"url": CONTACT_URL,
|
62
|
+
},
|
63
|
+
terms_of_service=TERMS_OF_SERVICE_URL,
|
131
64
|
)
|
132
65
|
|
66
|
+
# Register routers with appropriate API versions
|
67
|
+
for router in locate_implementations(VersionedAPIRouter):
|
68
|
+
router_instance: VersionedAPIRouter = router
|
69
|
+
version = router_instance.version
|
70
|
+
if version in API_VERSIONS:
|
71
|
+
api_instances[version].include_router(router_instance)
|
133
72
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
"""
|
138
|
-
Return a hello world message.
|
139
|
-
|
140
|
-
Returns:
|
141
|
-
_HelloWorldResponse: A response containing the hello world message.
|
142
|
-
"""
|
143
|
-
return _HelloWorldResponse(message=service.get_hello_world())
|
144
|
-
|
145
|
-
|
146
|
-
@api_v1.get("/echo/{text}", tags=["Basics"])
|
147
|
-
async def echo(text: str) -> Echo:
|
148
|
-
"""
|
149
|
-
Echo back the provided text.
|
150
|
-
|
151
|
-
Args:
|
152
|
-
text (str): The text to echo.
|
153
|
-
|
154
|
-
Returns:
|
155
|
-
Echo: The echo.
|
156
|
-
|
157
|
-
Raises:
|
158
|
-
422 Unprocessable Entity: If text is not provided or empty.
|
159
|
-
"""
|
160
|
-
return Service.echo(Utterance(text=text))
|
161
|
-
|
162
|
-
|
163
|
-
@api_v2.post("/echo", tags=["Basics"])
|
164
|
-
async def echo_v2(request: Utterance) -> Echo:
|
165
|
-
"""
|
166
|
-
Echo back the provided utterance.
|
167
|
-
|
168
|
-
Args:
|
169
|
-
request (Utterance): The utterance to echo back.
|
170
|
-
|
171
|
-
Returns:
|
172
|
-
Echo: The echo.
|
173
|
-
|
174
|
-
Raises:
|
175
|
-
422 Unprocessable Entity: If utterance is not provided or empty.
|
176
|
-
"""
|
177
|
-
return Service.echo(request)
|
178
|
-
|
179
|
-
|
180
|
-
app.mount("/v1", api_v1)
|
181
|
-
app.mount("/v2", api_v2)
|
73
|
+
# Mount all API versions to the main app
|
74
|
+
for version in API_VERSIONS:
|
75
|
+
app.mount(f"/{version}", api_instances[version])
|
@@ -1,150 +1,22 @@
|
|
1
1
|
"""CLI (Command Line Interface) of OE Python Template Example."""
|
2
2
|
|
3
|
-
import os
|
4
3
|
import sys
|
5
|
-
from enum import StrEnum
|
6
|
-
from pathlib import Path
|
7
|
-
from typing import Annotated
|
8
4
|
|
9
5
|
import typer
|
10
|
-
import uvicorn
|
11
|
-
import yaml
|
12
|
-
from rich.console import Console
|
13
6
|
|
14
|
-
from . import
|
15
|
-
from .
|
7
|
+
from .constants import MODULES_TO_INSTRUMENT
|
8
|
+
from .utils import __version__, boot, console, get_logger, prepare_cli
|
16
9
|
|
17
|
-
|
18
|
-
|
19
|
-
_console = Console()
|
10
|
+
boot(MODULES_TO_INSTRUMENT)
|
11
|
+
logger = get_logger(__name__)
|
20
12
|
|
13
|
+
cli = typer.Typer(help="Command Line Interface of ")
|
14
|
+
prepare_cli(cli, f"🧠 OE Python Template Example v{__version__} - built with love in Berlin 🐻")
|
21
15
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
def info() -> None:
|
30
|
-
"""Print info about service configuration."""
|
31
|
-
_console.print(_service.info())
|
32
|
-
|
33
|
-
|
34
|
-
@cli.command()
|
35
|
-
def echo(
|
36
|
-
text: Annotated[
|
37
|
-
str, typer.Argument(help="The text to echo")
|
38
|
-
] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
39
|
-
json: Annotated[
|
40
|
-
bool,
|
41
|
-
typer.Option(
|
42
|
-
help=("Print as JSON"),
|
43
|
-
),
|
44
|
-
] = False,
|
45
|
-
) -> None:
|
46
|
-
"""Echo the text."""
|
47
|
-
echo = Service.echo(Utterance(text=text))
|
48
|
-
if json:
|
49
|
-
_console.print_json(data={"text": echo.text})
|
50
|
-
else:
|
51
|
-
_console.print(echo.text)
|
52
|
-
|
53
|
-
|
54
|
-
@cli.command()
|
55
|
-
def hello_world() -> None:
|
56
|
-
"""Print hello world message and what's in the environment variable THE_VAR."""
|
57
|
-
_console.print(_service.get_hello_world())
|
58
|
-
|
59
|
-
|
60
|
-
@cli.command()
|
61
|
-
def serve(
|
62
|
-
host: Annotated[str, typer.Option(help="Host to bind the server to")] = "127.0.0.1",
|
63
|
-
port: Annotated[int, typer.Option(help="Port to bind the server to")] = 8000,
|
64
|
-
watch: Annotated[bool, typer.Option(help="Enable auto-reload")] = True,
|
65
|
-
) -> None:
|
66
|
-
"""Start the API server."""
|
67
|
-
_console.print(f"Starting API server at http://{host}:{port}")
|
68
|
-
os.environ["UVICORN_HOST"] = host
|
69
|
-
os.environ["UVICORN_PORT"] = str(port)
|
70
|
-
uvicorn.run(
|
71
|
-
"oe_python_template_example.api:app",
|
72
|
-
host=host,
|
73
|
-
port=port,
|
74
|
-
reload=watch,
|
75
|
-
)
|
76
|
-
|
77
|
-
|
78
|
-
class APIVersion(StrEnum):
|
79
|
-
"""
|
80
|
-
Enum representing the API versions.
|
81
|
-
|
82
|
-
This enum defines the supported API verions:
|
83
|
-
- V1: Output doc for v1 API
|
84
|
-
- V2: Output doc for v2 API
|
85
|
-
|
86
|
-
Usage:
|
87
|
-
version = APIVersion.V1
|
88
|
-
print(f"Using {version} version")
|
89
|
-
|
90
|
-
"""
|
91
|
-
|
92
|
-
V1 = "v1"
|
93
|
-
V2 = "v2"
|
94
|
-
|
95
|
-
|
96
|
-
class OutputFormat(StrEnum):
|
97
|
-
"""
|
98
|
-
Enum representing the supported output formats.
|
99
|
-
|
100
|
-
This enum defines the possible formats for output data:
|
101
|
-
- YAML: Output data in YAML format
|
102
|
-
- JSON: Output data in JSON format
|
103
|
-
|
104
|
-
Usage:
|
105
|
-
format = OutputFormat.YAML
|
106
|
-
print(f"Using {format} format")
|
107
|
-
"""
|
108
|
-
|
109
|
-
YAML = "yaml"
|
110
|
-
JSON = "json"
|
111
|
-
|
112
|
-
|
113
|
-
@cli.command()
|
114
|
-
def openapi(
|
115
|
-
api_version: Annotated[APIVersion, typer.Option(help="API Version", case_sensitive=False)] = APIVersion.V1,
|
116
|
-
output_format: Annotated[
|
117
|
-
OutputFormat, typer.Option(help="Output format", case_sensitive=False)
|
118
|
-
] = OutputFormat.YAML,
|
119
|
-
) -> None:
|
120
|
-
"""Dump the OpenAPI specification to stdout (YAML by default)."""
|
121
|
-
match api_version:
|
122
|
-
case APIVersion.V1:
|
123
|
-
schema = api_v1.openapi()
|
124
|
-
case APIVersion.V2:
|
125
|
-
schema = api_v2.openapi()
|
126
|
-
match output_format:
|
127
|
-
case OutputFormat.JSON:
|
128
|
-
_console.print_json(data=schema)
|
129
|
-
case OutputFormat.YAML:
|
130
|
-
_console.print(yaml.dump(schema, width=80, default_flow_style=False), end="")
|
131
|
-
|
132
|
-
|
133
|
-
def _apply_cli_settings(cli: typer.Typer, epilog: str) -> None:
|
134
|
-
"""Configure default behavior and add epilog to all typers in the tree."""
|
135
|
-
cli.info.no_args_is_help = True
|
136
|
-
|
137
|
-
if not any(arg.endswith("typer") for arg in Path(sys.argv[0]).parts):
|
138
|
-
cli.info.epilog = epilog
|
139
|
-
for command in cli.registered_commands:
|
140
|
-
command.epilog = cli.info.epilog
|
141
|
-
|
142
|
-
|
143
|
-
_apply_cli_settings(
|
144
|
-
cli,
|
145
|
-
f"🧠 OE Python Template Example v{__version__} - built with love in Berlin 🐻",
|
146
|
-
)
|
147
|
-
|
148
|
-
|
149
|
-
if __name__ == "__main__":
|
150
|
-
cli()
|
16
|
+
if __name__ == "__main__": # pragma: no cover
|
17
|
+
try:
|
18
|
+
cli()
|
19
|
+
except Exception as e: # noqa: BLE001
|
20
|
+
logger.critical("Fatal error occurred: %s", e)
|
21
|
+
console.print(f"Fatal error occurred: {e}", style="error")
|
22
|
+
sys.exit(1)
|
@@ -1,10 +1,7 @@
|
|
1
|
-
"""Constants
|
1
|
+
"""Constants for the OE Python Template Example."""
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
__version__ = importlib.metadata.version(__project_name__)
|
9
|
-
|
10
|
-
LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
3
|
+
API_VERSIONS = {
|
4
|
+
"v1": "1.0.0",
|
5
|
+
"v2": "2.0.0",
|
6
|
+
}
|
7
|
+
MODULES_TO_INSTRUMENT = ["oe_python_template_example.hello"]
|
@@ -0,0 +1,17 @@
|
|
1
|
+
"""Hello module."""
|
2
|
+
|
3
|
+
from ._api import api_v1, api_v2
|
4
|
+
from ._cli import cli
|
5
|
+
from ._models import Echo, Utterance
|
6
|
+
from ._service import Service
|
7
|
+
from ._settings import Settings
|
8
|
+
|
9
|
+
__all__ = [
|
10
|
+
"Echo",
|
11
|
+
"Service",
|
12
|
+
"Settings",
|
13
|
+
"Utterance",
|
14
|
+
"api_v1",
|
15
|
+
"api_v2",
|
16
|
+
"cli",
|
17
|
+
]
|
@@ -0,0 +1,94 @@
|
|
1
|
+
"""Webservice API of Hello module.
|
2
|
+
|
3
|
+
This module provides a webservice API with several operations:
|
4
|
+
- A hello/world operation that returns a greeting message
|
5
|
+
- A hello/echo endpoint that echoes back the provided text
|
6
|
+
"""
|
7
|
+
|
8
|
+
from collections.abc import Generator
|
9
|
+
from typing import Annotated
|
10
|
+
|
11
|
+
from fastapi import Depends
|
12
|
+
from pydantic import BaseModel, Field
|
13
|
+
|
14
|
+
from oe_python_template_example.utils import VersionedAPIRouter
|
15
|
+
|
16
|
+
from ._models import Echo, Utterance
|
17
|
+
from ._service import Service
|
18
|
+
|
19
|
+
HELLO_WORLD_EXAMPLE = "Hello, world!"
|
20
|
+
|
21
|
+
# VersionedAPIRouters exported by modules via their __init__.py are automatically registered
|
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"])
|
25
|
+
|
26
|
+
|
27
|
+
def get_service() -> Generator[Service, None, None]:
|
28
|
+
"""Get instance of Service.
|
29
|
+
|
30
|
+
Yields:
|
31
|
+
Service: The service instance.
|
32
|
+
"""
|
33
|
+
service = Service()
|
34
|
+
try:
|
35
|
+
yield service
|
36
|
+
finally:
|
37
|
+
# Cleanup code if needed
|
38
|
+
pass
|
39
|
+
|
40
|
+
|
41
|
+
class _HelloWorldResponse(BaseModel):
|
42
|
+
"""Response model for hello-world endpoint."""
|
43
|
+
|
44
|
+
message: str = Field(
|
45
|
+
...,
|
46
|
+
description="The hello world message",
|
47
|
+
examples=[HELLO_WORLD_EXAMPLE],
|
48
|
+
)
|
49
|
+
|
50
|
+
|
51
|
+
@api_v1.get("/world")
|
52
|
+
@api_v2.get("/world")
|
53
|
+
def hello_world(service: Annotated[Service, Depends(get_service)]) -> _HelloWorldResponse:
|
54
|
+
"""
|
55
|
+
Return a hello world message.
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
_HelloWorldResponse: A response containing the hello world message.
|
59
|
+
"""
|
60
|
+
return _HelloWorldResponse(message=service.get_hello_world())
|
61
|
+
|
62
|
+
|
63
|
+
@api_v1.get("/echo/{text}")
|
64
|
+
def echo(text: str) -> Echo:
|
65
|
+
"""
|
66
|
+
Echo back the provided text.
|
67
|
+
|
68
|
+
Args:
|
69
|
+
text (str): The text to echo.
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
Echo: The echo.
|
73
|
+
|
74
|
+
Raises:
|
75
|
+
422 Unprocessable Entity: If text is not provided or empty.
|
76
|
+
"""
|
77
|
+
return Service.echo(Utterance(text=text))
|
78
|
+
|
79
|
+
|
80
|
+
@api_v2.post("/echo")
|
81
|
+
def echo_v2(request: Utterance) -> Echo:
|
82
|
+
"""
|
83
|
+
Echo back the provided utterance.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
request (Utterance): The utterance to echo back.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
Echo: The echo.
|
90
|
+
|
91
|
+
Raises:
|
92
|
+
422 Unprocessable Entity: If utterance is not provided or empty.
|
93
|
+
"""
|
94
|
+
return Service.echo(request)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
"""CLI (Command Line Interface) of OE Python Template Example."""
|
2
|
+
|
3
|
+
from typing import Annotated
|
4
|
+
|
5
|
+
import typer
|
6
|
+
|
7
|
+
from oe_python_template_example.utils import console, get_logger
|
8
|
+
|
9
|
+
from ._models import Utterance
|
10
|
+
from ._service import Service
|
11
|
+
|
12
|
+
logger = get_logger(__name__)
|
13
|
+
|
14
|
+
# CLI apps exported by modules via their __init__.py are automatically registered and injected into the main CLI app
|
15
|
+
cli = typer.Typer(name="hello", help="Hello commands")
|
16
|
+
_service = Service()
|
17
|
+
|
18
|
+
|
19
|
+
@cli.command()
|
20
|
+
def echo(
|
21
|
+
text: Annotated[
|
22
|
+
str, typer.Argument(help="The text to echo")
|
23
|
+
] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
24
|
+
json: Annotated[
|
25
|
+
bool,
|
26
|
+
typer.Option(
|
27
|
+
help=("Print as JSON"),
|
28
|
+
),
|
29
|
+
] = False,
|
30
|
+
) -> None:
|
31
|
+
"""Echo the text.
|
32
|
+
|
33
|
+
Args:
|
34
|
+
text (str): The text to echo.
|
35
|
+
json (bool): Print as JSON.
|
36
|
+
"""
|
37
|
+
echo = Service.echo(Utterance(text=text))
|
38
|
+
if json:
|
39
|
+
console.print_json(data={"text": echo.text})
|
40
|
+
else:
|
41
|
+
console.print(echo.text)
|
42
|
+
|
43
|
+
|
44
|
+
@cli.command()
|
45
|
+
def world() -> None:
|
46
|
+
"""Print hello world message and what's in the environment variable THE_VAR."""
|
47
|
+
console.print(_service.get_hello_world())
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"""Models of the hello module."""
|
2
|
+
|
3
|
+
from pydantic import BaseModel, Field
|
4
|
+
|
5
|
+
_UTTERANCE_EXAMPLE = "Hello, world!"
|
6
|
+
_ECHO_EXAMPLE = "HELLO, WORLD!"
|
7
|
+
|
8
|
+
|
9
|
+
class Utterance(BaseModel):
|
10
|
+
"""Model representing a text utterance."""
|
11
|
+
|
12
|
+
text: str = Field(
|
13
|
+
...,
|
14
|
+
min_length=1,
|
15
|
+
description="The utterance to echo back",
|
16
|
+
examples=[_UTTERANCE_EXAMPLE],
|
17
|
+
)
|
18
|
+
|
19
|
+
|
20
|
+
class Echo(BaseModel):
|
21
|
+
"""Response model for echo endpoint."""
|
22
|
+
|
23
|
+
text: str = Field(
|
24
|
+
...,
|
25
|
+
min_length=1,
|
26
|
+
description="The echo",
|
27
|
+
examples=[_ECHO_EXAMPLE],
|
28
|
+
)
|