oe-python-template 0.10.6__py3-none-any.whl → 0.10.7__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.
@@ -5,6 +5,7 @@ import string
5
5
  from http import HTTPStatus
6
6
  from typing import Any
7
7
 
8
+ import logfire
8
9
  import requests
9
10
 
10
11
  from oe_python_template.utils import BaseService, Health
@@ -74,6 +75,9 @@ class Service(BaseService):
74
75
  Returns:
75
76
  str: Hello world message.
76
77
  """
78
+ messages_sent = logfire.metric_counter("hello_world_messages_sent")
79
+ messages_sent.add(1)
80
+
77
81
  match self._settings.language:
78
82
  case Language.GERMAN:
79
83
  return HELLO_WORLD_DE_DE
@@ -3,10 +3,10 @@
3
3
  from enum import StrEnum
4
4
  from typing import Annotated
5
5
 
6
- from pydantic import Field
7
- from pydantic_settings import BaseSettings, SettingsConfigDict
6
+ from pydantic import BeforeValidator, Field, PlainSerializer, SecretStr
7
+ from pydantic_settings import SettingsConfigDict
8
8
 
9
- from oe_python_template.utils import __env_file__, __project_name__
9
+ from oe_python_template.utils import OpaqueSettings, __env_file__, __project_name__, strip_to_none_before_validator
10
10
 
11
11
 
12
12
  class Language(StrEnum):
@@ -18,7 +18,7 @@ class Language(StrEnum):
18
18
 
19
19
  # Settings derived from BaseSettings and exported by modules via their __init__.py are automatically registered
20
20
  # by the system module e.g. for showing all settings via the system info command.
21
- class Settings(BaseSettings):
21
+ class Settings(OpaqueSettings):
22
22
  """Settings."""
23
23
 
24
24
  model_config = SettingsConfigDict(
@@ -35,3 +35,15 @@ class Settings(BaseSettings):
35
35
  description="Language to use for output - defaults to US english.",
36
36
  ),
37
37
  ]
38
+
39
+ token: Annotated[
40
+ SecretStr | None,
41
+ BeforeValidator(strip_to_none_before_validator), # strip and if empty set to None
42
+ PlainSerializer(
43
+ func=OpaqueSettings.serialize_sensitive_info, return_type=str, when_used="always"
44
+ ), # allow to unhide sensitive info from CLI or if user presents valid token via API
45
+ Field(
46
+ description="Secret token of Hello module.",
47
+ default=None,
48
+ ),
49
+ ]
@@ -23,7 +23,7 @@ from ._logfire import LogfireSettings
23
23
  from ._process import ProcessInfo, get_process_info
24
24
  from ._sentry import SentrySettings
25
25
  from ._service import BaseService
26
- from ._settings import UNHIDE_SENSITIVE_INFO, OpaqueSettings, load_settings
26
+ from ._settings import UNHIDE_SENSITIVE_INFO, OpaqueSettings, load_settings, strip_to_none_before_validator
27
27
  from .boot import boot
28
28
 
29
29
  __all__ = [
@@ -56,4 +56,5 @@ __all__ = [
56
56
  "locate_implementations",
57
57
  "locate_subclasses",
58
58
  "prepare_cli",
59
+ "strip_to_none_before_validator",
59
60
  ]
@@ -3,11 +3,11 @@
3
3
  from typing import Annotated
4
4
 
5
5
  import logfire
6
- from pydantic import Field, PlainSerializer, SecretStr
6
+ from pydantic import BeforeValidator, Field, PlainSerializer, SecretStr
7
7
  from pydantic_settings import SettingsConfigDict
8
8
 
9
9
  from ._constants import __env__, __env_file__, __project_name__, __repository_url__, __version__
10
- from ._settings import OpaqueSettings, load_settings
10
+ from ._settings import OpaqueSettings, load_settings, strip_to_none_before_validator
11
11
 
12
12
 
13
13
  class LogfireSettings(OpaqueSettings):
@@ -22,6 +22,7 @@ class LogfireSettings(OpaqueSettings):
22
22
 
23
23
  token: Annotated[
24
24
  SecretStr | None,
25
+ BeforeValidator(strip_to_none_before_validator),
25
26
  PlainSerializer(func=OpaqueSettings.serialize_sensitive_info, return_type=str, when_used="always"),
26
27
  Field(description="Logfire token. Leave empty to disable logfire.", examples=["YOUR_TOKEN"], default=None),
27
28
  ]
@@ -3,12 +3,12 @@
3
3
  from typing import Annotated
4
4
 
5
5
  import sentry_sdk
6
- from pydantic import Field, PlainSerializer, SecretStr
6
+ from pydantic import BeforeValidator, Field, PlainSerializer, SecretStr
7
7
  from pydantic_settings import SettingsConfigDict
8
8
  from sentry_sdk.integrations.typer import TyperIntegration
9
9
 
10
10
  from ._constants import __env__, __env_file__, __project_name__, __version__
11
- from ._settings import OpaqueSettings, load_settings
11
+ from ._settings import OpaqueSettings, load_settings, strip_to_none_before_validator
12
12
 
13
13
 
14
14
  class SentrySettings(OpaqueSettings):
@@ -23,6 +23,7 @@ class SentrySettings(OpaqueSettings):
23
23
 
24
24
  dsn: Annotated[
25
25
  SecretStr | None,
26
+ BeforeValidator(strip_to_none_before_validator),
26
27
  PlainSerializer(func=OpaqueSettings.serialize_sensitive_info, return_type=str, when_used="always"),
27
28
  Field(description="Sentry DSN", examples=["https://SECRET@SECRET.ingest.de.sentry.io/SECRET"], default=None),
28
29
  ]
@@ -44,12 +45,14 @@ class SentrySettings(OpaqueSettings):
44
45
  int,
45
46
  Field(
46
47
  description="Max breadcrumbs (https://docs.sentry.io/platforms/python/configuration/options/#max_breadcrumbs)",
47
- default=5.0,
48
+ ge=0,
49
+ default=50,
48
50
  ),
49
51
  ]
50
52
  sample_rate: Annotated[
51
53
  float,
52
54
  Field(
55
+ ge=0.0,
53
56
  description="Sample Rate (https://docs.sentry.io/platforms/python/configuration/sampling/#sampling-error-events)",
54
57
  default=1.0,
55
58
  ),
@@ -57,6 +60,7 @@ class SentrySettings(OpaqueSettings):
57
60
  traces_sample_rate: Annotated[
58
61
  float,
59
62
  Field(
63
+ ge=0.0,
60
64
  description="Traces Sample Rate (https://docs.sentry.io/platforms/python/configuration/sampling/#configuring-the-transaction-sample-rate)",
61
65
  default=1.0,
62
66
  ),
@@ -64,6 +68,7 @@ class SentrySettings(OpaqueSettings):
64
68
  profiles_sample_rate: Annotated[
65
69
  float,
66
70
  Field(
71
+ ge=0.0,
67
72
  description="Traces Sample Rate (https://docs.sentry.io/platforms/python/tracing/#configure)",
68
73
  default=1.0,
69
74
  ),
@@ -84,7 +89,7 @@ def sentry_initialize() -> bool:
84
89
  sentry_sdk.init(
85
90
  release=f"{__project_name__}@{__version__}", # https://docs.sentry.io/platforms/python/configuration/releases/,
86
91
  environment=__env__,
87
- dsn=settings.dsn.get_secret_value(),
92
+ dsn=settings.dsn.get_secret_value().strip(),
88
93
  max_breadcrumbs=settings.max_breadcrumbs,
89
94
  debug=settings.debug,
90
95
  send_default_pii=settings.send_default_pii,
@@ -20,6 +20,15 @@ logger = logging.getLogger(__name__)
20
20
  UNHIDE_SENSITIVE_INFO = "unhide_sensitive_info"
21
21
 
22
22
 
23
+ def strip_to_none_before_validator(v: str | None) -> str | None:
24
+ if v is None:
25
+ return None
26
+ v = v.strip()
27
+ if not v:
28
+ return None
29
+ return v
30
+
31
+
23
32
  class OpaqueSettings(BaseSettings):
24
33
  @staticmethod
25
34
  def serialize_sensitive_info(input_value: SecretStr, info: FieldSerializationInfo) -> str | None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oe-python-template
3
- Version: 0.10.6
3
+ Version: 0.10.7
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/
@@ -76,7 +76,8 @@ Description-Content-Type: text/markdown
76
76
 
77
77
  # 🧠 OE Python Template
78
78
 
79
- [![License](https://img.shields.io/github/license/helmut-hoffer-von-ankershoffen/oe-python-template?logo=opensourceinitiative&logoColor=3DA639&labelColor=414042&color=A41831)](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template/blob/main/LICENSE)
79
+ [![License](https://img.shields.io/github/license/helmut-hoffer-von-ankershoffen/oe-python-template?logo=opensourceinitiative&logoColor=3DA639&labelColor=414042&color=A41831)
80
+ ](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template/blob/main/LICENSE)
80
81
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/oe-python-template.svg?logo=python&color=204361&labelColor=1E2933)](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template/blob/main/noxfile.py)
81
82
  [![CI](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template/actions/workflows/test-and-report.yml/badge.svg)](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template/actions/workflows/test-and-report.yml)
82
83
  [![Read the Docs](https://img.shields.io/readthedocs/oe-python-template)](https://oe-python-template.readthedocs.io/en/latest/)
@@ -109,10 +110,7 @@ Description-Content-Type: text/markdown
109
110
  -->
110
111
 
111
112
  > [!TIP]
112
- > 📚
113
- > [Online documentation](https://oe-python-template.readthedocs.io/en/latest/) -
114
- > 📖
115
- > [PDF Manual](https://oe-python-template.readthedocs.io/_/downloads/en/latest/pdf/)
113
+ > 📚 [Online documentation](https://oe-python-template.readthedocs.io/en/latest/) - 📖 [PDF Manual](https://oe-python-template.readthedocs.io/_/downloads/en/latest/pdf/)
116
114
 
117
115
  ---
118
116
 
@@ -162,7 +160,7 @@ Projects generated with this template come with a comprehensive development tool
162
160
 
163
161
  Beyond development tooling, projects generated with this template include the code, documentation, and configuration of a fully functioning application and service. This reference implementation serves as a starting point for your own business logic with modern patterns and enterprise practices already in place:
164
162
 
165
- 1. Usable as library with "Hello" module exposing a simple service
163
+ 1. Usable as library with "Hello" module exposing a simple service that can say "Hello, world!" and echo utterances.
166
164
  2. Command-line interface (CLI) with [Typer](https://typer.tiangolo.com/)
167
165
  3. Versioned webservice API with [FastAPI](https://fastapi.tiangolo.com/)
168
166
  4. [Interactive Jupyter notebook](https://jupyter.org/) and [reactive Marimo notebook](https://marimo.io/)
@@ -172,8 +170,9 @@ Beyond development tooling, projects generated with this template include the co
172
170
  8. Info command enabling to inspect the runtime, compiled settings, and further info provided dynamically by modules
173
171
  9. Health endpoint exposing system health dynamically aggregated from all modules and dependencies
174
172
  10. Flexible logging and instrumentation, including support for [Sentry](https://sentry.io/) and [Logfire](https://logfire.dev/)
175
- 11. Modular architecture including auto-registration of services, CLI commands and API routes exposed by modules
176
- 12. Documentation including dynamic badges, setup instructions, contribution guide and security policy
173
+ 11. Hello service demonstrates use of custom real time metrics collected via Logfire
174
+ 12. Modular architecture including auto-registration of services, CLI commands and API routes exposed by modules
175
+ 13. Documentation including dynamic badges, setup instructions, contribution guide and security policy
177
176
 
178
177
  Explore [here](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template-example) for what's generated out of the box.
179
178
 
@@ -7,14 +7,14 @@ oe_python_template/hello/_api.py,sha256=hWWlEDUfFY1se2ZzhqGMfPyD3FPwuA-YzxG9Q9z4
7
7
  oe_python_template/hello/_cli.py,sha256=mSNmRj_VRoRfCAR1I8tssZqZCTmT6jgzsNWrTtlFP7Y,1184
8
8
  oe_python_template/hello/_constants.py,sha256=6aRleAIcdgC13TeTzI07YwjoSwqGb2g131dw8aEoM4I,109
9
9
  oe_python_template/hello/_models.py,sha256=JtI7wGT72u23NOxFa-oeWzdyiMg7PnHL5eg22im2_yQ,574
10
- oe_python_template/hello/_service.py,sha256=zI5lXKdI28D1uIqY0phecnlJPxCrwtQVO6d7w1HstS8,3054
11
- oe_python_template/hello/_settings.py,sha256=1z77_B4pWFx_ag9EasgUM01MjKeBwiAhnkhL9XjlR9I,984
10
+ oe_python_template/hello/_service.py,sha256=22-IqNpra43IQcdeZNnzY4uhdPZmlXL6mCsLTCm6Zp4,3175
11
+ oe_python_template/hello/_settings.py,sha256=_85PIdq8UuS4orCZ0qFJ22RTQTooOTB0USRxBapBK3M,1533
12
12
  oe_python_template/system/__init__.py,sha256=NNgODkr7AyJjTTJiv3pys7o2z6xi1G96g0vnsxVhlI4,427
13
13
  oe_python_template/system/_api.py,sha256=rE9Aau3IIHXdEkOBUXOwJ7SxN3cZpgtYEuojnSWfT_4,3687
14
14
  oe_python_template/system/_cli.py,sha256=J_4upBBdbSxbONPYmOZPbuhZcKjfnPIUeZpp0L7lY-Q,4846
15
15
  oe_python_template/system/_service.py,sha256=e08VPguXKz2RhyhTbIw1ncSpE6zwKUr1xLM2WZC9u7k,6287
16
16
  oe_python_template/system/_settings.py,sha256=MwMAJYifJ6jGImeSh4e9shmIXmiUSuQGHXz_Ts0mSdk,901
17
- oe_python_template/utils/__init__.py,sha256=rHdmSS21CtvF3AXPMSCZO17FTzxPDo-NiKZ5AjVU9b0,1449
17
+ oe_python_template/utils/__init__.py,sha256=MMpcuyU1gfCC3_h3RavS6SD1cAn1FeaqD1qfu6SrAZ8,1519
18
18
  oe_python_template/utils/_api.py,sha256=w3hPQK1pL2gBI4_1qNWNa2b4S_oH-8mY-ckRX0KrCWM,617
19
19
  oe_python_template/utils/_cli.py,sha256=J_mFtXZ1gGeovGrE5i3wlokTOBfiTTKEz5magiRP7GA,2091
20
20
  oe_python_template/utils/_console.py,sha256=u0-utcdRmVu4rabrYUyNOx8yPxLhxB3E92m22kSCwPQ,293
@@ -22,14 +22,14 @@ oe_python_template/utils/_constants.py,sha256=1ocbciHjhmy66uHrTw6p-fbBq_wLl1HaUS
22
22
  oe_python_template/utils/_di.py,sha256=KdjiD4xZ_QSfbddkKWwsPJmG5YrIg6dzuBrlsd-FhxA,2189
23
23
  oe_python_template/utils/_health.py,sha256=35QOWe2r5InrEpGtuVMym9dI5aRHS0HWf4BHBRAUIj0,4102
24
24
  oe_python_template/utils/_log.py,sha256=ZW4gs540SdjVK-2KeheLfDY15d_3xpO5FyGn7wTXyaM,3592
25
- oe_python_template/utils/_logfire.py,sha256=g2tR60XgpdRs_UwDpqkwWm6ErUBcV7lPFBJdvgfTuu0,2022
25
+ oe_python_template/utils/_logfire.py,sha256=wZYNVowQx7kh3XJoJ59FjUKdrta7tp6cXOJRUT6lDU8,2128
26
26
  oe_python_template/utils/_process.py,sha256=40R0NZMqJUn0iUPERzohSUpJgU1HcJApIg1HipIxFCw,941
27
- oe_python_template/utils/_sentry.py,sha256=Y4hZ-PeBOdR3iRhoXW9j0tbWsYf07460UG8OVTKH1mU,3128
27
+ oe_python_template/utils/_sentry.py,sha256=2sXrDSZSYoDEM87v7CakJ6eGBtcIhDI48PsQCLwOHgg,3319
28
28
  oe_python_template/utils/_service.py,sha256=atHAejvBucKXjzhsMSdOBBFa7rRD74zcV70Pp0pl0Tg,1038
29
- oe_python_template/utils/_settings.py,sha256=5K1pnp-AxMQbktREb3bXDmqgrOx_L4EJIgjPQfqH4sE,2294
29
+ oe_python_template/utils/_settings.py,sha256=owFoaHEzJnVD3EVyOWF4rfIY7g6eLnU6rN0m4VHhCbA,2464
30
30
  oe_python_template/utils/boot.py,sha256=TBgmqbtIryQz0cAozYzxhYQRIldfbJ6v9R-rH6sO9mY,2696
31
- oe_python_template-0.10.6.dist-info/METADATA,sha256=5ajCUu0QLNc8cMlDF2fGynuMvMeqnGYJYtKrWWKfo6I,31964
32
- oe_python_template-0.10.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
33
- oe_python_template-0.10.6.dist-info/entry_points.txt,sha256=IroSSWhLGxus9rxcashkYQda39TTvf7LbUMYtOKXUBE,66
34
- oe_python_template-0.10.6.dist-info/licenses/LICENSE,sha256=5H409K6xzz9U5eUaoAHQExNkoWJRlU0LEj6wL2QJ34s,1113
35
- oe_python_template-0.10.6.dist-info/RECORD,,
31
+ oe_python_template-0.10.7.dist-info/METADATA,sha256=7myPyZr9mRK3A6cn_lSkybFVOihRyCUkavEoLOliaAk,32094
32
+ oe_python_template-0.10.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
33
+ oe_python_template-0.10.7.dist-info/entry_points.txt,sha256=IroSSWhLGxus9rxcashkYQda39TTvf7LbUMYtOKXUBE,66
34
+ oe_python_template-0.10.7.dist-info/licenses/LICENSE,sha256=5H409K6xzz9U5eUaoAHQExNkoWJRlU0LEj6wL2QJ34s,1113
35
+ oe_python_template-0.10.7.dist-info/RECORD,,