hexkit 8.0.0__tar.gz → 8.2.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 (88) hide show
  1. {hexkit-8.0.0 → hexkit-8.2.0}/LICENSE +1 -1
  2. {hexkit-8.0.0/src/hexkit.egg-info → hexkit-8.2.0}/PKG-INFO +16 -8
  3. {hexkit-8.0.0 → hexkit-8.2.0}/pyproject.toml +37 -11
  4. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/__init__.py +1 -1
  5. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/__main__.py +1 -1
  6. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/base.py +1 -1
  7. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/config.py +19 -8
  8. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/correlation.py +1 -1
  9. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/custom_types.py +1 -1
  10. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/log.py +1 -1
  11. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/opentelemetry.py +1 -1
  12. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/protocols/__init__.py +1 -1
  13. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/protocols/dao.py +12 -1
  14. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/protocols/daopub.py +1 -1
  15. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/protocols/daosub.py +1 -1
  16. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/protocols/eventpub.py +1 -1
  17. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/protocols/eventsub.py +1 -1
  18. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/protocols/kvstore.py +1 -1
  19. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/protocols/objstorage.py +58 -1
  20. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/__init__.py +1 -1
  21. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/__init__.py +1 -1
  22. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/config.py +1 -1
  23. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/provider/__init__.py +1 -1
  24. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/provider/daosub.py +3 -3
  25. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/provider/eventpub.py +1 -1
  26. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/provider/eventsub.py +5 -3
  27. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/provider/utils.py +1 -1
  28. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/testcontainer.py +1 -1
  29. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/testutils.py +4 -4
  30. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/__init__.py +1 -1
  31. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/config.py +1 -1
  32. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/migrations/__init__.py +1 -1
  33. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/migrations/_manager.py +1 -1
  34. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/migrations/_utils.py +1 -1
  35. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/migrations/helpers.py +5 -5
  36. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/provider/__init__.py +1 -1
  37. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/provider/client.py +1 -1
  38. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/provider/dao.py +27 -8
  39. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/provider/kvstore.py +7 -3
  40. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/provider/utils.py +2 -2
  41. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/testutils.py +1 -1
  42. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/__init__.py +3 -1
  43. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/config.py +1 -1
  44. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/provider/__init__.py +1 -1
  45. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/provider/daopub.py +48 -15
  46. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/provider/persistent_pub.py +1 -1
  47. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/testutils.py +1 -1
  48. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/redis/__init__.py +1 -1
  49. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/redis/config.py +1 -1
  50. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/redis/provider/__init__.py +1 -1
  51. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/redis/provider/client.py +1 -1
  52. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/redis/provider/kvstore.py +9 -4
  53. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/redis/testutils.py +1 -1
  54. hexkit-8.2.0/src/hexkit/providers/s3/__init__.py +36 -0
  55. hexkit-8.2.0/src/hexkit/providers/s3/config.py +68 -0
  56. {hexkit-8.0.0/src/hexkit/providers/s3 → hexkit-8.2.0/src/hexkit/providers/s3/provider}/__init__.py +17 -5
  57. hexkit-8.2.0/src/hexkit/providers/s3/provider/kvstore.py +250 -0
  58. hexkit-8.0.0/src/hexkit/providers/s3/provider.py → hexkit-8.2.0/src/hexkit/providers/s3/provider/objstorage.py +87 -112
  59. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/s3/test_files/__init__.py +1 -1
  60. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/s3/testutils/__init__.py +1 -1
  61. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/s3/testutils/_fixtures.py +2 -2
  62. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/s3/testutils/_typical_workflow.py +1 -1
  63. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/s3/testutils/_utils.py +1 -1
  64. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/testing/__init__.py +11 -1
  65. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/testing/dao.py +1 -1
  66. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/testing/eventpub.py +1 -1
  67. hexkit-8.2.0/src/hexkit/providers/testing/kvstore.py +222 -0
  68. hexkit-8.0.0/src/hexkit/providers/testing/s3.py → hexkit-8.2.0/src/hexkit/providers/testing/objstorage.py +44 -17
  69. hexkit-8.2.0/src/hexkit/providers/vault/__init__.py +33 -0
  70. hexkit-8.2.0/src/hexkit/providers/vault/config.py +118 -0
  71. hexkit-8.2.0/src/hexkit/providers/vault/provider/__init__.py +20 -0
  72. hexkit-8.2.0/src/hexkit/providers/vault/provider/client.py +131 -0
  73. hexkit-8.2.0/src/hexkit/providers/vault/provider/kvstore.py +275 -0
  74. hexkit-8.2.0/src/hexkit/providers/vault/testutils.py +258 -0
  75. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/utils.py +4 -3
  76. {hexkit-8.0.0 → hexkit-8.2.0/src/hexkit.egg-info}/PKG-INFO +16 -8
  77. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit.egg-info/SOURCES.txt +12 -2
  78. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit.egg-info/requires.txt +14 -7
  79. {hexkit-8.0.0 → hexkit-8.2.0}/MANIFEST.in +0 -0
  80. {hexkit-8.0.0 → hexkit-8.2.0}/README.md +0 -0
  81. {hexkit-8.0.0 → hexkit-8.2.0}/setup.cfg +0 -0
  82. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/s3/test_files/test_file1.yaml +0 -0
  83. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/s3/test_files/test_file2.yaml +0 -0
  84. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/s3/test_files/test_file3.yaml +0 -0
  85. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/providers/s3/test_files/test_file4.yaml +0 -0
  86. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit/py.typed +0 -0
  87. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit.egg-info/dependency_links.txt +0 -0
  88. {hexkit-8.0.0 → hexkit-8.2.0}/src/hexkit.egg-info/top_level.txt +0 -0
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
189
+ Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
190
190
  for the German Human Genome-Phenome Archive (GHGA)
