hexkit 6.0.0__tar.gz → 7.1.0__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 (66) hide show
  1. {hexkit-6.0.0/src/hexkit.egg-info → hexkit-7.1.0}/PKG-INFO +21 -21
  2. {hexkit-6.0.0 → hexkit-7.1.0}/pyproject.toml +28 -30
  3. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/config.py +5 -4
  4. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/custom_types.py +12 -14
  5. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/log.py +3 -3
  6. hexkit-7.1.0/src/hexkit/opentelemetry.py +76 -0
  7. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/protocols/dao.py +5 -5
  8. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/protocols/daopub.py +5 -6
  9. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/protocols/eventpub.py +2 -3
  10. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/protocols/objstorage.py +20 -24
  11. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/akafka/config.py +2 -2
  12. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/akafka/provider/daosub.py +1 -2
  13. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/akafka/provider/eventpub.py +4 -4
  14. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/akafka/provider/eventsub.py +11 -11
  15. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/akafka/provider/utils.py +1 -2
  16. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/akafka/testcontainer.py +6 -6
  17. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/akafka/testutils.py +14 -14
  18. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongodb/migrations/_manager.py +2 -2
  19. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongodb/migrations/_utils.py +5 -5
  20. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongodb/migrations/helpers.py +2 -3
  21. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongodb/provider.py +4 -4
  22. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongodb/testutils.py +2 -3
  23. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongokafka/provider/daopub.py +5 -5
  24. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongokafka/provider/persistent_pub.py +3 -4
  25. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/s3/provider.py +47 -46
  26. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/s3/testutils/_fixtures.py +6 -6
  27. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/testing/eventpub.py +2 -2
  28. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/utils.py +2 -2
  29. {hexkit-6.0.0 → hexkit-7.1.0/src/hexkit.egg-info}/PKG-INFO +21 -21
  30. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit.egg-info/SOURCES.txt +0 -1
  31. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit.egg-info/requires.txt +19 -18
  32. hexkit-6.0.0/src/hexkit/opentelemetry.py +0 -177
  33. hexkit-6.0.0/src/hexkit.egg-info/entry_points.txt +0 -2
  34. {hexkit-6.0.0 → hexkit-7.1.0}/LICENSE +0 -0
  35. {hexkit-6.0.0 → hexkit-7.1.0}/MANIFEST.in +0 -0
  36. {hexkit-6.0.0 → hexkit-7.1.0}/README.md +0 -0
  37. {hexkit-6.0.0 → hexkit-7.1.0}/setup.cfg +0 -0
  38. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/__init__.py +0 -0
  39. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/__main__.py +0 -0
  40. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/base.py +0 -0
  41. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/correlation.py +0 -0
  42. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/protocols/__init__.py +0 -0
  43. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/protocols/daosub.py +0 -0
  44. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/protocols/eventsub.py +0 -0
  45. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/__init__.py +0 -0
  46. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/akafka/__init__.py +0 -0
  47. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/akafka/provider/__init__.py +0 -0
  48. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongodb/__init__.py +0 -0
  49. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongodb/migrations/__init__.py +0 -0
  50. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongokafka/__init__.py +0 -0
  51. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongokafka/provider/__init__.py +0 -0
  52. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongokafka/provider/config.py +0 -0
  53. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/mongokafka/testutils.py +0 -0
  54. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/s3/__init__.py +0 -0
  55. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/s3/test_files/__init__.py +0 -0
  56. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/s3/test_files/test_file1.yaml +0 -0
  57. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/s3/test_files/test_file2.yaml +0 -0
  58. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/s3/test_files/test_file3.yaml +0 -0
  59. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/s3/test_files/test_file4.yaml +0 -0
  60. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/s3/testutils/__init__.py +0 -0
  61. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/s3/testutils/_typical_workflow.py +0 -0
  62. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/s3/testutils/_utils.py +0 -0
  63. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/providers/testing/__init__.py +0 -0
  64. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit/py.typed +0 -0
  65. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit.egg-info/dependency_links.txt +0 -0
  66. {hexkit-6.0.0 → hexkit-7.1.0}/src/hexkit.egg-info/top_level.txt +0 -0
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hexkit
3
- Version: 6.0.0
3
+ Version: 7.1.0
4
4
  Summary: A Toolkit for Building Microservices using the Hexagonal Architecture
