oe-python-template 0.13.13__tar.gz → 0.13.14__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.
Files changed (40) hide show
  1. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/PKG-INFO +1 -1
  2. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/pyproject.toml +2 -2
  3. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_sentry.py +102 -1
  4. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/.gitignore +0 -0
  5. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/LICENSE +0 -0
  6. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/README.md +0 -0
  7. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/__init__.py +0 -0
  8. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/api.py +0 -0
  9. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/cli.py +0 -0
  10. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/constants.py +0 -0
  11. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/hello/__init__.py +0 -0
  12. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/hello/_api.py +0 -0
  13. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/hello/_cli.py +0 -0
  14. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/hello/_constants.py +0 -0
  15. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/hello/_gui.py +0 -0
  16. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/hello/_models.py +0 -0
  17. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/hello/_service.py +0 -0
  18. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/hello/_settings.py +0 -0
  19. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/system/__init__.py +0 -0
  20. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/system/_api.py +0 -0
  21. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/system/_cli.py +0 -0
  22. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/system/_gui.py +0 -0
  23. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/system/_service.py +0 -0
  24. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/system/_settings.py +0 -0
  25. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/.vendored/bottle.py +0 -0
  26. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/__init__.py +0 -0
  27. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_api.py +0 -0
  28. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_cli.py +0 -0
  29. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_console.py +0 -0
  30. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_constants.py +0 -0
  31. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_di.py +0 -0
  32. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_gui.py +0 -0
  33. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_health.py +0 -0
  34. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_log.py +0 -0
  35. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_logfire.py +0 -0
  36. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_notebook.py +0 -0
  37. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_process.py +0 -0
  38. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_service.py +0 -0
  39. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/_settings.py +0 -0
  40. {oe_python_template-0.13.13 → oe_python_template-0.13.14}/src/oe_python_template/utils/boot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oe-python-template
3
- Version: 0.13.13
3
+ Version: 0.13.14
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/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "oe-python-template"
3
- version = "0.13.13"
3
+ version = "0.13.14"
4
4
  description = "🧠 Copier template to scaffold Python projects compliant with best practices and modern tooling."
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Helmut Hoffer von Ankershoffen", email = "helmuthva@gmail.com" }]
@@ -279,7 +279,7 @@ source = ["src/"]
279
279
 
280
280
 
281
281
  [tool.bumpversion]
282
- current_version = "0.13.13"
282
+ current_version = "0.13.14"
283
283
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
284
284
  serialize = ["{major}.{minor}.{patch}"]
285
285
  search = "{current_version}"
@@ -1,15 +1,115 @@
1
1
  """Sentry integration for application monitoring."""
2
2
 
3
+ import re
4
+ import urllib.parse
3
5
  from typing import Annotated
4
6
 
5
7
  import sentry_sdk
6
- from pydantic import BeforeValidator, Field, PlainSerializer, SecretStr
8
+ from pydantic import AfterValidator, BeforeValidator, Field, PlainSerializer, SecretStr
7
9
  from pydantic_settings import SettingsConfigDict
8
10
  from sentry_sdk.integrations.typer import TyperIntegration
9
11
 
10
12
  from ._constants import __env__, __env_file__, __project_name__, __version__
11
13
  from ._settings import OpaqueSettings, load_settings, strip_to_none_before_validator
12
14
 
