oe-python-template 0.7.3__tar.gz → 0.7.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oe-python-template
3
- Version: 0.7.3
3
+ Version: 0.7.5
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/
@@ -48,14 +48,14 @@ Classifier: Programming Language :: Python :: 3.12
48
48
  Classifier: Programming Language :: Python :: 3.13
49
49
  Classifier: Typing :: Typed
50
50
  Requires-Python: <4.0,>=3.11
51
- Requires-Dist: fastapi[all,standard]>=0.115.11
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.0.1
54
54
  Requires-Dist: typer>=0.15.1
55
55
  Provides-Extra: examples
56
56
  Requires-Dist: jinja2>=3.1.6; extra == 'examples'
57
57
  Requires-Dist: jupyter>=1.1.1; extra == 'examples'
58
- Requires-Dist: marimo>=0.11.19; extra == 'examples'
58
+ Requires-Dist: marimo>=0.11.26; extra == 'examples'
59
59
  Requires-Dist: streamlit>=1.43.2; extra == 'examples'
60
60
  Description-Content-Type: text/markdown
61
61
 
@@ -1,11 +1,9 @@
1
1
  [project]
2
2
  name = "oe-python-template"
3
- version = "0.7.3"
3
+ version = "0.7.5"
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 = [
@@ -62,9 +60,9 @@ classifiers = [
62
60
  requires-python = ">=3.11, <4.0"
63
61
 
64
62
  dependencies = [
65
- "fastapi[standard,all]>=0.115.11",
63
+ "fastapi[standard,all]>=0.115.12",
66
64
  "pydantic>=2.10.6",
67
- "python-dotenv>=1.0.1",
65
+ "pydantic-settings>=2.8.1",
68
66
  "typer>=0.15.1",
69
67
  ]
70
68
 
@@ -91,7 +89,7 @@ packages = ["src/oe_python_template"]
91
89
  [project.optional-dependencies]
92
90
  examples = [
93
91
  "streamlit>=1.43.2",
94
- "marimo>=0.11.19",
92
+ "marimo>=0.11.26",
95
93
  "jupyter>=1.1.1",
96
94
  "jinja2>=3.1.6",
97
95
  ]
@@ -99,7 +97,7 @@ examples = [
99
97
  [dependency-groups]
100
98
  dev = [
101
99
  "autodoc-pydantic>=2.2.0",
102
- "bump-my-version>=1.0.2",
100
+ "bump-my-version>=1.1.1",
103
101
  "cyclonedx-py>=1.0.1",
104
102
  "detect-secrets>=1.5.0",
105
103
  "enum-tools>=0.12.0",
@@ -113,11 +111,12 @@ dev = [
113
111
  "pre-commit>=4.1.0",
114
112
  "pyright>=1.1.396",
115
113
  "pytest>=8.3.5",
116
- "pytest-asyncio>=0.25.3",
114
+ "pytest-asyncio>=0.26.0",
117
115
  "pytest-cov>=6.0.0",
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'", # GPLv3
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", # notebooks surface variable without print
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] # https://mypy.readthedocs.io/en/latest/config_file.html
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] # https://docs.pydantic.dev/latest/integrations/mypy/#configuring-the-plugin
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.3"
255
+ current_version = "0.7.5"
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 oe_python_template import Echo, Health, HealthStatus, Service, Utterance
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=Service.get_hello_world())
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 oe_python_template import Service, Utterance, __version__
13
- from oe_python_template.api import api_v1, api_v2
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
- console.print_json(data={"text": echo.text})
47
+ _console.print_json(data={"text": echo.text})
36
48
  else:
37
- console.print(echo.text)
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
- console.print(Service.get_hello_world())
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
- console.print(f"Starting API server at http://{host}:{port}")
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
- console.print_json(data=schema)
126
+ _console.print_json(data=schema)
115
127
  case OutputFormat.YAML:
116
- console.print(yaml.dump(schema, default_flow_style=False), end="")
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's."""
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 oe_python_template.models import Echo, Utterance
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
- @staticmethod
21
- def get_hello_world() -> str:
24
+ def healthy(self) -> bool:
22
25
  """
23
- Get a hello world message.
26
+ Check if the service is healthy.
24
27
 
25
28
  Returns:
26
- str: Hello world message.
29
+ bool: True if the service is healthy, False otherwise.
27
30
  """
28
- return f"Hello, world! The value of THE_VAR is {THE_VAR}"
31
+ return self.is_healthy
29
32
 
30
- def healthy(self) -> bool:
33
+ def info(self) -> str:
31
34
  """
32
- Check if the service is healthy.
35
+ Get info about configuration of service.
33
36
 
34
37
  Returns:
35
- bool: True if the service is healthy, False otherwise.
38
+ str: Service configuration.
36
39
  """
37
- return self.is_healthy
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
+ ]