5
5
  Author-email: "German Human Genome Phenome Archive (GHGA)" <contact@ghga.de>
6
6
  License: Apache 2.0
7
7
  Project-URL: Repository, https://github.com/ghga-de/hexkit
8
8
  Classifier: Development Status :: 5 - Production/Stable
9
9
  Classifier: Operating System :: POSIX :: Linux
10
- Classifier: Programming Language :: Python :: 3.9
11
10
  Classifier: Programming Language :: Python :: 3.10
12
11
  Classifier: Programming Language :: Python :: 3.11
13
12
  Classifier: Programming Language :: Python :: 3.12
@@ -16,41 +15,42 @@ Classifier: License :: OSI Approved :: Apache Software License
16
15
  Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
17
16
  Classifier: Topic :: Software Development :: Libraries
18
17
  Classifier: Intended Audience :: Developers
19
- Requires-Python: >=3.9
18
+ Requires-Python: >=3.10
20
19
  Description-Content-Type: text/markdown
21
20
  License-File: LICENSE
22
- Requires-Dist: opentelemetry-api<2,>=1.31.1
23
- Requires-Dist: pydantic<3,>=2.10
24
- Requires-Dist: pydantic_settings<3,>=2.8
21
+ Requires-Dist: opentelemetry-api<2,>=1.37
22
+ Requires-Dist: pydantic<3,>=2.11
23
+ Requires-Dist: pydantic_settings<3,>=2.10
25
24
  Requires-Dist: PyYAML<7,>=6
26
25
  Provides-Extra: akafka
27
26
  Requires-Dist: aiokafka[lz4,snappy,zstd]~=0.12.0; extra == "akafka"
28
- Requires-Dist: jsonschema<5,>=4.23; extra == "akafka"
27
+ Requires-Dist: jsonschema<5,>=4.25; extra == "akafka"
29
28
  Provides-Extra: s3
30
- Requires-Dist: boto3<2,>=1.37; extra == "s3"
31
- Requires-Dist: botocore<2,>=1.37; extra == "s3"
29
+ Requires-Dist: boto3<2,>=1.40; extra == "s3"
30
+ Requires-Dist: botocore<2,>=1.40; extra == "s3"
32
31
  Provides-Extra: mongodb
33
- Requires-Dist: pymongo<5,>=4.13; extra == "mongodb"
32
+ Requires-Dist: pymongo<5,>=4.15; extra == "mongodb"
34
33
  Provides-Extra: opentelemetry-base
35
- Requires-Dist: opentelemetry-sdk<2,>=1.31.1; extra == "opentelemetry-base"
36
- Requires-Dist: opentelemetry-exporter-otlp<2,>=1.31.1; extra == "opentelemetry-base"
37
- Requires-Dist: opentelemetry-instrumentation>=0.52b1; extra == "opentelemetry-base"
38
- Requires-Dist: opentelemetry-instrumentation-httpx>=0.52b1; extra == "opentelemetry-base"
34
+ Requires-Dist: opentelemetry-sdk<2,>=1.37.0; extra == "opentelemetry-base"
35
+ Requires-Dist: opentelemetry-exporter-otlp<2,>=1.37.0; extra == "opentelemetry-base"
36
+ Requires-Dist: opentelemetry-instrumentation>=0.58b0; extra == "opentelemetry-base"
37
+ Requires-Dist: opentelemetry-instrumentation-httpx>=0.58b0; extra == "opentelemetry-base"
38
+ Requires-Dist: opentelemetry-distro>=0.58b0; extra == "opentelemetry-base"
39
39
  Provides-Extra: opentelemetry-akafka
40
40
  Requires-Dist: hexkit[opentelemetry-base]; extra == "opentelemetry-akafka"
41
41
  Requires-Dist: hexkit[akafka]; extra == "opentelemetry-akafka"
42
- Requires-Dist: opentelemetry-instrumentation-aiokafka>=0.52b1; extra == "opentelemetry-akafka"
42
+ Requires-Dist: opentelemetry-instrumentation-aiokafka>=0.58b0; extra == "opentelemetry-akafka"
43
43
  Provides-Extra: opentelemetry-mongodb
44
44
  Requires-Dist: hexkit[opentelemetry-base]; extra == "opentelemetry-mongodb"
45
45
  Requires-Dist: hexkit[mongodb]; extra == "opentelemetry-mongodb"
