diff --git a/datalad/support/path.py b/datalad/support/path.py index db5e3f8dd2..85620409c4 100644 --- a/datalad/support/path.py +++ b/datalad/support/path.py @@ -15,13 +15,15 @@ # TODO: RF and move all paths related functions from datalad.utils in here import os import os.path as op - # to not pollute API importing as _ from collections import defaultdict as _defaultdict - from functools import wraps from itertools import dropwhile -from pathlib import Path, PurePosixPath +from pathlib import ( + Path, + PurePosixPath, +) +from typing import Generator from ..utils import ( ensure_bytes, @@ -209,7 +211,8 @@ def get_parent_paths(paths, parents, only_with_parents=False, *, sep='/'): return res -def get_limited_paths(paths: list[str|Path], limits: list[str|Path], *, include_within_path: bool = False) -> list[Path]: +def get_limited_paths(paths: list[str|Path], limits: list[str|Path], *, include_within_path: bool = False) \ + -> Generator[str, None, None]: """Given list of relative POSIX paths (or Path objects), select the ones within limits (also relative and POSIX). In case of include_with_path=True, if limit points to some path under a 'path' within 'paths', @@ -247,8 +250,8 @@ def _harmonize_paths(l: list) -> list: limits_parts = limits_parts[1:] else: break # otherwise -- consider this one! - if not limits_parts: - # none left + else: + # no limiting path left - the other paths cannot be the selected ones break if include_within_path: # if one identical or subpath of another one -- their parts match in the beginning diff --git a/datalad/support/tests/test_path.py b/datalad/support/tests/test_path.py index 3d683673ba..e2ceadd1c6 100644 --- a/datalad/support/tests/test_path.py +++ b/datalad/support/tests/test_path.py @@ -8,6 +8,7 @@ # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import os +from pathlib import PurePosixPath from ...tests.utils_pytest import ( SkipTest, @@ -123,5 +124,26 @@ def glp(*args, **kwargs): return list(get_limited_paths(*args, **kwargs)) assert glp(['a', 'b'], ['a', 'c']) == ['a'] + assert glp(['a', 'b'], ['b']) == ['b'] + assert glp(['a', 'b'], ['c']) == [] + assert glp(['a', 'b'], ['a/b', 'c']) == [] # a is not subpath of a/b - assert glp(['a', 'b'], ['a/b', 'c'], include_within_path=True) == ['a'] # a is not subpath of a/b \ No newline at end of file + assert glp(['a', 'b'], ['a/b', 'c'], include_within_path=True) == ['a'] # a is not subpath of a/b + + # all paths returned due to '.', and order is sorted + paths = ['a', 'b', '1/2/3', 'abc'] + paths_sorted = sorted(paths) + assert glp(paths, ['.']) == paths_sorted + assert glp(paths, paths_sorted) == paths_sorted + assert glp(paths, paths_sorted, include_within_path=True) == paths_sorted + # we can take a mix of str and Path + assert glp([PurePosixPath(paths[0])] + paths[1:], ['.']) == paths_sorted + + + # nothing within empty "limits" matches -- so no paths yielded + assert glp(paths, []) == [] + + assert_raises(ValueError, glp, ['/a'], []) + assert_raises(ValueError, glp, [PurePosixPath('/a')], []) + assert_raises(ValueError, glp, ['a'], ['/a']) + assert_raises(ValueError, glp, ['../a'], ['a']) \ No newline at end of file diff --git a/tox.ini b/tox.ini index d188ee4bdd..6988d9f56a 100644 --- a/tox.ini +++ b/tox.ini @@ -39,7 +39,8 @@ commands = datalad/cmd.py \ datalad/downloaders/providers.py \ datalad/runner \ - datalad/support/collections.py + datalad/support/collections.py \ + datalad/support/path.py [testenv:venv] commands = {posargs}