191
191
 
192
192
  Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,10 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hexkit
3
- Version: 8.0.0
3
+ Version: 8.2.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
+ Project-URL: Documentation, https://ghga-de.github.io/hexkit/
9
+ Project-URL: Release Notes, https://github.com/ghga-de/hexkit/releases
10
+ Project-URL: Issue Tracker, https://github.com/ghga-de/hexkit/issues
8
11
  Classifier: Development Status :: 5 - Production/Stable
9
12
  Classifier: Operating System :: POSIX :: Linux
10
13
  Classifier: Programming Language :: Python :: 3.10
@@ -23,15 +26,17 @@ Requires-Dist: pydantic<3,>=2.12
23
26
  Requires-Dist: pydantic_settings<3,>=2.12
24
27
  Requires-Dist: PyYAML<7,>=6
25
28
  Provides-Extra: akafka
26
- Requires-Dist: aiokafka[lz4,snappy,zstd]~=0.13.0; extra == "akafka"
29
+ Requires-Dist: aiokafka[lz4,snappy,zstd]~=0.14.0; extra == "akafka"
27
30
  Requires-Dist: jsonschema<5,>=4.25; extra == "akafka"
28
31
  Provides-Extra: s3
29
32
  Requires-Dist: boto3<2,>=1.42; extra == "s3"
30
33
  Requires-Dist: botocore<2,>=1.42; extra == "s3"
31
34
  Provides-Extra: mongodb
32
- Requires-Dist: pymongo<5,>=4.15; extra == "mongodb"
35
+ Requires-Dist: pymongo<5,>=4.16; extra == "mongodb"
33
36
  Provides-Extra: redis
34
37
  Requires-Dist: redis<8,>=7.1; extra == "redis"
38
+ Provides-Extra: vault
39
+ Requires-Dist: hvac<3,>=2.4; extra == "vault"
35
40
  Provides-Extra: opentelemetry-base
36
41
  Requires-Dist: opentelemetry-sdk<2,>=1.39.1; extra == "opentelemetry-base"
37
42
  Requires-Dist: opentelemetry-exporter-otlp<2,>=1.39.1; extra == "opentelemetry-base"
@@ -64,18 +69,21 @@ Requires-Dist: hexkit[opentelemetry-fastapi]; extra == "opentelemetry"
64
69
  Requires-Dist: hexkit[opentelemetry-s3]; extra == "opentelemetry"
65
70
  Provides-Extra: test-akafka
66
71
  Requires-Dist: hexkit[akafka]; extra == "test-akafka"