46
- Requires-Dist: opentelemetry-instrumentation-pymongo>=0.52b1; extra == "opentelemetry-mongodb"
46
+ Requires-Dist: opentelemetry-instrumentation-pymongo>=0.58b0; extra == "opentelemetry-mongodb"
47
47
  Provides-Extra: opentelemetry-fastapi
48
48
  Requires-Dist: hexkit[opentelemetry-base]; extra == "opentelemetry-fastapi"
49
- Requires-Dist: opentelemetry-instrumentation-fastapi>=0.52b1; extra == "opentelemetry-fastapi"
49
+ Requires-Dist: opentelemetry-instrumentation-fastapi>=0.58b0; extra == "opentelemetry-fastapi"
50
50
  Provides-Extra: opentelemetry-s3
51
51
  Requires-Dist: hexkit[opentelemetry-base]; extra == "opentelemetry-s3"
52
52
  Requires-Dist: hexkit[s3]; extra == "opentelemetry-s3"
53
- Requires-Dist: opentelemetry-instrumentation-botocore>=0.52b1; extra == "opentelemetry-s3"
53
+ Requires-Dist: opentelemetry-instrumentation-botocore>=0.58b0; extra == "opentelemetry-s3"
54
54
  Provides-Extra: opentelemetry
55
55
  Requires-Dist: hexkit[opentelemetry-akafka]; extra == "opentelemetry"
56
56
  Requires-Dist: hexkit[opentelemetry-mongodb]; extra == "opentelemetry"
@@ -58,13 +58,13 @@ Requires-Dist: hexkit[opentelemetry-fastapi]; extra == "opentelemetry"
58
58
  Requires-Dist: hexkit[opentelemetry-s3]; extra == "opentelemetry"
59
59
  Provides-Extra: test-akafka
60
60
  Requires-Dist: hexkit[akafka]; extra == "test-akafka"
61
- Requires-Dist: testcontainers[kafka]<5,>=4.9; extra == "test-akafka"
61
+ Requires-Dist: testcontainers[kafka]<5,>=4.13; extra == "test-akafka"
62
62
  Provides-Extra: test-s3
63
63
  Requires-Dist: hexkit[s3]; extra == "test-s3"
64
- Requires-Dist: testcontainers<5,>=4.9; extra == "test-s3"
64
+ Requires-Dist: testcontainers<5,>=4.13; extra == "test-s3"
65
65
  Provides-Extra: test-mongodb
66
66
  Requires-Dist: hexkit[mongodb]; extra == "test-mongodb"
67
- Requires-Dist: testcontainers[mongo]<5,>=4.9; extra == "test-mongodb"
67
+ Requires-Dist: testcontainers[mongo]<5,>=4.13; extra == "test-mongodb"
68
68
  Provides-Extra: test
69
69
  Requires-Dist: hexkit[test-akafka,test-mongodb,test-s3]; extra == "test"
70
70
  Provides-Extra: all
@@ -1,6 +1,6 @@
1
1
  [build-system]
2
2
  requires = [
3
- "setuptools>=80.3",
3
+ "setuptools>=80.9",
4
4
  ]
5
5
  build-backend = "setuptools.build_meta"
6
6
 
@@ -9,11 +9,10 @@ readme = "README.md"
9
9
  authors = [
10
10
  { name = "German Human Genome Phenome Archive (GHGA)", email = "contact@ghga.de" },
11
11
  ]
