24 self.
_storage: Dict[pathlib.Path, S3Object] = {}
27 def _generate_etag(data):
28 return hashlib.md5(data).hexdigest()
34 user_defined_meta: Mapping[str, str] = {},
35 last_modified: Optional[Union[dt.datetime, str]] =
None,
37 key_path = pathlib.Path(key)
38 if last_modified
is None:
40 last_modified = dt.datetime.now().replace(tzinfo=tz.tzlocal()).isoformat()
41 elif isinstance(last_modified, dt.datetime):
42 last_modified = last_modified.isoformat()
47 'Last-Modified': last_modified,
48 'Size': str(sys.getsizeof(data)),
51 meta.update(user_defined_meta)
56 def get_object(self, key: str) -> Optional[S3Object]:
57 key_path = pathlib.Path(key)
60 def get_objects(self, parent_dir='') -> Dict[str, S3Object]:
61 all_objects = {str(key_path): value
for key_path, value
in self.
_storage.items()}
66 return {key: value
for key, value
in all_objects.items()
if key.startswith(str(pathlib.Path(parent_dir)))}
68 def delete_object(self, key) -> Optional[S3Object]:
69 key = pathlib.Path(key)
76 def __init__(self, mockserver, s3_mock_storage, mock_base_url):
81 def _get_bucket_name(self, request):
82 return request.headers[
'Host'].split(
'.')[0]
84 def _extract_key(self, request):
85 return request.path[len(self.
_base_url) + 1 :]
87 def _generate_get_objects_result(
89 s3_objects_dict: Dict[str, S3Object],
91 marker: Optional[str],
93 empty_result = {
'result_objects': [],
'is_truncated':
False}
94 keys = list(s3_objects_dict.keys())
100 if marker > keys[-1]:
102 for i, key
in enumerate(keys):
107 result_objects = [s3_objects_dict[key]
for key
in keys[from_index : from_index + max_keys]]
108 is_truncated = from_index + max_keys >= len(keys)
109 return {
'result_objects': result_objects,
'is_truncated': is_truncated}
111 def _generate_get_objects_xml(
113 s3_objects: List[S3Object],
116 max_keys: Optional[int],
117 marker: Optional[str],
121 for s3_object
in s3_objects:
124 <ETag>{s3_object.meta['ETag']}</ETag>
125 <Key>{s3_object.meta['Key']}</Key>
126 <LastModified>{s3_object.meta['Last-Modified']}</LastModified>
127 <Size>{s3_object.meta['Size']}</Size>
128 <StorageClass>STANDARD</StorageClass>
132 <?xml version="1.0" encoding="UTF-8"?>
134 <Name>{bucket_name}</Name>
135 <Prefix>{prefix}</Prefix>
136 <Marker>{marker or ''}</Marker>
137 <MaxKeys>{max_keys or ''}</MaxKeys>
138 <IsTruncated>{is_truncated}</IsTruncated>
143 def get_object(self, request):
148 s3_object = bucket_storage.get_object(key)
150 return self.
_mockserver.make_response(
'Object not found', 404)
154 headers=s3_object.meta,
157 def put_object(self, request):
162 data = request.get_data()
164 user_defined_meta = {}
165 for meta_key, meta_value
in request.headers.items():
167 if meta_key.startswith(
'x-amz-meta-'):
168 user_defined_meta[meta_key] = meta_value
170 meta = bucket_storage.put_object(key, data, user_defined_meta)
172 return self.
_mockserver.make_response(
'', 200, headers=meta)
174 def copy_object(self, request):
177 source_bucket_name, source_key = request.headers.get(
181 src_bucket_storage = self.
_storage[source_bucket_name]
182 dst_bucket_storage = self.
_storage[dest_bucket_name]
184 src_obj = src_bucket_storage.get_object(source_key)
185 src_data = src_obj.data
186 src_meta = src_obj.meta
187 meta = dst_bucket_storage.put_object(key, src_data, src_meta)
189 return self.
_mockserver.make_response(
'', 200, headers=meta)
191 def get_objects(self, request):
192 prefix = request.query[
'prefix']
194 max_keys = int(request.query.get(
'max-keys', 1000))
195 marker = request.query.get(
'marker')
198 bucket_storage = self.
_storage[bucket_name]
200 s3_objects_dict = bucket_storage.get_objects(parent_dir=prefix)
202 s3_objects_dict=s3_objects_dict,
207 s3_objects=result[
'result_objects'],
208 bucket_name=bucket_name,
212 is_truncated=result[
'is_truncated'],
214 return self.
_mockserver.make_response(result_xml, 200)
216 def delete_object(self, request):
221 bucket_storage.delete_object(key)
226 def get_object_head(self, request):
231 s3_object = bucket_storage.get_object(key)
233 return self.
_mockserver.make_response(
'Object not found', 404)
238 headers=s3_object.meta,