hexkit 3.4.0__tar.gz → 3.5.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.
- {hexkit-3.4.0/src/hexkit.egg-info → hexkit-3.5.0}/PKG-INFO +1 -1
- {hexkit-3.4.0 → hexkit-3.5.0}/pyproject.toml +1 -1
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/config.py +6 -29
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/protocols/dao.py +1 -1
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/akafka/config.py +7 -1
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/akafka/provider/eventpub.py +4 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/akafka/provider/eventsub.py +4 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/mongodb/provider.py +6 -1
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/mongokafka/provider.py +12 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/s3/testutils/__init__.py +32 -3
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/testing/eventpub.py +2 -0
- {hexkit-3.4.0 → hexkit-3.5.0/src/hexkit.egg-info}/PKG-INFO +1 -1
- {hexkit-3.4.0 → hexkit-3.5.0}/LICENSE +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/MANIFEST.in +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/README.md +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/setup.cfg +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/__init__.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/__main__.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/base.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/correlation.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/custom_types.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/log.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/protocols/__init__.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/protocols/daopub.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/protocols/daosub.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/protocols/eventpub.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/protocols/eventsub.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/protocols/objstorage.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/__init__.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/akafka/__init__.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/akafka/provider/__init__.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/akafka/provider/daosub.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/akafka/provider/utils.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/akafka/testcontainer.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/akafka/testutils.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/mongodb/__init__.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/mongodb/testutils.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/mongokafka/__init__.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/mongokafka/testutils.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/s3/__init__.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/s3/provider.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/s3/test_files/__init__.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/s3/test_files/test_file1.yaml +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/s3/test_files/test_file2.yaml +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/s3/test_files/test_file3.yaml +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/s3/test_files/test_file4.yaml +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/s3/testutils/_fixtures.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/s3/testutils/_typical_workflow.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/s3/testutils/_utils.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/providers/testing/__init__.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/py.typed +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit/utils.py +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit.egg-info/SOURCES.txt +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit.egg-info/dependency_links.txt +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit.egg-info/requires.txt +0 -0
- {hexkit-3.4.0 → hexkit-3.5.0}/src/hexkit.egg-info/top_level.txt +0 -0
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
|
|
18
18
|
import os
|
|
19
19
|
from pathlib import Path
|
|
20
|
-
from typing import
|
|
20
|
+
from typing import Callable, Final, Optional
|
|
21
21
|
|
|
22
|
-
import yaml
|
|
23
22
|
from pydantic_settings import (
|
|
24
23
|
BaseSettings,
|
|
25
24
|
PydanticBaseSettingsSource,
|
|
26
25
|
SettingsConfigDict,
|
|
26
|
+
YamlConfigSettingsSource,
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
# Default config prefix:
|
|
@@ -79,29 +79,6 @@ def get_default_config_yaml(prefix: str) -> Optional[Path]:
|
|
|
79
79
|
return None
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
def yaml_settings_factory(
|
|
83
|
-
config_yaml: Optional[Path] = None,
|
|
84
|
-
) -> Callable[[], dict[str, Any]]:
|
|
85
|
-
"""
|
|
86
|
-
A factory of source methods for pydantic's BaseSettings Config that load
|
|
87
|
-
settings from a yaml file.
|
|
88
|
-
|
|
89
|
-
Args:
|
|
90
|
-
config_yaml (str, Optional):
|
|
91
|
-
Path to the yaml file to read from.
|
|
92
|
-
"""
|
|
93
|
-
|
|
94
|
-
def yaml_settings() -> dict[str, Any]:
|
|
95
|
-
"""Source method for loading pydantic BaseSettings from a yaml file"""
|
|
96
|
-
if config_yaml is None:
|
|
97
|
-
return {}
|
|
98
|
-
|
|
99
|
-
with open(config_yaml, encoding="utf8") as yaml_file:
|
|
100
|
-
return yaml.safe_load(yaml_file)
|
|
101
|
-
|
|
102
|
-
return yaml_settings
|
|
103
|
-
|
|
104
|
-
|
|
105
82
|
def config_from_yaml(
|
|
106
83
|
prefix: str = DEFAULT_CONFIG_PREFIX,
|
|
107
84
|
) -> Callable:
|
|
@@ -119,7 +96,7 @@ def config_from_yaml(
|
|
|
119
96
|
|
|
120
97
|
Args:
|
|
121
98
|
prefix: (str, optional):
|
|
122
|
-
When defining parameters via
|
|
99
|
+
When defining parameters via environment variables, all variables
|
|
123
100
|
have to be prefixed with this string following this pattern
|
|
124
101
|
"{prefix}_{actual_variable_name}". Moreover, this prefix is used
|
|
125
102
|
to derive the default location for the config yaml file
|
|
@@ -161,7 +138,7 @@ def config_from_yaml(
|
|
|
161
138
|
raise ConfigYamlDoesNotExist(path=config_yaml)
|
|
162
139
|
|
|
163
140
|
class ModSettings(settings):
|
|
164
|
-
"""Modifies the
|
|
141
|
+
"""Modifies the original Settings class provided by the user"""
|
|
165
142
|
|
|
166
143
|
model_config = SettingsConfigDict(frozen=True, env_prefix=f"{prefix}_")
|
|
167
144
|
|
|
@@ -174,12 +151,12 @@ def config_from_yaml(
|
|
|
174
151
|
dotenv_settings: PydanticBaseSettingsSource,
|
|
175
152
|
file_secret_settings: PydanticBaseSettingsSource,
|
|
176
153
|
):
|
|
177
|
-
"""Add custom yaml source"""
|
|
154
|
+
"""Add custom yaml source, but maintain the priority of sources."""
|
|
178
155
|
return (
|
|
179
156
|
init_settings,
|
|
180
157
|
env_settings,
|
|
181
158
|
file_secret_settings,
|
|
182
|
-
|
|
159
|
+
YamlConfigSettingsSource(settings_cls, config_yaml),
|
|
183
160
|
)
|
|
184
161
|
|
|
185
162
|
# construct settings class:
|
|
@@ -61,7 +61,7 @@ class ResourceAlreadyExistsError(RuntimeError):
|
|
|
61
61
|
"""Raised when a resource did unexpectedly exist."""
|
|
62
62
|
|
|
63
63
|
def __init__(self, *, id_: str):
|
|
64
|
-
message = f'The resource with the id "{id_}"
|
|
64
|
+
message = f'The resource with the id "{id_}" already exists.'
|
|
65
65
|
super().__init__(message)
|
|
66
66
|
|
|
67
67
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
from typing import Literal
|
|
20
20
|
|
|
21
|
-
from pydantic import Field, SecretStr
|
|
21
|
+
from pydantic import Field, PositiveInt, SecretStr
|
|
22
22
|
from pydantic_settings import BaseSettings
|
|
23
23
|
|
|
24
24
|
|
|
@@ -76,3 +76,9 @@ class KafkaConfig(BaseSettings):
|
|
|
76
76
|
+ " newly correlation ID will be generated and used in the event header."
|
|
77
77
|
),
|
|
78
78
|
)
|
|
79
|
+
kafka_max_message_size: PositiveInt = Field(
|
|
80
|
+
default=1024 * 1024, # 1 MiB
|
|
81
|
+
description="The largest message size that can be transmitted, in bytes. Only"
|
|
82
|
+
+ " services that have a need to send/receive larger messages should set this.",
|
|
83
|
+
examples=[1024 * 1024, 16 * 1024 * 1024],
|
|
84
|
+
)
|
|
@@ -54,6 +54,7 @@ class KafkaProducerCompatible(Protocol):
|
|
|
54
54
|
client_id: str,
|
|
55
55
|
key_serializer: Callable[[Any], bytes],
|
|
56
56
|
value_serializer: Callable[[Any], bytes],
|
|
57
|
+
max_request_size: int,
|
|
57
58
|
):
|
|
58
59
|
"""
|
|
59
60
|
Initialize the producer with some config params.
|
|
@@ -67,6 +68,8 @@ class KafkaProducerCompatible(Protocol):
|
|
|
67
68
|
Function to serialize the keys into bytes.
|
|
68
69
|
value_serializer:
|
|
69
70
|
Function to serialize the values into bytes.
|
|
71
|
+
max_request_size:
|
|
72
|
+
Maximum sendable message size.
|
|
70
73
|
"""
|
|
71
74
|
...
|
|
72
75
|
|
|
@@ -116,6 +119,7 @@ class KafkaEventPublisher(EventPublisherProtocol):
|
|
|
116
119
|
value_serializer=lambda event_value: json.dumps(event_value).encode(
|
|
117
120
|
"ascii"
|
|
118
121
|
),
|
|
122
|
+
max_request_size=config.kafka_max_message_size,
|
|
119
123
|
)
|
|
120
124
|
|
|
121
125
|
try:
|
|
@@ -90,6 +90,7 @@ class KafkaConsumerCompatible(Protocol):
|
|
|
90
90
|
enable_auto_commit: bool,
|
|
91
91
|
key_deserializer: Callable[[bytes], str],
|
|
92
92
|
value_deserializer: Callable[[bytes], str],
|
|
93
|
+
max_partition_fetch_bytes: int,
|
|
93
94
|
):
|
|
94
95
|
"""
|
|
95
96
|
Initialize the consumer with some config params.
|
|
@@ -108,6 +109,8 @@ class KafkaConsumerCompatible(Protocol):
|
|
|
108
109
|
Function to deserialize the keys into strings.
|
|
109
110
|
value_serializer:
|
|
110
111
|
Function to deserialize the values into strings.
|
|
112
|
+
max_partition_fetch_bytes:
|
|
113
|
+
Maximum receivable message size
|
|
111
114
|
"""
|
|
112
115
|
...
|
|
113
116
|
|
|
@@ -176,6 +179,7 @@ class KafkaEventSubscriber(InboundProviderBase):
|
|
|
176
179
|
value_deserializer=lambda event_value: json.loads(
|
|
177
180
|
event_value.decode("ascii")
|
|
178
181
|
),
|
|
182
|
+
max_partition_fetch_bytes=config.kafka_max_message_size,
|
|
179
183
|
)
|
|
180
184
|
try:
|
|
181
185
|
await consumer.start()
|
|
@@ -31,6 +31,7 @@ from motor.core import AgnosticCollection
|
|
|
31
31
|
from motor.motor_asyncio import AsyncIOMotorClient
|
|
32
32
|
from pydantic import Field, SecretStr
|
|
33
33
|
from pydantic_settings import BaseSettings
|
|
34
|
+
from pymongo.errors import DuplicateKeyError
|
|
34
35
|
|
|
35
36
|
from hexkit.protocols.dao import (
|
|
36
37
|
DaoFactoryProtocol,
|
|
@@ -42,6 +43,7 @@ from hexkit.protocols.dao import (
|
|
|
42
43
|
InvalidFindMappingError,
|
|
43
44
|
MultipleHitsFoundError,
|
|
44
45
|
NoHitsFoundError,
|
|
46
|
+
ResourceAlreadyExistsError,
|
|
45
47
|
ResourceNotFoundError,
|
|
46
48
|
)
|
|
47
49
|
from hexkit.utils import FieldNotInModelError, validate_fields_in_model
|
|
@@ -386,7 +388,10 @@ class MongoDbDaoNaturalId(MongoDbDaoBase[Dto]):
|
|
|
386
388
|
when a resource with the ID specified in the dto does already exist.
|
|
387
389
|
"""
|
|
388
390
|
document = self._dto_to_document(dto)
|
|
389
|
-
|
|
391
|
+
try:
|
|
392
|
+
await self._collection.insert_one(document)
|
|
393
|
+
except DuplicateKeyError as error:
|
|
394
|
+
raise ResourceAlreadyExistsError(id_=document["_id"]) from error
|
|
390
395
|
|
|
391
396
|
async def upsert(self, dto: Dto) -> None:
|
|
392
397
|
"""Update the provided resource if it already exists, create it otherwise.
|
|
@@ -20,6 +20,7 @@ Require dependencies of the `akafka` and `mongodb` extras.
|
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
import json
|
|
23
|
+
import logging
|
|
23
24
|
from collections.abc import AsyncIterator, Awaitable, Collection, Mapping
|
|
24
25
|
from contextlib import AbstractAsyncContextManager, asynccontextmanager, contextmanager
|
|
25
26
|
from typing import Any, Callable, Generic, Optional
|
|
@@ -27,6 +28,7 @@ from typing import Any, Callable, Generic, Optional
|
|
|
27
28
|
from aiokafka import AIOKafkaProducer
|
|
28
29
|
from motor.core import AgnosticCollection
|
|
29
30
|
from motor.motor_asyncio import AsyncIOMotorClient
|
|
31
|
+
from pydantic import field_validator
|
|
30
32
|
|
|
31
33
|
from hexkit.correlation import get_correlation_id, set_correlation_id
|
|
32
34
|
from hexkit.custom_types import JsonObject
|
|
@@ -396,6 +398,16 @@ class MongoKafkaDaoPublisher(Generic[Dto]):
|
|
|
396
398
|
class MongoKafkaConfig(MongoDbConfig, KafkaConfig):
|
|
397
399
|
"""Config parameters and their defaults."""
|
|
398
400
|
|
|
401
|
+
@field_validator("kafka_max_message_size", mode="after")
|
|
402
|
+
@classmethod
|
|
403
|
+
def validate_max_message_size(cls, value: int) -> int:
|
|
404
|
+
"""Validate the maximum message size."""
|
|
405
|
+
if value > 2**24: # 16 MiB
|
|
406
|
+
logging.warning(
|
|
407
|
+
f"Max message size ({value}) exceeds the 16 MiB document size limit for MongoDB!"
|
|
408
|
+
)
|
|
409
|
+
return value
|
|
410
|
+
|
|
399
411
|
|
|
400
412
|
class MongoKafkaDaoPublisherFactory(DaoPublisherFactoryProtocol):
|
|
401
413
|
"""A provider implementing the DaoPublisherFactoryProtocol based on MongoDB and
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
"""S3-related test fixtures and support functions."""
|
|
17
17
|
|
|
18
|
-
from ._fixtures import (
|
|
18
|
+
from ._fixtures import (
|
|
19
19
|
LOCALSTACK_IMAGE,
|
|
20
20
|
MEBIBYTE,
|
|
21
21
|
TEST_FILE_DIR,
|
|
@@ -36,8 +36,8 @@ from ._fixtures import ( # noqa: F401
|
|
|
36
36
|
tmp_file,
|
|
37
37
|
upload_file,
|
|
38
38
|
)
|
|
39
|
-
from ._typical_workflow import typical_workflow
|
|
40
|
-
from ._utils import (
|
|
39
|
+
from ._typical_workflow import typical_workflow
|
|
40
|
+
from ._utils import (
|
|
41
41
|
check_part_size,
|
|
42
42
|
download_and_check_test_file,
|
|
43
43
|
multipart_upload_file,
|
|
@@ -45,3 +45,32 @@ from ._utils import ( # noqa: F401
|
|
|
45
45
|
upload_part_of_size,
|
|
46
46
|
upload_part_via_url,
|
|
47
47
|
)
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
"LOCALSTACK_IMAGE",
|
|
51
|
+
"MEBIBYTE",
|
|
52
|
+
"TEST_FILE_DIR",
|
|
53
|
+
"TEST_FILE_PATHS",
|
|
54
|
+
"FileObject",
|
|
55
|
+
"S3ContainerFixture",
|
|
56
|
+
"S3Fixture",
|
|
57
|
+
"calc_md5",
|
|
58
|
+
"clean_s3_fixture",
|
|
59
|
+
"get_clean_s3_fixture",
|
|
60
|
+
"get_persistent_s3_fixture",
|
|
61
|
+
"get_s3_container_fixture",
|
|
62
|
+
"persistent_s3_fixture",
|
|
63
|
+
"populate_storage",
|
|
64
|
+
"s3_container_fixture",
|
|
65
|
+
"s3_fixture",
|
|
66
|
+
"temp_file_object",
|
|
67
|
+
"tmp_file",
|
|
68
|
+
"upload_file",
|
|
69
|
+
"typical_workflow",
|
|
70
|
+
"check_part_size",
|
|
71
|
+
"download_and_check_test_file",
|
|
72
|
+
"multipart_upload_file",
|
|
73
|
+
"upload_part",
|
|
74
|
+
"upload_part_of_size",
|
|
75
|
+
"upload_part_via_url",
|
|
76
|
+
]
|
|
@@ -26,6 +26,8 @@ from typing import NamedTuple, Optional
|
|
|
26
26
|
from hexkit.custom_types import JsonObject
|
|
27
27
|
from hexkit.protocols.eventpub import EventPublisherProtocol
|
|
28
28
|
|
|
29
|
+
__all__ = ["InMemEventStore", "InMemEventPublisher", "TopicExhaustedError"]
|
|
30
|
+
|
|
29
31
|
|
|
30
32
|
class TopicExhaustedError(RuntimeError):
|
|
31
33
|
"""Thrown when now more event are queued in a topic."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|