12
- requires-python = ">=3.9"
12
+ requires-python = ">=3.10"
13
13
  classifiers = [
14
14
  "Development Status :: 5 - Production/Stable",
15
15
  "Operating System :: POSIX :: Linux",
16
- "Programming Language :: Python :: 3.9",
17
16
  "Programming Language :: Python :: 3.10",
18
17
  "Programming Language :: Python :: 3.11",
19
18
  "Programming Language :: Python :: 3.12",
@@ -24,12 +23,12 @@ classifiers = [
24
23
  "Intended Audience :: Developers",
25
24
  ]
26
25
  name = "hexkit"
27
- version = "6.0.0"
26
+ version = "7.1.0"
28
27
  description = "A Toolkit for Building Microservices using the Hexagonal Architecture"
29
28
  dependencies = [
30
- "opentelemetry-api >=1.31.1, <2",
31
- "pydantic >=2.10, <3",
32
- "pydantic_settings >=2.8, <3",
29
+ "opentelemetry-api >=1.37, <2",
30
+ "pydantic >=2.11, <3",
31
+ "pydantic_settings >=2.10, <3",
33
32
  "PyYAML >=6, <7",
34
33
  ]
35
34
 
@@ -39,39 +38,40 @@ text = "Apache 2.0"
39
38
  [project.optional-dependencies]
40
39
  akafka = [
41
40
  "aiokafka[zstd,lz4,snappy]~=0.12.0",
42
- "jsonschema >=4.23, <5",
41
+ "jsonschema >=4.25, <5",
43
42
  ]
44
43
  s3 = [
45
- "boto3 >=1.37, <2",
46
- "botocore >=1.37, <2",
44
+ "boto3 >=1.40, <2",
45
+ "botocore >=1.40, <2",
47
46
  ]
48
47
  mongodb = [
49
- "pymongo >=4.13, <5",
48
+ "pymongo >=4.15, <5",
50
49
  ]
51
50
  opentelemetry-base = [
52
- "opentelemetry-sdk >=1.31.1, <2",
53
- "opentelemetry-exporter-otlp >=1.31.1, <2",
54
- "opentelemetry-instrumentation >=0.52b1",
55
- "opentelemetry-instrumentation-httpx >=0.52b1",
51
+ "opentelemetry-sdk >=1.37.0, <2",
52
+ "opentelemetry-exporter-otlp >=1.37.0, <2",
53
+ "opentelemetry-instrumentation >=0.58b0",
54
+ "opentelemetry-instrumentation-httpx >=0.58b0",
55
+ "opentelemetry-distro >=0.58b0",
56
56
  ]
57
57
  opentelemetry-akafka = [
58
58
  "hexkit[opentelemetry-base]",
59
59
  "hexkit[akafka]",
60
- "opentelemetry-instrumentation-aiokafka >=0.52b1",
60
+ "opentelemetry-instrumentation-aiokafka >=0.58b0",
61
61
  ]
62
62
  opentelemetry-mongodb = [
63
63
  "hexkit[opentelemetry-base]",
64
64
  "hexkit[mongodb]",
65
- "opentelemetry-instrumentation-pymongo >=0.52b1",
65
+ "opentelemetry-instrumentation-pymongo >=0.58b0",
66
66
  ]
67
67
  opentelemetry-fastapi = [
68
68
  "hexkit[opentelemetry-base]",
69
- "opentelemetry-instrumentation-fastapi >=0.52b1",
69
+ "opentelemetry-instrumentation-fastapi >=0.58b0",
70
70
  ]
71
71
  opentelemetry-s3 = [
72
72
  "hexkit[opentelemetry-base]",
73
73
  "hexkit[s3]",
74
- "opentelemetry-instrumentation-botocore >=0.52b1",
74
+ "opentelemetry-instrumentation-botocore >=0.58b0",
75
75
  ]
76
76
  opentelemetry = [
77
77
  "hexkit[opentelemetry-akafka]",
@@ -81,15 +81,15 @@ opentelemetry = [
81
81
  ]
82
82
  test-akafka = [
83
83
  "hexkit[akafka]",
84
- "testcontainers[kafka] >=4.9, <5",
84
+ "testcontainers[kafka] >=4.13, <5",
85
85
  ]
86
86
  test-s3 = [
87
87
  "hexkit[s3]",
88
- "testcontainers >=4.9, <5",
88
+ "testcontainers >=4.13, <5",
89
89
  ]
90
90
  test-mongodb = [
91
91
  "hexkit[mongodb]",
92
- "testcontainers[mongo] >=4.9, <5",
92
+ "testcontainers[mongo] >=4.13, <5",
93
93
  ]
94
94
  test = [
95
95
  "hexkit[test-akafka,test-s3,test-mongodb]",
@@ -102,9 +102,6 @@ all = [
102
102
  [project.urls]
103
103
  Repository = "https://github.com/ghga-de/hexkit"
104
104
 
105
- [project.entry-points.opentelemetry_distro]
106
- hexkit_distro = "hexkit.opentelemetry:HexkitDistro"
107
-
108
105
  [tool.setuptools.packages.find]
109
106
  where = [
110
107
  "src",
@@ -125,7 +122,7 @@ src = [
125
122
  "examples",
126
123
  "scripts",
127
124
  ]
128
- target-version = "py39"
125
+ target-version = "py310"
129
126
 
130
127
  [tool.ruff.lint]
131
128
  fixable = [
@@ -212,7 +209,7 @@ check_untyped_defs = true
212
209
  no_site_packages = false
213
210
 
214
211
  [tool.pytest.ini_options]
215
- minversion = "8.3"
212
+ minversion = "8.4"
216
213
  asyncio_mode = "strict"
217
214
  asyncio_default_fixture_loop_scope = "function"
218
215
 
@@ -226,11 +223,10 @@ source = [
226
223
  [tool.tox]
227
224
  legacy_tox_ini = """
228
225
  [tox]
229
- env_list = py3{9,12,13}
226
+ env_list = py3{10,11,12,13}
230
227
 
231
228
  [gh-actions]
232
229
  python =
233
- 3.9: py39
234
230
  3.10: py310
235
231
  3.11: py311
236
232
  3.12: py312
@@ -242,5 +238,7 @@ legacy_tox_ini = """
242
238
  DOCKER_HOST
243
239
  deps =
244
240
  --no-deps -r ./lock/requirements-dev.txt
245
- commands = pytest {posargs}
241
+ commands =
242
+ py310: pip install --no-deps backports.asyncio.runner==1.2.0 exceptiongroup==1.3.0 tomli==2.2.1
243
+ pytest {posargs}
246
244
  """
@@ -16,8 +16,9 @@
16
16
  """Config parsing functionality based on pydantic's BaseSettings"""
17
17
 
18
18
  import os
19
+ from collections.abc import Callable
19
20
  from pathlib import Path
20
- from typing import Callable, Final, Optional
21
+ from typing import Final
21
22
 
22
23
  from pydantic_settings import (
23
24
  BaseSettings,
@@ -33,7 +34,7 @@ DEFAULT_CONFIG_PREFIX: Final = "ghga_services"
33
34
  class ConfigYamlDoesNotExist(RuntimeError):
34
35
  """Thrown when the context manager is used out of context."""
35
36
 
36
- def __init__(self, path: Path, specified_via: Optional[str] = None):
37
+ def __init__(self, path: Path, specified_via: str | None = None):
37
38
  message = (
38
39
  "The config yaml " + ""
39
40
  if specified_via is None
@@ -42,7 +43,7 @@ class ConfigYamlDoesNotExist(RuntimeError):
42
43
  super().__init__(message)
43
44
 
44
45
 
45
- def get_default_config_yaml(prefix: str) -> Optional[Path]:
46
+ def get_default_config_yaml(prefix: str) -> Path | None:
46
47
  """Get the path to the default config function.
47
48
 
48
49
  Args:
@@ -122,7 +123,7 @@ def config_from_yaml(
122
123
  )
123
124
 
124
125
  def constructor_wrapper(
125
- config_yaml: Optional[Path] = None,
126
+ config_yaml: Path | None = None,
126
127
  **kwargs,
127
128
  ):
128
129
  """A wrapper for constructing a pydantic BaseSetting with modified sources
@@ -18,29 +18,27 @@
18
18
 
19
19
  from collections.abc import Mapping, Sequence
20
20
  from datetime import date, datetime
21
- from typing import Any, Literal, Union
21
+ from typing import Any, Literal
22
22
  from uuid import UUID
23
23
 
24
24
  # A type for fields that can be used as identifiers (primary keys).
25
- ID = Union[int, str, UUID]
25
+ ID = int | str | UUID
26
26
 
27
27
  # This is intended to type objects that are assumed to be JSON serializable.
28
28
  # (Scalar types as well as arrays are excluded from the above assumption,
29
29
  # and we serialize date, datetime and UUID objects to JSON as strings.)
30
30
  JsonObject = Mapping[
31
31
  str,
32
- Union[
33
- int,
34
- float,
35
- str,
36
- bool,
37
- date,
38
- datetime,
39
- UUID,
40
- Sequence[Any],
41
- Mapping[str, Any],
42
- None,
43
- ],
32
+ int
33
+ | float
34
+ | str
35
+ | bool
36
+ | date
37
+ | datetime
38
+ | UUID
39
+ | Sequence[Any]
40
+ | Mapping[str, Any]
41
+ | None,
44
42
  ]
45
43
 
46
44
 
@@ -19,7 +19,7 @@
19
19
  import json
20
20
  from datetime import datetime, timezone
21
21
  from logging import Formatter, Logger, LogRecord, StreamHandler, addLevelName, getLogger
22
- from typing import Any, Literal, Optional
22
+ from typing import Any, Literal
23
23
 
24
24
  from pydantic import Field
25
25
  from pydantic_settings import BaseSettings
@@ -61,7 +61,7 @@ class LoggingConfig(BaseSettings):
61
61
  + " this service. This is included in log messages."
62
62
  ),
63
63
  )
64
- log_format: Optional[str] = Field(
64
+ log_format: str | None = Field(
65
65
  default=None,
66
66
  examples=[
67
67
  "%(timestamp)s - %(service)s - %(level)s - %(message)s",
@@ -166,7 +166,7 @@ class RecordCompiler(StreamHandler):
166
166
  return super().handle(record)
167
167
 
168
168
 
169
- def configure_logging(*, config: LoggingConfig, logger: Optional[Logger] = None):
169
+ def configure_logging(*, config: LoggingConfig, logger: Logger | None = None):
170
170
  """Set up logging.
171
171
 
172
172
  Configures the root logger by default, but can be used to configure a specific
@@ -0,0 +1,76 @@
1
+ # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
+ # for the German Human Genome-Phenome Archive (GHGA)
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """OpenTelemetry specific configuration code. This is gated behind the opentelemetry extra."""
16
+
17
+ import logging
18
+ import os
19
+ from typing import Annotated
20
+
21
+ from opentelemetry import trace
22
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
23
+ from opentelemetry.sdk.environment_variables import OTEL_SDK_DISABLED
24
+ from opentelemetry.sdk.resources import SERVICE_NAME, Resource
25
+ from opentelemetry.sdk.trace import TracerProvider
26
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
27
+ from opentelemetry.sdk.trace.sampling import ParentBasedTraceIdRatio
28
+ from pydantic import Field
29
+ from pydantic_settings import BaseSettings
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ class OpenTelemetryConfig(BaseSettings):
35
+ """OpenTelemetry specific configuration options"""
36
+
37
+ enable_opentelemetry: bool = Field(
38
+ default=False,
39
+ description="If set to true, this will run necessary setup code."
40
+ "If set to false, environment variables are set that should also effectively "
41
+ "disable autoinstrumentation.",
42
+ )
43
+ otel_trace_sampling_rate: Annotated[float, Field(strict=True, ge=0, le=1)] = Field(
44
+ default=1.0,
45
+ description="Determines which proportion of spans should be sampled. "
46
+ "A value of 1.0 means all and is equivalent to the previous behaviour. "
47
+ "Setting this to 0 will result in no spans being sampled, but this does not "
48
+ "automatically set `enable_opentelemetry` to False.",
49
+ )
50
+
51
+
52
+ def configure_opentelemetry(*, service_name: str, config: OpenTelemetryConfig):
53
+ """Configure all needed parts of OpenTelemetry.
54
+
55
+ Setup of the TracerProvider is done programmatically, while disabling OpenTelemetry
56
+ sets the corresponding environment variable.
57
+ """
58
+ if config.enable_opentelemetry:
59
+ resource = Resource(attributes={SERVICE_NAME: service_name})
60
+ # Replace the default static sampler with a probabilistic one that honors parent
61
+ # span sampling decisions
62
+ # This should consistently yield full traces within a service but not necessarily
63
+ # across service boundaries
64
+ # With the default sampling rate, behaviour does not change, but this allows to
65
+ # introduce head sampling by adjusting a config option on the service side later on
66
+ sampler = ParentBasedTraceIdRatio(rate=config.otel_trace_sampling_rate)
67
+
68
+ # Initialize service specific TracerProvider
69
+ trace_provider = TracerProvider(resource=resource, sampler=sampler)
70
+ processor = BatchSpanProcessor(OTLPSpanExporter())
71
+ trace_provider.add_span_processor(processor)
72
+ trace.set_tracer_provider(trace_provider)
73
+ else:
74
+ # Currently OTEL_SDK_DISABLED doesn't seem to be honored by all implementations yet
75
+ # It seems to be working well enough for the Python implementation.
76
+ os.environ[OTEL_SDK_DISABLED] = "true"
@@ -23,7 +23,7 @@ from abc import ABC, abstractmethod
23
23
  from collections.abc import AsyncIterator, Collection, Mapping
24
24
  from contextlib import AbstractAsyncContextManager
25
25
  from functools import partial
26
- from typing import Any, Optional, TypeVar
26
+ from typing import Any, TypeVar
27
27
  from uuid import uuid4
28
28
 
29
29
  from pydantic import BaseModel, Field
@@ -268,7 +268,7 @@ class DaoFactoryBase:
268
268
  cls,
269
269
  *,
270
270
  dto_model: type[Dto],
271
- fields_to_index: Optional[Collection[str]],
271
+ fields_to_index: Collection[str] | None,
272
272
  ) -> None:
273
273
  """Checks that all provided fields are present in the dto_model.
274
274
  Raises IndexFieldsInvalidError otherwise.
@@ -289,7 +289,7 @@ class DaoFactoryBase:
289
289
  *,
290
290
  dto_model: type[Dto],
291
291
  id_field: str,
292
- fields_to_index: Optional[Collection[str]],
292
+ fields_to_index: Collection[str] | None,
293
293
  ) -> None:
294
294
  """Validates the input parameters of the get_dao method."""
295
295
  cls._validate_dto_model_id(dto_model=dto_model, id_field=id_field)
@@ -309,7 +309,7 @@ class DaoFactoryProtocol(DaoFactoryBase, ABC):
309
309
  name: str,
310
310
  dto_model: type[Dto],
311
311
  id_field: str,
312
- fields_to_index: Optional[Collection[str]] = None,
312
+ fields_to_index: Collection[str] | None = None,
313
313
  ) -> Dao[Dto]:
314
314
  """Constructs a DAO for interacting with resources in a database.
315
315
 
@@ -354,7 +354,7 @@ class DaoFactoryProtocol(DaoFactoryBase, ABC):
354
354
  name: str,
355
355
  dto_model: type[Dto],
356
356
  id_field: str,
357
- fields_to_index: Optional[Collection[str]],
357
+ fields_to_index: Collection[str] | None,
358
358
  ) -> Dao[Dto]:
359
359
  """*To be implemented by the provider. Input validation is done outside of this
360
360
  method.*
@@ -23,8 +23,7 @@ of the outbox pattern.
23
23
 
24
24
  import typing
25
25
  from abc import ABC, abstractmethod
26
- from collections.abc import Collection
27
- from typing import Callable, Optional
26
+ from collections.abc import Callable, Collection
28
27
 
29
28
  from hexkit.custom_types import JsonObject
30
29
  from hexkit.protocols.dao import (
@@ -61,8 +60,8 @@ class DaoPublisherFactoryProtocol(DaoFactoryBase, ABC):
61
60
  name: str,
62
61
  dto_model: type[Dto],
63
62
  id_field: str,
64
- fields_to_index: Optional[Collection[str]] = None,
65
- dto_to_event: Callable[[Dto], Optional[JsonObject]],
63
+ fields_to_index: Collection[str] | None = None,
64
+ dto_to_event: Callable[[Dto], JsonObject | None],
66
65
  event_topic: str,
67
66
  autopublish: bool = True,
68
67
  ) -> DaoPublisher[Dto]:
@@ -121,8 +120,8 @@ class DaoPublisherFactoryProtocol(DaoFactoryBase, ABC):
121
120
  name: str,
122
121
  dto_model: type[Dto],
123
122
  id_field: str,
124
- fields_to_index: Optional[Collection[str]],
125
- dto_to_event: Callable[[Dto], Optional[JsonObject]],
123
+ fields_to_index: Collection[str] | None,
124
+ dto_to_event: Callable[[Dto], JsonObject | None],
126
125
  event_topic: str,
127
126
  autopublish: bool,
128
127
  ) -> DaoPublisher[Dto]:
@@ -18,7 +18,6 @@
18
18
 
19
19
  from abc import ABC, abstractmethod
20
20
  from collections.abc import Mapping
21
- from typing import Optional
22
21
  from uuid import UUID, uuid4
23
22
 
24
23
  from pydantic import UUID4
@@ -37,8 +36,8 @@ class EventPublisherProtocol(ABC):
37
36
  type_: Ascii,
38
37
  key: Ascii,
39
38
  topic: Ascii,
40
- event_id: Optional[UUID4] = None,
41
- headers: Optional[Mapping[str, str]] = None,
39
+ event_id: UUID4 | None = None,
40
+ headers: Mapping[str, str] | None = None,
42
41
  ) -> None:
43
42
  """Publish an event.
44
43