15
+ _ERR_MSG_MISSING_SCHEME = "Sentry DSN is missing URL scheme (protocol)"
16
+ _ERR_MSG_MISSING_NETLOC = "Sentry DSN is missing network location (domain)"
17
+ _ERR_MSG_NON_HTTPS = "Sentry DSN must use HTTPS protocol for security"
18
+ _ERR_MSG_INVALID_DOMAIN = "Sentry DSN must use a valid Sentry domain (ingest.us.sentry.io or ingest.de.sentry.io)"
19
+ _ERR_MSG_INVALID_FORMAT = "Invalid Sentry DSN format"
20
+ _VALID_SENTRY_DOMAIN_PATTERN = r"^[a-f0-9]+@o\d+\.ingest\.(us|de)\.sentry\.io$"
21
+
22
+
23
+ def _validate_url_scheme(parsed_url: urllib.parse.ParseResult) -> None:
24
+ """Validate that the URL has a scheme.
25
+
26
+ Args:
27
+ parsed_url: The parsed URL to validate
28
+
29
+ Raises:
30
+ ValueError: If URL is missing scheme
31
+ """
32
+ if not parsed_url.scheme:
33
+ raise ValueError(_ERR_MSG_MISSING_SCHEME)
34
+
35
+
36
+ def _validate_url_netloc(parsed_url: urllib.parse.ParseResult) -> None:
37
+ """Validate that the URL has a network location.
38
+
39
+ Args:
40
+ parsed_url: The parsed URL to validate
41
+
42
+ Raises:
43
+ ValueError: If URL is missing network location
44
+ """
45
+ if not parsed_url.netloc:
46
+ raise ValueError(_ERR_MSG_MISSING_NETLOC)
47
+
48
+
49
+ def _validate_https_scheme(parsed_url: urllib.parse.ParseResult) -> None:
50
+ """Validate that the URL uses HTTPS scheme.
51
+
52
+ Args:
53
+ parsed_url: The parsed URL to validate
54
+
55
+ Raises:
56
+ ValueError: If URL doesn't use HTTPS scheme
57
+ """
58
+ if parsed_url.scheme != "https":
59
+ raise ValueError(_ERR_MSG_NON_HTTPS)
60
+
61
+
62
+ def _validate_sentry_domain(netloc_with_auth: str) -> None:
63
+ """Validate that the URL uses a valid Sentry domain.
64
+
65
+ Args:
66
+ netloc_with_auth: The network location with auth part
67
+
68
+ Raises:
69
+ ValueError: If URL doesn't use a valid Sentry domain
70
+ """
71
+ if "@" not in netloc_with_auth:
72
+ raise ValueError(_ERR_MSG_INVALID_DOMAIN)
73
+
74
+ user_pass, domain = netloc_with_auth.split("@", 1)
75
+ full_auth = f"{user_pass}@{domain}"
76
+ if not re.match(_VALID_SENTRY_DOMAIN_PATTERN, full_auth):
77
+ raise ValueError(_ERR_MSG_INVALID_DOMAIN)
78
+
79
+
80
+ def validate_https_dsn(value: SecretStr | None) -> SecretStr | None:
81
+ """Validate that the Sentry DSN is a valid HTTPS URL.
82
+
83
+ Args:
84
+ value: The DSN value to validate
85
+
86
+ Returns:
87
+ SecretStr | None: The validated DSN value
88
+
89
+ Raises:
90
+ ValueError: If DSN isn't a valid HTTPS URL with specific error details
91
+ """
92
+ if value is None:
93
+ return value
94
+
95
+ dsn = value.get_secret_value()
96
+ try:
97
+ parsed_url = urllib.parse.urlparse(dsn)
98
+
99
+ # Call validation functions outside of the try block
100
+ _validate_url_scheme(parsed_url)
101
+ _validate_url_netloc(parsed_url)
102
+ _validate_https_scheme(parsed_url)
103
+ _validate_sentry_domain(parsed_url.netloc)
104
+
105
+ except ValueError as exc:
106
+ raise exc from None
107
+ except Exception as exc:
108
+ error_message = _ERR_MSG_INVALID_FORMAT
109
+ raise ValueError(error_message) from exc
110
+
111
+ return value
112
+
13
113
 
14
114
  class SentrySettings(OpaqueSettings):
15
115
  """Configuration settings for Sentry integration."""
@@ -24,6 +124,7 @@ class SentrySettings(OpaqueSettings):
24
124
  dsn: Annotated[
25
125
  SecretStr | None,
26
126
  BeforeValidator(strip_to_none_before_validator),
127
+ AfterValidator(validate_https_dsn),
27
128
  PlainSerializer(func=OpaqueSettings.serialize_sensitive_info, return_type=str, when_used="always"),
28
129
  Field(description="Sentry DSN", examples=["https://SECRET@SECRET.ingest.de.sentry.io/SECRET"], default=None),
29
130
  ]