hexkit 3.3.0__tar.gz → 3.4.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.3.0/src/hexkit.egg-info → hexkit-3.4.0}/PKG-INFO +1 -1
- {hexkit-3.3.0 → hexkit-3.4.0}/pyproject.toml +1 -1
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/protocols/objstorage.py +21 -18
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/akafka/testutils.py +1 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/mongodb/testutils.py +16 -10
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/s3/testutils/_fixtures.py +57 -11
- {hexkit-3.3.0 → hexkit-3.4.0/src/hexkit.egg-info}/PKG-INFO +1 -1
- {hexkit-3.3.0 → hexkit-3.4.0}/LICENSE +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/MANIFEST.in +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/README.md +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/setup.cfg +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/__init__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/__main__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/base.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/config.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/correlation.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/custom_types.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/log.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/protocols/__init__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/protocols/dao.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/protocols/daopub.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/protocols/daosub.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/protocols/eventpub.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/protocols/eventsub.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/__init__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/akafka/__init__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/akafka/config.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/akafka/provider/__init__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/akafka/provider/daosub.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/akafka/provider/eventpub.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/akafka/provider/eventsub.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/akafka/provider/utils.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/akafka/testcontainer.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/mongodb/__init__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/mongodb/provider.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/mongokafka/__init__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/mongokafka/provider.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/mongokafka/testutils.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/s3/__init__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/s3/provider.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/s3/test_files/__init__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/s3/test_files/test_file1.yaml +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/s3/test_files/test_file2.yaml +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/s3/test_files/test_file3.yaml +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/s3/test_files/test_file4.yaml +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/s3/testutils/__init__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/s3/testutils/_typical_workflow.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/s3/testutils/_utils.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/testing/__init__.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/providers/testing/eventpub.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/py.typed +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit/utils.py +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit.egg-info/SOURCES.txt +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit.egg-info/dependency_links.txt +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit.egg-info/requires.txt +0 -0
- {hexkit-3.3.0 → hexkit-3.4.0}/src/hexkit.egg-info/top_level.txt +0 -0
|
@@ -457,7 +457,10 @@ class ObjectStorageProtocol(ABC):
|
|
|
457
457
|
|
|
458
458
|
# Validation logic for input parameter:
|
|
459
459
|
# (is typically only used by the protocol but may also be used in
|
|
460
|
-
# provider-specific code)
|
|
460
|
+
# provider-specific code or overwritten by the provider)
|
|
461
|
+
|
|
462
|
+
_re_bucket_id = re.compile(r"^[a-z0-9\-]{3,63}$")
|
|
463
|
+
_re_bucket_id_msg = "must consist of 3-63 lowercase letters, digits or hyphens"
|
|
461
464
|
|
|
462
465
|
@classmethod
|
|
463
466
|
def _validate_bucket_id(cls, bucket_id: str):
|
|
@@ -466,43 +469,43 @@ class ObjectStorageProtocol(ABC):
|
|
|
466
469
|
https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html
|
|
467
470
|
Raises BucketIdValidationError if not valid.
|
|
468
471
|
"""
|
|
469
|
-
if not
|
|
472
|
+
if not cls._re_bucket_id.match(bucket_id):
|
|
470
473
|
raise cls.BucketIdValidationError(
|
|
471
474
|
bucket_id=bucket_id,
|
|
472
|
-
reason=
|
|
473
|
-
)
|
|
474
|
-
if not re.match(r"^[a-z0-9\-]*$", bucket_id):
|
|
475
|
-
raise cls.BucketIdValidationError(
|
|
476
|
-
bucket_id=bucket_id,
|
|
477
|
-
reason="only lowercase letters, digits and hyphens (-) are allowed",
|
|
475
|
+
reason=cls._re_bucket_id_msg,
|
|
478
476
|
)
|
|
479
477
|
if bucket_id.startswith("-") or bucket_id.endswith("-"):
|
|
480
478
|
raise cls.BucketIdValidationError(
|
|
481
479
|
bucket_id=bucket_id,
|
|
482
|
-
reason="may not start or end with a hyphen
|
|
480
|
+
reason="may not start or end with a hyphen",
|
|
483
481
|
)
|
|
484
482
|
|
|
483
|
+
_re_object_id = re.compile(r"^[a-zA-Z0-9\-_.]{3,255}$")
|
|
484
|
+
_re_object_id_msg = (
|
|
485
|
+
"must consist of 3-255 letters, digits, hyphens, underscores or dots"
|
|
486
|
+
)
|
|
487
|
+
|
|
485
488
|
@classmethod
|
|
486
489
|
def _validate_object_id(cls, object_id: str):
|
|
487
|
-
"""Check whether
|
|
490
|
+
"""Check whether an object id follows the recommended naming pattern.
|
|
488
491
|
This is roughly based on (plus some additional restrictions):
|
|
489
492
|
https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html
|
|
490
493
|
Raises ObjectIdValidationError if not valid.
|
|
494
|
+
|
|
495
|
+
Note that the default implementation does not allow object IDs to contain
|
|
496
|
+
forward slashes (/) which are sometimes used to represent directories.
|
|
497
|
+
Providers can override this method or the above constants to allow slashes,
|
|
498
|
+
other characters or longer object names.
|
|
491
499
|
"""
|
|
492
|
-
if not
|
|
493
|
-
raise cls.ObjectIdValidationError(
|
|
494
|
-
object_id=object_id,
|
|
495
|
-
reason="must be between 3 and 63 characters long",
|
|
496
|
-
)
|
|
497
|
-
if not re.match(r"^[a-zA-Z0-9\-\.]*$", object_id):
|
|
500
|
+
if not cls._re_object_id.match(object_id):
|
|
498
501
|
raise cls.ObjectIdValidationError(
|
|
499
502
|
object_id=object_id,
|
|
500
|
-
reason=
|
|
503
|
+
reason=cls._re_object_id_msg,
|
|
501
504
|
)
|
|
502
505
|
if object_id.startswith(("-", ".")) or object_id.endswith(("-", ".")):
|
|
503
506
|
raise cls.ObjectIdValidationError(
|
|
504
507
|
object_id=object_id,
|
|
505
|
-
reason="may not start or end with a hyphen
|
|
508
|
+
reason="object names may not start or end with a hyphen or a dot",
|
|
506
509
|
)
|
|
507
510
|
|
|
508
511
|
# Exceptions that may be used by implementation:
|
|
@@ -61,24 +61,30 @@ class MongoDbFixture:
|
|
|
61
61
|
|
|
62
62
|
def empty_collections(
|
|
63
63
|
self,
|
|
64
|
+
*,
|
|
65
|
+
collections: Optional[Union[str, list[str]]] = None,
|
|
64
66
|
exclude_collections: Optional[Union[str, list[str]]] = None,
|
|
65
67
|
):
|
|
66
|
-
"""Drop
|
|
68
|
+
"""Drop the given mongodb collection(s) in the database.
|
|
67
69
|
|
|
70
|
+
If no collections are specified, all collections will be dropped.
|
|
68
71
|
You can also specify collection(s) that should be excluded
|
|
69
72
|
from the operation, i.e. collections that should be kept.
|
|
70
73
|
"""
|
|
71
74
|
db_name = self.config.db_name
|
|
72
|
-
if exclude_collections is None:
|
|
73
|
-
exclude_collections = []
|
|
74
|
-
if isinstance(exclude_collections, str):
|
|
75
|
-
exclude_collections = [exclude_collections]
|
|
76
|
-
excluded_collections = set(exclude_collections)
|
|
77
75
|
try:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
if collections is None:
|
|
77
|
+
collections = self.client[db_name].list_collection_names()
|
|
78
|
+
elif isinstance(collections, str):
|
|
79
|
+
collections = [collections]
|
|
80
|
+
if exclude_collections is None:
|
|
81
|
+
exclude_collections = []
|
|
82
|
+
elif isinstance(exclude_collections, str):
|
|
83
|
+
exclude_collections = [exclude_collections]
|
|
84
|
+
excluded_collections = set(exclude_collections)
|
|
85
|
+
for collection in collections:
|
|
86
|
+
if collection not in excluded_collections:
|
|
87
|
+
self.client[db_name].drop_collection(collection)
|
|
82
88
|
except (ExecutionTimeout, OperationFailure) as error:
|
|
83
89
|
raise RuntimeError(
|
|
84
90
|
f"Could not drop collection(s) of Mongo database {db_name}"
|
|
@@ -24,7 +24,7 @@ from collections.abc import AsyncGenerator, Generator
|
|
|
24
24
|
from contextlib import contextmanager
|
|
25
25
|
from pathlib import Path
|
|
26
26
|
from tempfile import NamedTemporaryFile
|
|
27
|
-
from typing import Any, NamedTuple, Optional
|
|
27
|
+
from typing import Any, NamedTuple, Optional, Union
|
|
28
28
|
|
|
29
29
|
try:
|
|
30
30
|
from typing import Self
|
|
@@ -93,11 +93,10 @@ class S3Fixture:
|
|
|
93
93
|
self.config = config
|
|
94
94
|
self.storage = storage
|
|
95
95
|
|
|
96
|
-
def get_buckets(self) ->
|
|
96
|
+
def get_buckets(self) -> list[str]:
|
|
97
97
|
"""Return a list of the buckets currently existing in the S3 object storage."""
|
|
98
98
|
response = self.storage._client.list_buckets()
|
|
99
|
-
|
|
100
|
-
return buckets
|
|
99
|
+
return [bucket["Name"] for bucket in response["Buckets"]]
|
|
101
100
|
|
|
102
101
|
async def populate_buckets(self, buckets: list[str]):
|
|
103
102
|
"""Populate the storage with buckets."""
|
|
@@ -111,19 +110,66 @@ class S3Fixture:
|
|
|
111
110
|
self.storage, bucket_fixtures=[], object_fixtures=file_objects
|
|
112
111
|
)
|
|
113
112
|
|
|
114
|
-
async def empty_buckets(
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
async def empty_buckets(
|
|
114
|
+
self,
|
|
115
|
+
*,
|
|
116
|
+
buckets: Optional[Union[str, list[str]]] = None,
|
|
117
|
+
exclude_buckets: Optional[Union[str, list[str]]] = None,
|
|
118
|
+
):
|
|
119
|
+
"""Remove all test objects from the given bucket(s).
|
|
120
|
+
|
|
121
|
+
If no buckets are specified, all existing buckets will be emptied,
|
|
122
|
+
therefore we recommend to always specify a bucket list.
|
|
123
|
+
You can also specify bucket(s) that should be excluded
|
|
124
|
+
from the operation, i.e. buckets that should not be emptied.
|
|
125
|
+
"""
|
|
126
|
+
if buckets is None:
|
|
127
|
+
buckets = self.get_buckets()
|
|
128
|
+
elif isinstance(buckets, str):
|
|
129
|
+
buckets = [buckets]
|
|
130
|
+
if exclude_buckets is None:
|
|
131
|
+
exclude_buckets = []
|
|
132
|
+
elif isinstance(exclude_buckets, str):
|
|
133
|
+
exclude_buckets = [exclude_buckets]
|
|
134
|
+
excluded_buckets = set(exclude_buckets)
|
|
135
|
+
for bucket in buckets:
|
|
136
|
+
if bucket in excluded_buckets:
|
|
137
|
+
continue
|
|
117
138
|
# Get list of all objects in the bucket
|
|
118
139
|
object_ids = await self.storage.list_all_object_ids(bucket_id=bucket)
|
|
119
140
|
# Delete all of these objects
|
|
120
141
|
for object_id in object_ids:
|
|
142
|
+
# Note that id validation errors can be raised here if the bucket
|
|
143
|
+
# was populated by other means than the S3 storage fixture.
|
|
144
|
+
# We intentionally do not catch these errors because they
|
|
145
|
+
# usually mean that you're operating on the wrong buckets.
|
|
121
146
|
await self.storage.delete_object(bucket_id=bucket, object_id=object_id)
|
|
122
147
|
|
|
123
|
-
async def delete_buckets(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
148
|
+
async def delete_buckets(
|
|
149
|
+
self,
|
|
150
|
+
*,
|
|
151
|
+
buckets: Optional[Union[str, list[str]]] = None,
|
|
152
|
+
exclude_buckets: Optional[Union[str, list[str]]] = None,
|
|
153
|
+
):
|
|
154
|
+
"""Delete the given bucket(s).
|
|
155
|
+
|
|
156
|
+
If no buckets are specified, all existing buckets will be deleted,
|
|
157
|
+
therefore we recommend to always specify a bucket list.
|
|
158
|
+
You can also specify bucket(s) that should be excluded
|
|
159
|
+
from the operation, i.e. buckets that should be kept.
|
|
160
|
+
"""
|
|
161
|
+
if buckets is None:
|
|
162
|
+
buckets = self.get_buckets()
|
|
163
|
+
elif isinstance(buckets, str):
|
|
164
|
+
buckets = [buckets]
|
|
165
|
+
if exclude_buckets is None:
|
|
166
|
+
exclude_buckets = []
|
|
167
|
+
elif isinstance(exclude_buckets, str):
|
|
168
|
+
exclude_buckets = [exclude_buckets]
|
|
169
|
+
excluded_buckets = set(exclude_buckets)
|
|
170
|
+
for bucket in buckets:
|
|
171
|
+
if bucket not in excluded_buckets:
|
|
172
|
+
await self.storage.delete_bucket(bucket, delete_content=True)
|
|
127
173
|
|
|
128
174
|
async def get_initialized_upload(self) -> UploadDetails:
|
|
129
175
|
"""Initialize a new empty multipart upload process.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|