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.
- oe_python_template/hello/_service.py +4 -0
- oe_python_template/hello/_settings.py +16 -4
- oe_python_template/utils/__init__.py +2 -1
- oe_python_template/utils/_logfire.py +3 -2
- oe_python_template/utils/_sentry.py +9 -4
- oe_python_template/utils/_settings.py +9 -0
- {oe_python_template-0.10.6.dist-info → oe_python_template-0.10.7.dist-info}/METADATA +8 -9
- {oe_python_template-0.10.6.dist-info → oe_python_template-0.10.7.dist-info}/RECORD +11 -11
- {oe_python_template-0.10.6.dist-info → oe_python_template-0.10.7.dist-info}/WHEEL +0 -0
- {oe_python_template-0.10.6.dist-info → oe_python_template-0.10.7.dist-info}/entry_points.txt +0 -0
- {oe_python_template-0.10.6.dist-info → oe_python_template-0.10.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -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
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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
|
-
[
|
|
79
|
+
[
|
|
80
|
+
](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template/blob/main/LICENSE)
|
|
80
81
|
[](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template/blob/main/noxfile.py)
|
|
81
82
|
[](https://github.com/helmut-hoffer-von-ankershoffen/oe-python-template/actions/workflows/test-and-report.yml)
|
|
82
83
|
[](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.
|
|
176
|
-
12.
|
|
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=
|
|
11
|
-
oe_python_template/hello/_settings.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
32
|
-
oe_python_template-0.10.
|
|
33
|
-
oe_python_template-0.10.
|
|
34
|
-
oe_python_template-0.10.
|
|
35
|
-
oe_python_template-0.10.
|
|
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,,
|
|
File without changes
|
{oe_python_template-0.10.6.dist-info → oe_python_template-0.10.7.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{oe_python_template-0.10.6.dist-info → oe_python_template-0.10.7.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|