muninn 7.0.2__tar.gz → 7.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.
- {muninn-7.0.2 → muninn-7.2.0}/CHANGES +11 -0
- {muninn-7.0.2 → muninn-7.2.0}/PKG-INFO +1 -1
- {muninn-7.0.2 → muninn-7.2.0}/muninn/__init__.py +1 -1
- {muninn-7.0.2 → muninn-7.2.0}/muninn/archive.py +41 -5
- {muninn-7.0.2 → muninn-7.2.0}/muninn/remote.py +9 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/pull.py +4 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn.egg-info/PKG-INFO +1 -1
- {muninn-7.0.2 → muninn-7.2.0}/setup.py +1 -1
- {muninn-7.0.2 → muninn-7.2.0}/DEVELOPER.md +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/LICENSE +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/MANIFEST.in +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/README.md +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/_compat.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/config.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/core.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/database/__init__.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/database/base.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/database/blobgeometry.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/database/ewkb.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/database/postgresql.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/database/sql.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/database/sqlite.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/enum.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/exceptions.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/extension.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/function.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/geometry.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/language.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/schema.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/storage/__init__.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/storage/base.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/storage/fs.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/storage/s3.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/storage/swift.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/struct.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/__init__.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/attach.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/destroy.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/export.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/hash.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/info.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/ingest.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/list_tags.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/prepare.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/remove.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/retrieve.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/search.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/strip.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/summary.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/sync.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/tag.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/untag.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/update.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/tools/utils.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/util.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn/visitor.py +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn.egg-info/SOURCES.txt +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn.egg-info/dependency_links.txt +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn.egg-info/entry_points.txt +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/muninn.egg-info/top_level.txt +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/setup.cfg +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/test/coverage.sh +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/test/data/README +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/test/data/multi/1.txt +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/test/data/multi/2.txt +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/test/data/pi.txt +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/test/product_type.py.template +0 -0
- {muninn-7.0.2 → muninn-7.2.0}/test/test.py +0 -0
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
7.2.0 2025-10-29
|
|
2
|
+
~~~~~~~~~~~~~~~~
|
|
3
|
+
|
|
4
|
+
* Added support for defining s3fs mappings in the muninn archive config
|
|
5
|
+
for mapping 'remote_url' s3 urls (mostly useful when storage=none).
|
|
6
|
+
|
|
7
|
+
7.1.0 2025-10-20
|
|
8
|
+
~~~~~~~~~~~~~~~~
|
|
9
|
+
|
|
10
|
+
* Added --update-properties option to muninn-pull.
|
|
11
|
+
|
|
1
12
|
7.0.2 2025-10-10
|
|
2
13
|
~~~~~~~~~~~~~~~~
|
|
3
14
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
from __future__ import absolute_import, division, print_function
|
|
6
6
|
|
|
7
|
-
__version__ = "7.0
|
|
7
|
+
__version__ = "7.2.0"
|
|
8
8
|
__copyright__ = "Copyright (C) 2014-2025 S[&]T, The Netherlands."
|
|
9
9
|
|
|
10
10
|
__all__ = ["Error", "InternalError", "Struct", "Archive", "open", "config_path"]
|
|
@@ -245,6 +245,15 @@ class Archive(object):
|
|
|
245
245
|
|
|
246
246
|
_register_extension(self, remote, 'muninn.remote', 'remote_backend')
|
|
247
247
|
|
|
248
|
+
# s3fs mappings
|
|
249
|
+
self.s3fs_mappings = {}
|
|
250
|
+
if configuration is not None and 's3fs' in configuration:
|
|
251
|
+
for mapping in configuration.get('s3fs').get('mappings').split(' '):
|
|
252
|
+
prefix, path = mapping.split('=')
|
|
253
|
+
if not prefix.startswith('s3://'):
|
|
254
|
+
raise Error("s3fs mapping '%s' does not start with an s3 url" % mapping)
|
|
255
|
+
self.s3fs_mappings[prefix] = path
|
|
256
|
+
|
|
248
257
|
def __enter__(self):
|
|
249
258
|
return self
|
|
250
259
|
|
|
@@ -638,8 +647,14 @@ class Archive(object):
|
|
|
638
647
|
if self._storage is None:
|
|
639
648
|
remote_url = product.core.remote_url
|
|
640
649
|
|
|
650
|
+
# apply s3fs mapping if applicable
|
|
651
|
+
for prefix in self.s3fs_mappings:
|
|
652
|
+
if remote_url.startswith(prefix):
|
|
653
|
+
remote_url = "file://" + self.s3fs_mappings[prefix] + remote_url[len(prefix):]
|
|
654
|
+
break
|
|
655
|
+
|
|
641
656
|
if remote_url.startswith('file://'):
|
|
642
|
-
product_path =
|
|
657
|
+
product_path = remote_url[7:]
|
|
643
658
|
if os.path.isdir(product_path):
|
|
644
659
|
paths = [os.path.join(product_path, basename) for basename in os.listdir(product_path)]
|
|
645
660
|
else:
|
|
@@ -1315,12 +1330,16 @@ class Archive(object):
|
|
|
1315
1330
|
product_path = self._product_path(product)
|
|
1316
1331
|
return os.path.join(self._storage.global_prefix, product_path)
|
|
1317
1332
|
if remote_url is not None:
|
|
1333
|
+
# apply s3fs mapping if applicable
|
|
1334
|
+
for prefix in self.s3fs_mappings:
|
|
1335
|
+
if remote_url.startswith(prefix):
|
|
1336
|
+
return self.s3fs_mappings[prefix] + remote_url[len(prefix):]
|
|
1318
1337
|
if remote_url.startswith('file://'):
|
|
1319
1338
|
return remote_url[7:]
|
|
1320
1339
|
return remote_url
|
|
1321
1340
|
return None
|
|
1322
1341
|
|
|
1323
|
-
def pull(self, where="", parameters={}, verify_hash=False, verify_hash_download=False):
|
|
1342
|
+
def pull(self, where="", parameters={}, update_properties=False, verify_hash=False, verify_hash_download=False):
|
|
1324
1343
|
"""Pull one or more remote products into the archive.
|
|
1325
1344
|
|
|
1326
1345
|
Products should have a valid remote_url core metadata field and they should not yet exist in the local
|
|
@@ -1329,6 +1348,9 @@ class Archive(object):
|
|
|
1329
1348
|
Arguments:
|
|
1330
1349
|
where -- Search expression or one or more product uuid(s) or properties.
|
|
1331
1350
|
parameters -- Parameters referenced in the search expression (if any).
|
|
1351
|
+
update_properties -- If set to True then, after the pull, metadata and tags will be extracted from the
|
|
1352
|
+
product using the analyze() function of the product type plugin, and the catalog entry
|
|
1353
|
+
for the product will be updated with this metadata and tags.
|
|
1332
1354
|
verify_hash -- If set to True then, after the pull, the product in the archive will be matched against
|
|
1333
1355
|
the hash from the metadata (only if the metadata contained a hash).
|
|
1334
1356
|
verify_hash_download -- If set to True then, before the product is stored in the archive, the pulled
|
|
@@ -1365,10 +1387,20 @@ class Archive(object):
|
|
|
1365
1387
|
if len(paths) > 1 and not use_enclosing_directory:
|
|
1366
1388
|
raise Error("cannot pull multi-file product without enclosing directory")
|
|
1367
1389
|
|
|
1390
|
+
# extract properties/tags if an update is requested
|
|
1391
|
+
if update_properties:
|
|
1392
|
+
properties, tags = self._analyze_paths(plugin, paths)
|
|
1393
|
+
else:
|
|
1394
|
+
properties = Struct({'core': Struct()})
|
|
1395
|
+
tags = []
|
|
1396
|
+
core = properties.core
|
|
1397
|
+
|
|
1368
1398
|
# reactivate and update archive_date, size
|
|
1369
1399
|
product_path = self._product_path(product)
|
|
1370
1400
|
size = self._storage.size(product_path)
|
|
1371
|
-
|
|
1401
|
+
core.active = True
|
|
1402
|
+
core.archive_date = self._database.server_time_utc()
|
|
1403
|
+
core.size = size
|
|
1372
1404
|
if 'hash' not in product.core or product.core.hash is None:
|
|
1373
1405
|
hash_type = self._plugin_hash_type(plugin)
|
|
1374
1406
|
if hash_type is not None:
|
|
@@ -1376,8 +1408,12 @@ class Archive(object):
|
|
|
1376
1408
|
hash = util.product_hash(paths, hash_type=hash_type)
|
|
1377
1409
|
except EnvironmentError as _error:
|
|
1378
1410
|
raise Error("cannot determine product hash [%s]" % (_error,))
|
|
1379
|
-
|
|
1380
|
-
|
|
1411
|
+
# update hash
|
|
1412
|
+
core.hash = hash
|
|
1413
|
+
self.update_properties(properties, product.core.uuid, create_namespaces=update_properties)
|
|
1414
|
+
|
|
1415
|
+
if tags:
|
|
1416
|
+
self._database.tag(product.core.uuid, tags)
|
|
1381
1417
|
|
|
1382
1418
|
# verify product hash.
|
|
1383
1419
|
if verify_hash and 'hash' in product.core:
|
|
@@ -289,6 +289,15 @@ class FTPBackend(RemoteBackend):
|
|
|
289
289
|
|
|
290
290
|
class S3Backend(RemoteBackend):
|
|
291
291
|
def pull(self, archive, product, target_dir):
|
|
292
|
+
# apply s3fs mapping if applicable
|
|
293
|
+
for prefix in archive.s3fs_mappings:
|
|
294
|
+
if product.core.remote_url.startswith(prefix):
|
|
295
|
+
source_path = archive.s3fs_mappings[prefix] + product.core.remote_url[len(prefix):]
|
|
296
|
+
if not os.path.exists(source_path):
|
|
297
|
+
raise DownloadError("s3fs path %s (for %s) does not exist" % (source_path, product.core.remote_url))
|
|
298
|
+
target_path = os.path.join(target_dir, os.path.basename(source_path))
|
|
299
|
+
util.copy_path(source_path, target_path)
|
|
300
|
+
return self.auto_extract(target_path, product)
|
|
292
301
|
credentials = get_credentials(archive, product.core.remote_url)
|
|
293
302
|
paths = download_s3(product.core.remote_url, target_dir, credentials=credentials)
|
|
294
303
|
if len(paths) == 1:
|
|
@@ -15,12 +15,14 @@ class PullProcessor(Processor):
|
|
|
15
15
|
def __init__(self, args):
|
|
16
16
|
super(PullProcessor, self).__init__(args)
|
|
17
17
|
self.args = args
|
|
18
|
+
self.update_properties = True if args.update_properties else False
|
|
18
19
|
self.verify_hash = True if args.verify_hash else False
|
|
19
20
|
self.verify_hash_download = True if args.verify_hash_download else False
|
|
20
21
|
|
|
21
22
|
def perform_operation(self, archive, product):
|
|
22
23
|
archive.pull(
|
|
23
24
|
product.core.uuid,
|
|
25
|
+
update_properties=self.update_properties,
|
|
24
26
|
verify_hash=self.verify_hash,
|
|
25
27
|
verify_hash_download=self.verify_hash_download
|
|
26
28
|
)
|
|
@@ -44,6 +46,8 @@ def pull(args):
|
|
|
44
46
|
|
|
45
47
|
def main():
|
|
46
48
|
parser = create_parser(description="Pull remote files into the archive.", parallel=True)
|
|
49
|
+
parser.add_argument("--update-properties", action="store_true",
|
|
50
|
+
help="update properties in catalog as if the product was ingested using muninn-ingest")
|
|
47
51
|
parser.add_argument("--verify-hash", action="store_true",
|
|
48
52
|
help="verify the hash of the product after it has been put in the archive")
|
|
49
53
|
parser.add_argument("--verify-hash-download", action="store_true",
|
|
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
|
|
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
|