From 4d6c0bd1f53836f2fc59e6e5a1f3555558d82133 Mon Sep 17 00:00:00 2001 From: cpburnz Date: Sat, 12 Jun 2021 12:37:57 -0400 Subject: [PATCH] Fix matching absolute paths --- CHANGES.rst | 5 +++- pathspec/pathspec.py | 5 ++-- pathspec/tests/test_pathspec.py | 46 +++++++++++++++++++++++++++++++++ pathspec/util.py | 23 ++++++++++++----- 4 files changed, 70 insertions(+), 9 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 031e867..d9e62c6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,12 +2,15 @@ Change History ============== -0.8.2 (TDB) +0.9.0 (TDB) ----------- - `Issue #45`_: Fix for duplicate leading double-asterisk, and edge cases. +- `Issue #46`_: Fix matching absolute paths. +- API change: `util.normalize_files()` now returns a `Dict[str, List[pathlike]]` instead of a `Dict[str, pathlike]`. .. _`Issue #45`: https://github.com/cpburnz/python-path-specification/pull/45 +.. _`Issue #46`: https://github.com/cpburnz/python-path-specification/issues/46 0.8.1 (2020-11-07) diff --git a/pathspec/pathspec.py b/pathspec/pathspec.py index 73250ef..4585c37 100644 --- a/pathspec/pathspec.py +++ b/pathspec/pathspec.py @@ -155,8 +155,9 @@ def match_files(self, files, separators=None): file_map = util.normalize_files(files, separators=separators) matched_files = util.match_files(self.patterns, iterkeys(file_map)) - for path in matched_files: - yield file_map[path] + for norm_file in matched_files: + for orig_file in file_map[norm_file]: + yield orig_file def match_tree_entries(self, root, on_error=None, follow_links=None): """ diff --git a/pathspec/tests/test_pathspec.py b/pathspec/tests/test_pathspec.py index e9435f6..3ce52d7 100644 --- a/pathspec/tests/test_pathspec.py +++ b/pathspec/tests/test_pathspec.py @@ -13,6 +13,52 @@ class PathSpecTest(unittest.TestCase): The ``PathSpecTest`` class tests the ``PathSpec`` class. """ + def test_01_absolute_dir_paths_1(self): + """ + Tests that absolute paths will be properly normalized and matched. + """ + spec = pathspec.PathSpec.from_lines('gitwildmatch', [ + 'foo', + ]) + results = set(spec.match_files([ + '/a.py', + '/foo/a.py', + '/x/a.py', + '/x/foo/a.py', + 'a.py', + 'foo/a.py', + 'x/a.py', + 'x/foo/a.py', + ])) + self.assertEqual(results, { + '/foo/a.py', + '/x/foo/a.py', + 'foo/a.py', + 'x/foo/a.py', + }) + + def test_01_absolute_dir_paths_2(self): + """ + Tests that absolute paths will be properly normalized and matched. + """ + spec = pathspec.PathSpec.from_lines('gitwildmatch', [ + '/foo', + ]) + results = set(spec.match_files([ + '/a.py', + '/foo/a.py', + '/x/a.py', + '/x/foo/a.py', + 'a.py', + 'foo/a.py', + 'x/a.py', + 'x/foo/a.py', + ])) + self.assertEqual(results, { + '/foo/a.py', + 'foo/a.py', + }) + def test_01_current_dir_paths(self): """ Tests that paths referencing the current directory will be properly diff --git a/pathspec/util.py b/pathspec/util.py index bcba878..e310778 100644 --- a/pathspec/util.py +++ b/pathspec/util.py @@ -300,7 +300,8 @@ def _normalize_entries(entries, separators=None): def normalize_file(file, separators=None): """ - Normalizes the file path to use the POSIX path separator (i.e., ``'/'``). + Normalizes the file path to use the POSIX path separator (i.e., + ``'/'``), and make the paths relative (remove leading ``'/'``). *file* (:class:`str` or :class:`pathlib.PurePath`) is the file path. @@ -323,8 +324,12 @@ def normalize_file(file, separators=None): for sep in separators: norm_file = norm_file.replace(sep, posixpath.sep) - # Remove current directory prefix. - if norm_file.startswith('./'): + if norm_file.startswith('/'): + # Make path relative. + norm_file = norm_file[1:] + + elif norm_file.startswith('./'): + # Remove current directory prefix. norm_file = norm_file[2:] return norm_file @@ -341,12 +346,18 @@ def normalize_files(files, separators=None): :data:`None`) optionally contains the path separators to normalize. See :func:`normalize_file` for more information. - Returns a :class:`dict` mapping the each normalized file path (:class:`str`) - to the original file path (:class:`str`) + Returns a :class:`dict` mapping the each normalized file path + (:class:`str`) to the original file paths (:class:`list` of + :class:`str`). """ norm_files = {} for path in files: - norm_files[normalize_file(path, separators=separators)] = path + norm_file = normalize_file(path, separators=separators) + if norm_file in norm_files: + norm_files[norm_file].append(path) + else: + norm_files[norm_file] = [path] + return norm_files