Commit e53bfdd6 authored by Ophélie Gagnard's avatar Ophélie Gagnard

Add selection strategies for downloading objects.

Add a default strategies that returns the first match.
Add a most recent strategy that returns the object with the greater
timestamp.
Add a "version more than" strategy that returns the first match with
versions superior to that provided. There are two varients of this one:
"strictly more than" and "more than". Versions are required to be
compatible with Python project's version formats define here:
https://peps.python.org/pep-0440/
parent 673842a9
......@@ -24,6 +24,7 @@ import tarfile
import tempfile
import traceback
from base64 import b64encode, b64decode
from packaging import version
try:
import configparser
from http.client import HTTPConnection, HTTPSConnection
......@@ -473,6 +474,51 @@ class NetworkcacheClient(object):
pass
return False
def strategy_default(self, key, kw):
return next(self.select(key, kw))['sha512']
def strategy_most_recent(self, key, kw):
timestamp = 0
most_recent = None
for dictionary in self.select(key,kw):
try:
if int(dictionary['timestamp']) > timestamp:
most_recent = dictionary
timestamp = int(dictionary['timestamp'])
except ValueError:
logger.info('"timestamp" is not an integer (%r)',
dictionary['timestamp'])
except KeyError:
logger.info('No "timestamp" key (%r)',
dictionary)
return most_recent['sha512']
def strategy_higher_version(self, key, kw, strict=False):
# selection criteria:
# - match all non-version parameters
# - every asked version has a value in the dictionary
# - every such version is greater or equal to the asked version
# an entry in kw is read as a version if and only if its key contains "version" (case insensitive)
compare = version.Version.__gt__ if strict else version.Version.__ge__
version_dict = {k:version.Version(kw[k]) for k in kw if "version" in k.lower()}
# if there is no version restriction, then return most recent with same selection criteria
if len(version_dict) == 0:
return self.strategy_most_recent(key,kw)
selection_dict = {k:kw[k] for k in kw if "version" not in k.lower()}
first_match = None
for dictionary in self.select(key,selection_dict):
for k in version_dict:
try:
if k not in dictionary or not compare(version.parse(dictionary[k]),version_dict[k]):
break
except packaging.version.InvalidVersion:
logger.info('Failed to convert to Version (%r)',
dictionary[k])
print(dictionary['sha512'], file=sys.stderr)
else:
return dictionary['sha512']
raise StopIteration("No match.")
class NetworkcacheException(Exception):
pass
......@@ -533,6 +579,8 @@ def cmd_download(*args):
parser = _newArgumentParser("URL of data to download." + key_help)
parser.add_argument('--id',
help="Identifier of the shadir URL, overriding --prefix-key and --suffix-key.")
parser.add_argument('-s', '--selection_strategy', choices=["default", "most_recent", "higher_version", "strictly_higher_version"],
help="Strategy to use to select the object to be downloaded.")
parser.add_argument('meta', nargs='*', metavar='KEY=VALUE',
help="Extra metadata.")
args = parser.parse_args(args or sys.argv[1:])
......@@ -544,5 +592,12 @@ def cmd_download(*args):
urlmd5 = hashlib.md5(args.url.encode()).hexdigest()
key = args.prefix_key + urlmd5 + args.suffix_key
f = sys.stdout
shutil.copyfileobj(nc.download(next(nc.select(key, kw))['sha512']),
getattr(f, 'buffer', f))
if args.selection_strategy == "most_recent":
strategy = nc.strategy_most_recent
elif args.selection_strategy == "higher_version":
strategy = nc.strategy_higher_version
elif args.selection_strategy == "strictly_higher_version":
strategy = nc.strategy_higher_version(strict=True)
else:
strategy = nc.strategy_default
shutil.copyfileobj(nc.download(strategy(key, kw)), getattr(f, 'buffer', f))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment