Skip to content

Commit

Permalink
Merge pull request #492 from adswa/ensurehash
Browse files Browse the repository at this point in the history
Add EnsureHashAlgorithm
  • Loading branch information
mih authored Oct 21, 2023
2 parents 78216f1 + 4e04ec8 commit 6a2d65e
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 5 deletions.
6 changes: 6 additions & 0 deletions changelog.d/20231021_102012_michael.hanke_ensurehash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
### 💫 Enhancements and new features

- New `EnsureHashAlgorithm` constraint to automatically expose
and verify algorithm labels from `hashlib.algorithms_guaranteed`
Fixes https://github.com/datalad/datalad-next/issues/346 via
https://github.com/datalad/datalad-next/pull/492 (by @mslw @adswa)
6 changes: 3 additions & 3 deletions datalad_next/commands/ls_file_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
EnsureChoice,
EnsurePath,
EnsureURL,
EnsureHashAlgorithm,
EnsureListOf,
)
from datalad_next.uis import (
ansi_colors as ac,
Expand Down Expand Up @@ -93,9 +95,7 @@ def __init__(self):
param_constraints=dict(
type=self._collection_types,
collection=EnsurePath(lexists=True) | EnsureURL(),
# TODO EnsureHashAlgorithm
# https://github.com/datalad/datalad-next/issues/346
#hash=None,
hash=EnsureHashAlgorithm() | EnsureListOf(EnsureHashAlgorithm()),
),
joint_constraints={
ParameterConstraintContext(('type', 'collection', 'hash'),
Expand Down
1 change: 1 addition & 0 deletions datalad_next/constraints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
EnsureCallable,
EnsureChoice,
EnsureFloat,
EnsureHashAlgorithm,
EnsureInt,
EnsureKeyChoice,
EnsureNone,
Expand Down
13 changes: 13 additions & 0 deletions datalad_next/constraints/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

__docformat__ = 'restructuredtext'

from hashlib import algorithms_guaranteed as hash_algorithms_guaranteed
from pathlib import Path
import re

Expand Down Expand Up @@ -274,6 +275,9 @@ def long_description(self):
def short_description(self):
return '{%s}' % ', '.join([repr(c) for c in self._allowed])

def __str__(self):
return f"one of {self.short_description()}"


class EnsureKeyChoice(EnsureChoice):
"""Ensure value under a key in an input is in a set of possible values"""
Expand Down Expand Up @@ -497,3 +501,12 @@ def short_description(self):
if self._ref
else '',
)


class EnsureHashAlgorithm(EnsureChoice):
"""Ensure an input matches a name of a ``hashlib`` algorithm
Specifically the item must be in the ``algorithms_guaranteed`` collection.
"""
def __init__(self):
super().__init__(*hash_algorithms_guaranteed)
6 changes: 4 additions & 2 deletions datalad_next/constraints/compound.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,12 @@ def __call__(self, value):
iter = self._iter_type(
self._item_constraint(i) for i in value
)
except TypeError as e:
except (ConstraintError, TypeError) as e:
self.raise_for(
value,
"cannot coerce to target (item) type",
"{itertype} item is not {itype}",
itertype=self._iter_type.__name__,
itype=self._item_constraint,
__caused_by__=e,
)
if self._min_len is not None or self._max_len is not None:
Expand Down
26 changes: 26 additions & 0 deletions datalad_next/constraints/tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
EnsureNone,
EnsureCallable,
EnsureChoice,
EnsureHashAlgorithm,
EnsureKeyChoice,
EnsureRange,
EnsurePath,
Expand Down Expand Up @@ -188,6 +189,7 @@ def test_choice():
assert i in descr
# short is a "set" or repr()s
assert c.short_description() == "{'choice1', 'choice2', None}"
assert str(c) == "one of {'choice1', 'choice2', None}"
# this should always work
assert c('choice1') == 'choice1'
assert c(None) is None
Expand Down Expand Up @@ -317,3 +319,27 @@ def test_EnsurePath_fordataset(existing_dataset):
# 2. dataset is given as a dataset object
tc = c.for_dataset(DatasetParameter(ds, ds))
assert tc('relpath') == (ds.pathobj / 'relpath')


def test_EnsureHashAlgorithm():
c = EnsureHashAlgorithm()
# simple cases that should pass
hashes = [
'sha3_256', 'shake_256', 'sha3_384', 'md5', 'shake_128', 'sha384',
'sha3_224', 'blake2s', 'sha1', 'blake2b', 'sha224', 'sha512', 'sha256',
'sha3_512'
]
for hash in hashes:
c(hash)
# a few bogus ones:
bad_hashes = [
'md17', 'McGyver', 'sha2', 'bogus'
]
for baddie in bad_hashes:
with pytest.raises(ConstraintError):
c(baddie)

# check messaging
for i in ('md5', 'shake_256', 'sha3_512'):
assert i in c.short_description()
assert i in c.long_description()

0 comments on commit 6a2d65e

Please sign in to comment.