67
- Requires-Dist: testcontainers[kafka]<5,>=4.13; extra == "test-akafka"
72
+ Requires-Dist: testcontainers[kafka]<5,>=4.14; extra == "test-akafka"
68
73
  Provides-Extra: test-s3
69
74
  Requires-Dist: hexkit[s3]; extra == "test-s3"
70
- Requires-Dist: testcontainers<5,>=4.13; extra == "test-s3"
75
+ Requires-Dist: testcontainers<5,>=4.14; extra == "test-s3"
71
76
  Provides-Extra: test-mongodb
72
77
  Requires-Dist: hexkit[mongodb]; extra == "test-mongodb"
73
- Requires-Dist: testcontainers[mongo]<5,>=4.13; extra == "test-mongodb"
78
+ Requires-Dist: testcontainers[mongo]<5,>=4.14; extra == "test-mongodb"
74
79
  Provides-Extra: test-redis
75
80
  Requires-Dist: hexkit[redis]; extra == "test-redis"
76
- Requires-Dist: testcontainers[redis]<5,>=4.13; extra == "test-redis"
81
+ Requires-Dist: testcontainers[redis]<5,>=4.14; extra == "test-redis"
82
+ Provides-Extra: test-vault
83
+ Requires-Dist: hexkit[vault]; extra == "test-vault"
84
+ Requires-Dist: testcontainers<5,>=4.14; extra == "test-vault"
77
85
  Provides-Extra: test
78
- Requires-Dist: hexkit[test-akafka,test-mongodb,test-s3]; extra == "test"
86
+ Requires-Dist: hexkit[test-akafka,test-mongodb,test-s3,test-vault]; extra == "test"
79
87
  Provides-Extra: all
80
88
  Requires-Dist: hexkit[test]; extra == "all"
81
89
  Requires-Dist: hexkit[opentelemetry]; extra == "all"
@@ -1,6 +1,6 @@
1
1
  [build-system]
2
2
  requires = [
3
- "setuptools>=80.10",
3
+ "setuptools>=82",
4
4
  ]
5
5
  build-backend = "setuptools.build_meta"
6
6
 
@@ -23,7 +23,7 @@ classifiers = [
23
23
  "Intended Audience :: Developers",
24
24
  ]
25
25
  name = "hexkit"
26
- version = "8.0.0"
26
+ version = "8.2.0"
27
27
  description = "A Toolkit for Building Microservices using the Hexagonal Architecture"
28
28
  dependencies = [
29
29
  "opentelemetry-api >=1.39, <2",
@@ -37,7 +37,7 @@ text = "Apache 2.0"
37
37
 
38
38
  [project.optional-dependencies]
39
39
  akafka = [
40
- "aiokafka[zstd,lz4,snappy]~=0.13.0",
40
+ "aiokafka[zstd,lz4,snappy]~=0.14.0",
41
41
  "jsonschema >=4.25, <5",
42
42
  ]
43
43
  s3 = [
@@ -45,11 +45,14 @@ s3 = [
45
45
  "botocore >=1.42, <2",
46
46
  ]
47
47
  mongodb = [
48
- "pymongo >=4.15, <5",
48
+ "pymongo >=4.16, <5",
49
49
  ]
50
50
  redis = [
51
51
  "redis >=7.1, <8",
52
52
  ]
53
+ vault = [
54
+ "hvac >=2.4, <3",
55
+ ]
53
56
  opentelemetry-base = [
54
57
  "opentelemetry-sdk >=1.39.1, <2",
55
58
  "opentelemetry-exporter-otlp >=1.39.1, <2",
@@ -89,22 +92,26 @@ opentelemetry = [
89
92
  ]
90
93
  test-akafka = [
91
94
  "hexkit[akafka]",
92
- "testcontainers[kafka] >=4.13, <5",
95
+ "testcontainers[kafka] >=4.14, <5",
93
96
  ]
94
97
  test-s3 = [
95
98
  "hexkit[s3]",
96
- "testcontainers >=4.13, <5",
99
+ "testcontainers >=4.14, <5",
97
100
  ]
98
101
  test-mongodb = [
99
102
  "hexkit[mongodb]",
100
- "testcontainers[mongo] >=4.13, <5",
103
+ "testcontainers[mongo] >=4.14, <5",
101
104
  ]
102
105
  test-redis = [
103
106
  "hexkit[redis]",
104
- "testcontainers[redis] >=4.13, <5",
107
+ "testcontainers[redis] >=4.14, <5",
108
+ ]
109
+ test-vault = [
110
+ "hexkit[vault]",
111
+ "testcontainers >=4.14, <5",
105
112
  ]
106
113
  test = [
107
- "hexkit[test-akafka,test-s3,test-mongodb]",
114
+ "hexkit[test-akafka,test-s3,test-mongodb,test-vault]",
108
115
  ]
109
116
  all = [
110
117
  "hexkit[test]",
@@ -113,6 +120,9 @@ all = [
113
120
 
114
121
  [project.urls]
115
122
  Repository = "https://github.com/ghga-de/hexkit"
123
+ Documentation = "https://ghga-de.github.io/hexkit/"
124
+ "Release Notes" = "https://github.com/ghga-de/hexkit/releases"
125
+ "Issue Tracker" = "https://github.com/ghga-de/hexkit/issues"
116
126
 
117
127
  [tool.setuptools.packages.find]
118
128
  where = [
@@ -235,7 +245,7 @@ source = [
235
245
  [tool.tox]
236
246
  legacy_tox_ini = """
237
247
  [tox]
238
- env_list = py3{10,11,12,13}
248
+ env_list = py3{10,11,12,13}, docs
239
249
 
240
250
  [gh-actions]
241
251
  python =
@@ -244,6 +254,22 @@ legacy_tox_ini = """
244
254
  3.12: py312
245
255
  3.13: py313
246
256
 
257
+ [testenv:docs]
258
+ basepython = python3.13
259
+ allowlist_externals =
260
+ mv
261
+ rm
262
+ deps =
263
+ --no-deps -r ./lock/requirements-dev.txt
264
+ commands =
265
+ rm -rf site/api
266
+ pdoc -d google -o docs/api hexkit
267
+ mv docs/api/index.html docs/api/index.md
268
+ mkdocs build
269
+ mv docs/api/index.md docs/api/index.html
270
+ rm -rf site/api
271
+ mv docs/api site/api
272
+
247
273
  [testenv]
248
274
  pass_env =
249
275
  TC_HOST
@@ -251,6 +277,6 @@ legacy_tox_ini = """
251
277
  deps =
252
278
  --no-deps -r ./lock/requirements-dev.txt
253
279
  commands =
254
- py310: pip install --no-deps backports.asyncio.runner==1.2.0 exceptiongroup==1.3.0 tomli==2.2.1
280
+ py310: pip install --no-deps backports.asyncio.runner==1.2.0 exceptiongroup==1.3.1 tomli==2.2.1
255
281
  pytest {posargs}
256
282
  """
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,6 +29,8 @@ from pydantic_settings import (
29
29
 
30
30
  # Default config prefix:
31
31
  DEFAULT_CONFIG_PREFIX: Final = "ghga_services"
32
+ # Default dotenv path:
33
+ DEFAULT_DOTENV_PATH: Final[Path] = Path("/secrets")
32
34
 
33
35
 
34
36
  class ConfigYamlDoesNotExist(RuntimeError):
@@ -82,6 +84,7 @@ def get_default_config_yaml(prefix: str) -> Path | None:
82
84
 
83
85
  def config_from_yaml(
84
86
  prefix: str = DEFAULT_CONFIG_PREFIX,
87
+ dotenv_prefix: Path = DEFAULT_DOTENV_PATH,
85
88
  ) -> Callable:
86
89
  """A factory that returns decorator functions which extends a
87
90
  pydantic BaseSettings class to read in parameters from a config yaml.
@@ -102,21 +105,23 @@ def config_from_yaml(
102
105
  "{prefix}_{actual_variable_name}". Moreover, this prefix is used
103
106
  to derive the default location for the config yaml file
104
107
  ("~/.{prefix}.yaml"). Defaults to "ghga_services".
108
+ dotenv_prefix: (Path, optional):
109
+ When defining parameters via dotenv files, this prefix is used to set the path
110
+ for the dotenv files. The final path used is constructed as
111
+ "{dotenv_prefix}/.env". Defaults to "/secrets".
105
112
  """
106
113
 
107
114
  def decorator(settings) -> Callable:
108
- # settings should be a pydantic BaseSetting,
109
- # there is no type hint here to not restrict
110
- # autocompletion for attributes of the
111
- # modified settings class returned by the
112
- # contructor_wrapper
113
115
  """The actual decorator function.
114
116
 
115
117
  Args
116
118
  settings (BaseSettings):
117
119
  A pydantic BaseSettings class to be modified.
118
120
  """
119
- # check if settings inherits from pydantic BaseSettings:
121
+ # The settings argument should be a pydantic BaseSetting,
122
+ # there is no type hint here to not restrict autocompletion for attributes
123
+ # of the modified settings class returned by the constructor_wrapper.
124
+ # Check if settings inherits from pydantic BaseSettings:
120
125
  if not issubclass(settings, BaseSettings):
121
126
  raise TypeError(
122
127
  "The specified settings class is not a subclass of pydantic_settings.BaseSettings"
@@ -141,7 +146,12 @@ def config_from_yaml(
141
146
  class ModSettings(settings):
142
147
  """Modifies the original Settings class provided by the user"""
143
148
 
144
- model_config = SettingsConfigDict(frozen=True, env_prefix=f"{prefix}_")
149
+ model_config = SettingsConfigDict(
150
+ frozen=True,
151
+ env_prefix=f"{prefix}_",
152
+ env_file=(".env", dotenv_prefix / ".env"),
153
+ env_nested_delimiter="__",
154
+ )
145
155
 
146
156
  @classmethod
147
157
  def settings_customise_sources(
@@ -156,6 +166,7 @@ def config_from_yaml(
156
166
  return (
157
167
  init_settings,
158
168
  env_settings,
169
+ dotenv_settings,
159
170
  file_secret_settings,
160
171
  YamlConfigSettingsSource(settings_cls, config_yaml),
161
172
  )
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -178,6 +178,9 @@ class Dao(typing.Protocol[Dto]):
178
178
  Raises:
179
179
  ResourceNotFoundError:
180
180
  when resource with the id specified in the dto was not found
181
+ UniqueConstraintViolationError:
182
+ when updating the dto would violate a unique index constraint over some
183
+ field other than the ID field.
181
184
  """
182
185
  ...
183
186
 
@@ -250,6 +253,9 @@ class Dao(typing.Protocol[Dto]):
250
253
  Raises:
251
254
  ResourceAlreadyExistsError:
252
255
  when a resource with the ID specified in the dto does already exist.
256
+ UniqueConstraintViolationError:
257
+ when inserting the dto would violate a unique index constraint over some
258
+ field other than the ID field.
253
259
  """
254
260
  ...
255
261
 
@@ -260,6 +266,11 @@ class Dao(typing.Protocol[Dto]):
260
266
  dto:
261
267
  Resource content as a pydantic-based data transfer object including the
262
268
  resource ID.
269
+
270
+ Raises:
271
+ UniqueConstraintViolationError:
272
+ when upserting the dto would violate a unique index constraint over some
273
+ field other than the ID field.
263
274
  """
264
275
  ...
265
276
 
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -195,6 +195,38 @@ class ObjectStorageProtocol(ABC):
195
195
  bucket_id=bucket_id, object_id=object_id
196
196
  )
197
197
 
198
+ async def list_parts(
199
+ self,
200
+ *,
201
+ bucket_id: str,
202
+ object_id: str,
203
+ upload_id: str,
204
+ max_parts: int | None = None,
205
+ first_part_no: int | None = None,
206
+ ) -> list[dict]:
207
+ """Lists the parts that have been uploaded for a specific multipart upload.
208
+
209
+ Specify `max_parts` to return a limited parts list. If not specified, all
210
+ parts will be returned (up to 10,000).
211
+
212
+ Specify `first_part_no` to get parts starting with that part number. If not
213
+ specified, retrieved parts will start with the first part. Part numbers
214
+ start at 1, not 0.
215
+
216
+ Raises:
217
+ ValueError if `max_parts` or `first_part_no` are invalid.
218
+ MultiPartUploadNotFoundError if no upload with `upload_id` exists.
219
+ """
220
+ self._validate_bucket_id(bucket_id)
221
+ self._validate_object_id(object_id)
222
+ return await self._list_parts(
223
+ bucket_id=bucket_id,
224
+ object_id=object_id,
225
+ upload_id=upload_id,
226
+ max_parts=max_parts,
227
+ first_part_no=first_part_no,
228
+ )
229
+
198
230
  async def get_all_multipart_uploads(self, *, bucket_id: str) -> dict[str, str]:
199
231
  """Gets all active multipart uploads for the given bucket ID.
200
232
 
@@ -440,6 +472,31 @@ class ObjectStorageProtocol(ABC):
440
472
  """
441
473
  ...
442
474
 
475
+ @abstractmethod
476
+ async def _list_parts(
477
+ self,
478
+ *,
479
+ bucket_id: str,
480
+ object_id: str,
481
+ upload_id: str,
482
+ max_parts: int | None = None,
483
+ first_part_no: int | None = None,
484
+ ) -> list[dict]:
485
+ """Lists the parts that have been uploaded for a specific multipart upload.
486
+
487
+ Specify `max_parts` to return a limited parts list. If not specified, all
488
+ parts will be returned (up to 10,000).
489
+
490
+ Specify `first_part_no` to get parts starting with that part number. If not
491
+ specified, retrieved parts will start with the first part. Part numbers
492
+ start at 1, not 0.
493
+
494
+ Raises:
495
+ ValueError if `max_parts` or `first_part_no` are invalid.
496
+ MultiPartUploadNotFoundError if no upload with `upload_id` exists.
497
+ """
498
+ ...
499
+
443
500
  @abstractmethod
444
501
  async def _get_all_multipart_uploads(self, *, bucket_id: str) -> dict[str, str]:
445
502
  """Gets all active multipart uploads for the given bucket ID.
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,7 @@
19
19
  """
20
20
 
21
21
  import logging
22
- from collections.abc import Sequence
22
+ from collections.abc import AsyncGenerator, Sequence
23
23
  from contextlib import asynccontextmanager
24
24
 
25
25
  from aiokafka import AIOKafkaConsumer
@@ -57,7 +57,7 @@ class KafkaOutboxSubscriber(InboundProviderBase):
57
57
  translators: Sequence[DaoSubscriberProtocol],
58
58
  dlq_publisher: EventPublisherProtocol | None = None,
59
59
  kafka_consumer_cls: type[KafkaConsumerCompatible] = AIOKafkaConsumer,
60
- ):
60
+ ) -> AsyncGenerator["KafkaOutboxSubscriber", None]:
61
61
  """Setup and teardown an instance of the provider.
62
62
 
63
63
  Args:
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -331,9 +331,11 @@ class KafkaConsumerCompatible(Protocol):
331
331
  An identifier for the consumer group.
332
332
  auto_offset_reset:
333
333
  Can be set to "earliest".
334
- key_serializer:
334
+ enable_auto_commit:
335
+ Whether to enable auto-committing of offsets.
336
+ key_deserializer:
335
337
  Function to deserialize the keys into strings.
336
- value_serializer:
338
+ value_deserializer:
337
339
  Function to deserialize the values into strings.
338
340
  max_partition_fetch_bytes:
339
341
  Maximum receivable message size
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -644,11 +644,11 @@ class KafkaContainerFixture(KafkaContainer):
644
644
  """Run the given command in the kafka testcontainer.
645
645
 
646
646
  Args:
647
- - `command`: The full command to run.
648
- - `run_in_shell`: If True, will run the command in a shell.
647
+ command: The full command to run.
648
+ run_in_shell: If True, will run the command in a shell.
649
649
 
650
650
  Raises:
651
- - `RuntimeError`: when the exit code returned by the command is not zero.
651
+ RuntimeError: when the exit code returned by the command is not zero.
652
652
  """
653
653
  cmd = ["sh", "-c", command] if run_in_shell else command
654
654
  exit_code, output = self.get_wrapped_container().exec_run(cmd)
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
1
+ # Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
2
  # for the German Human Genome-Phenome Archive (GHGA)
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");