oe-python-template 0.7.4__tar.gz → 0.7.6__tar.gz
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-0.7.4 → oe_python_template-0.7.6}/PKG-INFO +2 -2
- {oe_python_template-0.7.4 → oe_python_template-0.7.6}/pyproject.toml +9 -10
- {oe_python_template-0.7.4 → oe_python_template-0.7.6}/src/oe_python_template/api.py +3 -3
- {oe_python_template-0.7.4 → oe_python_template-0.7.6}/src/oe_python_template/cli.py +22 -10
- {oe_python_template-0.7.4 → oe_python_template-0.7.6}/src/oe_python_template/service.py +27 -11
- oe_python_template-0.7.6/src/oe_python_template/settings.py +35 -0
- {oe_python_template-0.7.4 → oe_python_template-0.7.6}/.gitignore +0 -0
- {oe_python_template-0.7.4 → oe_python_template-0.7.6}/LICENSE +0 -0
- {oe_python_template-0.7.4 → oe_python_template-0.7.6}/README.md +0 -0
- {oe_python_template-0.7.4 → oe_python_template-0.7.6}/src/oe_python_template/__init__.py +0 -0
- {oe_python_template-0.7.4 → oe_python_template-0.7.6}/src/oe_python_template/constants.py +0 -0
- {oe_python_template-0.7.4 → oe_python_template-0.7.6}/src/oe_python_template/models.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oe-python-template
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.6
|
|
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/
|
|
@@ -49,8 +49,8 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
49
49
|
Classifier: Typing :: Typed
|
|
50
50
|
Requires-Python: <4.0,>=3.11
|
|
51
51
|
Requires-Dist: fastapi[all,standard]>=0.115.12
|
|
52
|
+
Requires-Dist: pydantic-settings>=2.8.1
|
|
52
53
|
Requires-Dist: pydantic>=2.10.6
|
|
53
|
-
Requires-Dist: python-dotenv>=1.1.0
|
|
54
54
|
Requires-Dist: typer>=0.15.1
|
|
55
55
|
Provides-Extra: examples
|
|
56
56
|
Requires-Dist: jinja2>=3.1.6; extra == 'examples'
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "oe-python-template"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.6"
|
|
4
4
|
description = "🧠 Copier template to scaffold Python projects compliant with best practices and modern tooling."
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
authors = [
|
|
7
|
-
{ name = "Helmut Hoffer von Ankershoffen", email = "helmuthva@gmail.com" },
|
|
8
|
-
]
|
|
6
|
+
authors = [{ name = "Helmut Hoffer von Ankershoffen", email = "helmuthva@gmail.com" }]
|
|
9
7
|
license = { file = "LICENSE" }
|
|
10
8
|
|
|
11
9
|
keywords = [
|
|
@@ -64,7 +62,7 @@ requires-python = ">=3.11, <4.0"
|
|
|
64
62
|
dependencies = [
|
|
65
63
|
"fastapi[standard,all]>=0.115.12",
|
|
66
64
|
"pydantic>=2.10.6",
|
|
67
|
-
"
|
|
65
|
+
"pydantic-settings>=2.8.1",
|
|
68
66
|
"typer>=0.15.1",
|
|
69
67
|
]
|
|
70
68
|
|
|
@@ -118,6 +116,7 @@ dev = [
|
|
|
118
116
|
"pytest-docker>=3.2.0",
|
|
119
117
|
"pytest-env>=1.1.5",
|
|
120
118
|
"pytest-regressions>=2.7.0",
|
|
119
|
+
"pytest-subprocess>=1.5.3",
|
|
121
120
|
"pytest-xdist[psutil]>=3.6.1",
|
|
122
121
|
"ruff>=0.10.0",
|
|
123
122
|
"sphinx>=8.2.3",
|
|
@@ -137,7 +136,7 @@ dev = [
|
|
|
137
136
|
|
|
138
137
|
[tool.uv]
|
|
139
138
|
override-dependencies = [ # https://github.com/astral-sh/uv/issues/4422
|
|
140
|
-
"rfc3987; sys_platform == 'never'",
|
|
139
|
+
"rfc3987; sys_platform == 'never'", # GPLv3
|
|
141
140
|
]
|
|
142
141
|
|
|
143
142
|
|
|
@@ -199,7 +198,7 @@ ignore = [
|
|
|
199
198
|
[tool.ruff.lint.extend-per-file-ignores]
|
|
200
199
|
"examples/notebook.py" = [
|
|
201
200
|
# we are more relaxed in notebooks, while sill applying hundreds of rules
|
|
202
|
-
"B018",
|
|
201
|
+
"B018", # notebooks surface variable without print
|
|
203
202
|
]
|
|
204
203
|
|
|
205
204
|
[tool.ruff.format]
|
|
@@ -208,7 +207,7 @@ docstring-code-format = true
|
|
|
208
207
|
[tool.ruff.lint.pydocstyle]
|
|
209
208
|
convention = "google"
|
|
210
209
|
|
|
211
|
-
[tool.mypy]
|
|
210
|
+
[tool.mypy] # https://mypy.readthedocs.io/en/latest/config_file.html
|
|
212
211
|
junit_xml = "reports/mypy_junit.xml"
|
|
213
212
|
plugins = "pydantic.mypy"
|
|
214
213
|
strict = true
|
|
@@ -221,7 +220,7 @@ show_error_codes = true
|
|
|
221
220
|
show_error_context = true
|
|
222
221
|
warn_unreachable = true
|
|
223
222
|
|
|
224
|
-
[tool.pydantic-mypy]
|
|
223
|
+
[tool.pydantic-mypy] # https://docs.pydantic.dev/latest/integrations/mypy/#configuring-the-plugin
|
|
225
224
|
init_forbid_extra = true
|
|
226
225
|
init_typed = true
|
|
227
226
|
warn_required_dynamic_aliases = true
|
|
@@ -253,7 +252,7 @@ source = ["src/"]
|
|
|
253
252
|
|
|
254
253
|
|
|
255
254
|
[tool.bumpversion]
|
|
256
|
-
current_version = "0.7.
|
|
255
|
+
current_version = "0.7.6"
|
|
257
256
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
258
257
|
serialize = ["{major}.{minor}.{patch}"]
|
|
259
258
|
search = "{current_version}"
|
|
@@ -15,7 +15,7 @@ from typing import Annotated
|
|
|
15
15
|
from fastapi import Depends, FastAPI, Response, status
|
|
16
16
|
from pydantic import BaseModel, Field
|
|
17
17
|
|
|
18
|
-
from
|
|
18
|
+
from . import Echo, Health, HealthStatus, Service, Utterance
|
|
19
19
|
|
|
20
20
|
TITLE = "OE Python Template"
|
|
21
21
|
HELLO_WORLD_EXAMPLE = "Hello, world!"
|
|
@@ -133,14 +133,14 @@ class _HelloWorldResponse(BaseModel):
|
|
|
133
133
|
|
|
134
134
|
@api_v1.get("/hello-world", tags=["Basics"])
|
|
135
135
|
@api_v2.get("/hello-world", tags=["Basics"])
|
|
136
|
-
async def hello_world() -> _HelloWorldResponse:
|
|
136
|
+
async def hello_world(service: Annotated[Service, Depends(get_service)]) -> _HelloWorldResponse:
|
|
137
137
|
"""
|
|
138
138
|
Return a hello world message.
|
|
139
139
|
|
|
140
140
|
Returns:
|
|
141
141
|
_HelloWorldResponse: A response containing the hello world message.
|
|
142
142
|
"""
|
|
143
|
-
return _HelloWorldResponse(message=
|
|
143
|
+
return _HelloWorldResponse(message=service.get_hello_world())
|
|
144
144
|
|
|
145
145
|
|
|
146
146
|
@api_v1.get("/echo/{text}", tags=["Basics"])
|
|
@@ -9,12 +9,24 @@ import uvicorn
|
|
|
9
9
|
import yaml
|
|
10
10
|
from rich.console import Console
|
|
11
11
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
14
|
-
|
|
15
|
-
console = Console()
|
|
12
|
+
from . import Service, Utterance, __version__
|
|
13
|
+
from .api import api_v1, api_v2
|
|
16
14
|
|
|
17
15
|
cli = typer.Typer(name="Command Line Interface of OE Python Template")
|
|
16
|
+
_service = Service()
|
|
17
|
+
_console = Console()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@cli.command()
|
|
21
|
+
def health() -> None:
|
|
22
|
+
"""Indicate if service is healthy."""
|
|
23
|
+
_console.print(_service.healthy())
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@cli.command()
|
|
27
|
+
def info() -> None:
|
|
28
|
+
"""Print info about service configuration."""
|
|
29
|
+
_console.print(_service.info())
|
|
18
30
|
|
|
19
31
|
|
|
20
32
|
@cli.command()
|
|
@@ -32,15 +44,15 @@ def echo(
|
|
|
32
44
|
"""Echo the text."""
|
|
33
45
|
echo = Service.echo(Utterance(text=text))
|
|
34
46
|
if json:
|
|
35
|
-
|
|
47
|
+
_console.print_json(data={"text": echo.text})
|
|
36
48
|
else:
|
|
37
|
-
|
|
49
|
+
_console.print(echo.text)
|
|
38
50
|
|
|
39
51
|
|
|
40
52
|
@cli.command()
|
|
41
53
|
def hello_world() -> None:
|
|
42
54
|
"""Print hello world message and what's in the environment variable THE_VAR."""
|
|
43
|
-
|
|
55
|
+
_console.print(_service.get_hello_world())
|
|
44
56
|
|
|
45
57
|
|
|
46
58
|
@cli.command()
|
|
@@ -50,7 +62,7 @@ def serve(
|
|
|
50
62
|
watch: Annotated[bool, typer.Option(help="Enable auto-reload")] = True,
|
|
51
63
|
) -> None:
|
|
52
64
|
"""Start the API server."""
|
|
53
|
-
|
|
65
|
+
_console.print(f"Starting API server at http://{host}:{port}")
|
|
54
66
|
os.environ["UVICORN_HOST"] = host
|
|
55
67
|
os.environ["UVICORN_PORT"] = str(port)
|
|
56
68
|
uvicorn.run(
|
|
@@ -111,9 +123,9 @@ def openapi(
|
|
|
111
123
|
schema = api_v2.openapi()
|
|
112
124
|
match output_format:
|
|
113
125
|
case OutputFormat.JSON:
|
|
114
|
-
|
|
126
|
+
_console.print_json(data=schema)
|
|
115
127
|
case OutputFormat.YAML:
|
|
116
|
-
|
|
128
|
+
_console.print(yaml.dump(schema, default_flow_style=False), end="")
|
|
117
129
|
|
|
118
130
|
|
|
119
131
|
def _apply_cli_settings(cli: typer.Typer, epilog: str) -> None:
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
"""Service of OE Python Template
|
|
1
|
+
"""Service of OE Python Template."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
5
|
from dotenv import load_dotenv
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from .models import Echo, Utterance
|
|
8
|
+
from .settings import Language, Settings
|
|
8
9
|
|
|
9
10
|
load_dotenv()
|
|
10
11
|
THE_VAR = os.getenv("THE_VAR", "not defined")
|
|
@@ -13,28 +14,43 @@ THE_VAR = os.getenv("THE_VAR", "not defined")
|
|
|
13
14
|
class Service:
|
|
14
15
|
"""Service of OE Python Template."""
|
|
15
16
|
|
|
17
|
+
_settings: Settings
|
|
18
|
+
|
|
16
19
|
def __init__(self) -> None:
|
|
17
20
|
"""Initialize service."""
|
|
21
|
+
self._settings = Settings() # pyright: ignore[reportCallIssue] - false positive
|
|
18
22
|
self.is_healthy = True
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
def get_hello_world() -> str:
|
|
24
|
+
def healthy(self) -> bool:
|
|
22
25
|
"""
|
|
23
|
-
|
|
26
|
+
Check if the service is healthy.
|
|
24
27
|
|
|
25
28
|
Returns:
|
|
26
|
-
|
|
29
|
+
bool: True if the service is healthy, False otherwise.
|
|
27
30
|
"""
|
|
28
|
-
return
|
|
31
|
+
return self.is_healthy
|
|
29
32
|
|
|
30
|
-
def
|
|
33
|
+
def info(self) -> str:
|
|
31
34
|
"""
|
|
32
|
-
|
|
35
|
+
Get info about configuration of service.
|
|
33
36
|
|
|
34
37
|
Returns:
|
|
35
|
-
|
|
38
|
+
str: Service configuration.
|
|
36
39
|
"""
|
|
37
|
-
return self.
|
|
40
|
+
return self._settings.model_dump_json()
|
|
41
|
+
|
|
42
|
+
def get_hello_world(self) -> str:
|
|
43
|
+
"""
|
|
44
|
+
Get a hello world message.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
str: Hello world message.
|
|
48
|
+
"""
|
|
49
|
+
match self._settings.language:
|
|
50
|
+
case Language.GERMAN:
|
|
51
|
+
return "Hallo, Welt!"
|
|
52
|
+
case _:
|
|
53
|
+
return "Hello, world!"
|
|
38
54
|
|
|
39
55
|
@staticmethod
|
|
40
56
|
def echo(utterance: Utterance) -> Echo:
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Settings of OE Python Template."""
|
|
2
|
+
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
8
|
+
|
|
9
|
+
from . import __project_name__
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Language(StrEnum):
|
|
13
|
+
"""Supported languages."""
|
|
14
|
+
|
|
15
|
+
GERMAN = "de_DE"
|
|
16
|
+
US_ENGLISH = "en_US"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Settings(BaseSettings):
|
|
20
|
+
"""Settings."""
|
|
21
|
+
|
|
22
|
+
model_config = SettingsConfigDict(
|
|
23
|
+
env_prefix=f"{__project_name__.upper()}_",
|
|
24
|
+
extra="ignore",
|
|
25
|
+
env_file=".env",
|
|
26
|
+
env_file_encoding="utf-8",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
language: Annotated[
|
|
30
|
+
Language,
|
|
31
|
+
Field(
|
|
32
|
+
Language.US_ENGLISH,
|
|
33
|
+
description="Language to use for output - defaults to US english.",
|
|
34
|
+
),
|
|
35
|
+
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|