Skip to content

Commit

Permalink
Align EnsureRemoteName with implementation target
Browse files Browse the repository at this point in the history
- swap notion of "exists" with "known". A remote may not actually exists
  *at* a remote location, but what counts here is whether it is
  registered (or not) in a local repo.
- swap `ValueError` handling with standard `raise_for()` calls. This
  enables joint error reporting and validation
- replace custom Runner invocation with `call_git_items_()`
  • Loading branch information
mih committed Jul 27, 2023
1 parent d4ca29f commit 048b741
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 38 deletions.
69 changes: 37 additions & 32 deletions datalad_next/constraints/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,26 @@ def short_description(self):


class EnsureRemoteName(EnsureGitRefName):
"""Ensures a remote name is provided, and if it fulfills optional
requirements"""
"""Ensures a remote name is given, and optionally if such a remote is known
"""

def __init__(self,
exists: bool | None = None,
known: bool | None = None,
dsarg: DatasetParameter | None = None):
"""
Parameters
----------
exists: bool, optional
If true, validates that the remote exists, fails otherwise.
If false, validates that the remote doesn't exist, fails otherwise.
If None, just checks that a sibling name was provided.
known: bool, optional
By default, a given value is only checked if it is a syntactically
correct remote name.
If ``True``, also checks that the given name corresponds to a
known remote in the dataset given by ``dsarg``. If ``False``,
checks that the given remote does not match any known remote
in that dataset.
dsarg: DatasetParameter, optional
Identifies a dataset for testing remote existence, if requested.
"""
self._exists = exists
self._known = known
self._dsarg = dsarg
super().__init__(allow_onelevel=True,
refspec_pattern=False)
Expand All @@ -106,40 +111,40 @@ def __call__(self, value: str) -> str:
raise ValueError('must state a sibling name')
super().__call__(value)

if self._exists is None:
# we only need to know that something was provided, no further check
if self._known is None:
# we only need to know that something was provided,
# no further check
return value

from datalad.runner import GitRunner, StdOutCapture
runner = GitRunner()
cmd = ['git', 'remote'] if not self._dsarg else \
['git', '-C', f'{self._dsarg.ds.path}', 'remote']
remotes = runner.run(cmd, protocol=StdOutCapture)['stdout'].split()
if self._exists and value not in remotes:
raise ValueError(
f'Sibling {value} is not among available remotes {remotes}'
assert self._dsarg, \
"Existence check for remote requires dataset specification"

remotes = list(self._dsarg.ds.repo.call_git_items_(['remote']))
if self._known and value not in remotes:
self.raise_for(
value,
'is not one of the known remote(s) {remotes!r}',
remotes=remotes,
)
elif self._exists is False and value in remotes:
raise ValueError(
f'Sibling {value} is already among available remotes {remotes}'
elif self._known is False and value in remotes:
self.raise_for(
value,
'name conflicts with a known remote',
remotes=remotes,
)
else:
return value

def short_description(self):
if self._exists is not None:
desc = ' that exists' if self._exists else ' that does not yet exist'
else:
desc = ''
return "Sibling name{}".format(desc)
return "Name of a{desc} remote".format(
desc=' known' if self._known
else ' not-yet-known' if self._known is False else ''
)

def for_dataset(self, dataset: DatasetParameter) -> Constraint:
"""Return an similarly parametrized variant that resolves
paths against a given dataset (argument)
"""
"""Return an similarly parametrized variant that checks remote names
against a given dataset (argument)"""
return self.__class__(
exists=self._exists,
known=self._known,
dsarg=dataset,
)
13 changes: 7 additions & 6 deletions datalad_next/constraints/tests/test_special_purpose.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ def test_EnsureRemoteName(existing_dataset):
# empty sibling name must raise
with pytest.raises(ValueError):
EnsureRemoteName()('')
assert EnsureRemoteName().short_description() == 'Sibling name'
assert EnsureRemoteName().short_description() == 'Name of a remote'
assert EnsureRemoteName(
exists=True).short_description() == 'Sibling name that exists'
known=True).short_description() == 'Name of a known remote'
assert EnsureRemoteName(
exists=False).short_description() == 'Sibling name that does not yet exist'
known=False).short_description() == 'Name of a not-yet-known remote'
ds = existing_dataset
c = EnsureRemoteName(exists=False)
c = EnsureRemoteName(known=False)
tc = c.for_dataset(DatasetParameter(None, ds))
assert tc('newremotename') == 'newremotename'
# add a remote
Expand All @@ -73,12 +73,13 @@ def test_EnsureRemoteName(existing_dataset):
with pytest.raises(ValueError):
tc('my-remote')
# should work when it should exist
c = EnsureRemoteName(exists=True)
c = EnsureRemoteName(known=True)
tc = c.for_dataset(DatasetParameter(None, ds))
assert tc('my-remote') == 'my-remote'
# but fail with non-existing remote
with pytest.raises(ValueError):
with pytest.raises(ValueError) as e:
tc('not-my-remote')
assert str(e.value) == "is not one of the known remote(s) ['my-remote']"
# return sibling name with no existence checks
assert EnsureRemoteName()('anything') == 'anything'

Expand Down

0 comments on commit 048b741

Please sign in to comment.