Hello community,
here is the log from the commit of package python-pathspec for openSUSE:Factory
checked in at 2020-10-29 09:46:48
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pathspec (Old)
and /work/SRC/openSUSE:Factory/.python-pathspec.new.3463 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pathspec"
Thu Oct 29 09:46:48 2020 rev:6 rq:839941 version:0.8.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pathspec/python-pathspec.changes
2020-03-11 18:56:50.835715147 +0100
+++
/work/SRC/openSUSE:Factory/.python-pathspec.new.3463/python-pathspec.changes
2020-10-29 09:46:50.508085623 +0100
@@ -1,0 +2,12 @@
+Wed Oct 7 03:51:16 UTC 2020 - John Vandenberg <[email protected]>
+
+- Update to v0.8.0
+ * Expose what patterns matched paths. Added `util.detailed_match_files()`
+ * `match_tree()` doesn't return symlinks
+ - Add `PathSpec.match_tree_entries` and `util.iter_tree_entries()` to
+ support directories and symlinks
+ - API change: `match_tree()` has been renamed to `match_tree_files()`
+ The old name `match_tree()` is still available as an alias
+ - API change: `match_tree_files()` now returns symlinks
+
+-------------------------------------------------------------------
Old:
----
pathspec-0.7.0.tar.gz
New:
----
pathspec-0.8.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pathspec.spec ++++++
--- /var/tmp/diff_new_pack.Z1l1eX/_old 2020-10-29 09:46:51.268086342 +0100
+++ /var/tmp/diff_new_pack.Z1l1eX/_new 2020-10-29 09:46:51.272086345 +0100
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-pathspec
-Version: 0.7.0
+Version: 0.8.0
Release: 0
Summary: Utility library for gitignore style pattern matching of file
paths
License: MPL-2.0
++++++ pathspec-0.7.0.tar.gz -> pathspec-0.8.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pathspec-0.7.0/CHANGES.rst
new/pathspec-0.8.0/CHANGES.rst
--- old/pathspec-0.7.0/CHANGES.rst 2019-12-27 16:28:16.000000000 +0100
+++ new/pathspec-0.8.0/CHANGES.rst 2020-04-09 15:04:11.000000000 +0200
@@ -3,6 +3,19 @@
==============
+0.8.0 (2020-04-09)
+------------------
+
+- `Issue #30`_: Expose what patterns matched paths. Added
`util.detailed_match_files()`.
+- `Issue #31`_: `match_tree()` doesn't return symlinks.
+- Add `PathSpec.match_tree_entries` and `util.iter_tree_entries()` to support
directories and symlinks.
+- API change: `match_tree()` has been renamed to `match_tree_files()`. The old
name `match_tree()` is still available as an alias.
+- API change: `match_tree_files()` now returns symlinks. This is a bug fix but
it will change the returned results.
+
+.. _`Issue #30`: https://github.com/cpburnz/python-path-specification/issues/30
+.. _`Issue #31`: https://github.com/cpburnz/python-path-specification/issues/31
+
+
0.7.0 (2019-12-27)
------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pathspec-0.7.0/PKG-INFO new/pathspec-0.8.0/PKG-INFO
--- old/pathspec-0.7.0/PKG-INFO 2019-12-27 16:31:53.000000000 +0100
+++ new/pathspec-0.8.0/PKG-INFO 2020-04-09 15:08:33.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: pathspec
-Version: 0.7.0
+Version: 0.8.0
Summary: Utility library for gitignore style pattern matching of file paths.
Home-page: https://github.com/cpburnz/python-path-specification
Author: Caleb P. Burns
@@ -163,6 +163,19 @@
==============
+ 0.8.0 (2020-04-09)
+ ------------------
+
+ - `Issue #30`_: Expose what patterns matched paths. Added
`util.detailed_match_files()`.
+ - `Issue #31`_: `match_tree()` doesn't return symlinks.
+ - Add `PathSpec.match_tree_entries` and `util.iter_tree_entries()` to
support directories and symlinks.
+ - API change: `match_tree()` has been renamed to `match_tree_files()`.
The old name `match_tree()` is still available as an alias.
+ - API change: `match_tree_files()` now returns symlinks. This is a bug
fix but it will change the returned results.
+
+ .. _`Issue #30`:
https://github.com/cpburnz/python-path-specification/issues/30
+ .. _`Issue #31`:
https://github.com/cpburnz/python-path-specification/issues/31
+
+
0.7.0 (2019-12-27)
------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec/__init__.py
new/pathspec-0.8.0/pathspec/__init__.py
--- old/pathspec-0.7.0/pathspec/__init__.py 2019-12-27 16:28:25.000000000
+0100
+++ new/pathspec-0.8.0/pathspec/__init__.py 2020-04-09 15:08:03.000000000
+0200
@@ -24,7 +24,7 @@
from __future__ import unicode_literals
__author__ = "Caleb P. Burns"
-__copyright__ = "Copyright © 2013-2018 Caleb P. Burns"
+__copyright__ = "Copyright © 2013-2020 Caleb P. Burns"
__created__ = "2013-10-12"
__credits__ = [
"dahlia <https://github.com/dahlia>",
@@ -46,13 +46,15 @@
"mroutis <https://github.com/mroutis>",
"jdufresne <https://github.com/jdufresne>",
"groodt <https://github.com/groodt>",
+ "ftrofin <https://github.com/ftrofin>",
+ "pykong <https://github.com/pykong>",
]
__email__ = "[email protected]"
__license__ = "MPL 2.0"
__project__ = "pathspec"
__status__ = "Development"
-__updated__ = "2019-12-27"
-__version__ = "0.7.0"
+__updated__ = "2020-04-09"
+__version__ = "0.8.0"
from .pathspec import PathSpec
from .pattern import Pattern, RegexPattern
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec/compat.py
new/pathspec-0.8.0/pathspec/compat.py
--- old/pathspec-0.7.0/pathspec/compat.py 2018-12-01 18:57:36.000000000
+0100
+++ new/pathspec-0.8.0/pathspec/compat.py 2020-04-07 05:10:57.000000000
+0200
@@ -13,6 +13,7 @@
unicode = unicode
string_types = (basestring,)
+ from collections import Iterable
from itertools import izip_longest
def iterkeys(mapping):
@@ -23,6 +24,7 @@
unicode = str
string_types = (unicode,)
+ from collections.abc import Iterable
from itertools import zip_longest as izip_longest
def iterkeys(mapping):
@@ -30,7 +32,7 @@
try:
# Python 3.6+.
- from collections.abc import Collection as collection_type
+ from collections.abc import Collection
except ImportError:
# Python 2.7 - 3.5.
- from collections import Container as collection_type
+ from collections import Container as Collection
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec/pathspec.py
new/pathspec-0.8.0/pathspec/pathspec.py
--- old/pathspec-0.7.0/pathspec/pathspec.py 2018-12-01 18:57:36.000000000
+0100
+++ new/pathspec-0.8.0/pathspec/pathspec.py 2020-04-07 05:39:17.000000000
+0200
@@ -5,7 +5,7 @@
"""
from . import util
-from .compat import collection_type, iterkeys, izip_longest, string_types,
unicode
+from .compat import Collection, iterkeys, izip_longest, string_types, unicode
class PathSpec(object):
@@ -22,7 +22,7 @@
yields each compiled pattern (:class:`.Pattern`).
"""
- self.patterns = patterns if isinstance(patterns,
collection_type) else list(patterns)
+ self.patterns = patterns if isinstance(patterns, Collection)
else list(patterns)
"""
*patterns* (:class:`~collections.abc.Collection` of
:class:`.Pattern`)
contains the compiled patterns.
@@ -68,7 +68,7 @@
if not callable(pattern_factory):
raise TypeError("pattern_factory:{!r} is not
callable.".format(pattern_factory))
- if isinstance(lines, (bytes, unicode)):
+ if not util._is_iterable(lines):
raise TypeError("lines:{!r} is not an
iterable.".format(lines))
lines = [pattern_factory(line) for line in lines if line]
@@ -78,8 +78,8 @@
"""
Matches the file to this path-spec.
- *file* (:class:`str`) is the file path to be matched against
- :attr:`self.patterns <PathSpec.patterns>`.
+ *file* (:class:`str` or :class:`~pathlib.PurePath`) is the file
path
+ to be matched against :attr:`self.patterns <PathSpec.patterns>`.
*separators* (:class:`~collections.abc.Collection` of
:class:`str`)
optionally contains the path separators to normalize. See
@@ -90,13 +90,36 @@
norm_file = util.normalize_file(file, separators=separators)
return util.match_file(self.patterns, norm_file)
+ def match_entries(self, entries, separators=None):
+ """
+ Matches the entries to this path-spec.
+
+ *entries* (:class:`~collections.abc.Iterable` of
:class:`~util.TreeEntry`)
+ contains the entries to be matched against :attr:`self.patterns
<PathSpec.patterns>`.
+
+ *separators* (:class:`~collections.abc.Collection` of
:class:`str`;
+ or :data:`None`) optionally contains the path separators to
+ normalize. See :func:`~pathspec.util.normalize_file` for more
+ information.
+
+ Returns the matched entries (:class:`~collections.abc.Iterable`
of
+ :class:`~util.TreeEntry`).
+ """
+ if not util._is_iterable(entries):
+ raise TypeError("entries:{!r} is not an
iterable.".format(entries))
+
+ entry_map = util._normalize_entries(entries,
separators=separators)
+ match_paths = util.match_files(self.patterns,
iterkeys(entry_map))
+ for path in match_paths:
+ yield entry_map[path]
+
def match_files(self, files, separators=None):
"""
Matches the files to this path-spec.
- *files* (:class:`~collections.abc.Iterable` of :class:`str`)
contains
- the file paths to be matched against :attr:`self.patterns
- <PathSpec.patterns>`.
+ *files* (:class:`~collections.abc.Iterable` of :class:`str; or
+ :class:`pathlib.PurePath`) contains the file paths to be matched
+ against :attr:`self.patterns <PathSpec.patterns>`.
*separators* (:class:`~collections.abc.Collection` of
:class:`str`;
or :data:`None`) optionally contains the path separators to
@@ -106,7 +129,7 @@
Returns the matched files (:class:`~collections.abc.Iterable` of
:class:`str`).
"""
- if isinstance(files, (bytes, unicode)):
+ if not util._is_iterable(files):
raise TypeError("files:{!r} is not an
iterable.".format(files))
file_map = util.normalize_files(files, separators=separators)
@@ -114,24 +137,49 @@
for path in matched_files:
yield file_map[path]
- def match_tree(self, root, on_error=None, follow_links=None):
+ def match_tree_entries(self, root, on_error=None, follow_links=None):
"""
Walks the specified root path for all files and matches them to
this
path-spec.
- *root* (:class:`str`) is the root directory to search for files.
+ *root* (:class:`str`; or :class:`pathlib.PurePath`) is the root
+ directory to search.
*on_error* (:class:`~collections.abc.Callable` or :data:`None`)
optionally is the error handler for file-system exceptions. See
- :func:`~pathspec.util.iter_tree` for more information.
+ :func:`~pathspec.util.iter_tree_entries` for more information.
+ *follow_links* (:class:`bool` or :data:`None`) optionally is
whether
+ to walk symbolic links that resolve to directories. See
+ :func:`~pathspec.util.iter_tree_files` for more information.
+
+ Returns the matched files (:class:`~collections.abc.Iterable` of
+ :class:`str`).
+ """
+ entries = util.iter_tree_entries(root, on_error=on_error,
follow_links=follow_links)
+ return self.match_entries(entries)
+
+ def match_tree_files(self, root, on_error=None, follow_links=None):
+ """
+ Walks the specified root path for all files and matches them to
this
+ path-spec.
+
+ *root* (:class:`str`; or :class:`pathlib.PurePath`) is the root
+ directory to search for files.
+
+ *on_error* (:class:`~collections.abc.Callable` or :data:`None`)
+ optionally is the error handler for file-system exceptions. See
+ :func:`~pathspec.util.iter_tree_files` for more information.
*follow_links* (:class:`bool` or :data:`None`) optionally is
whether
- to walk symbolik links that resolve to directories. See
- :func:`~pathspec.util.iter_tree` for more information.
+ to walk symbolic links that resolve to directories. See
+ :func:`~pathspec.util.iter_tree_files` for more information.
Returns the matched files (:class:`~collections.abc.Iterable` of
:class:`str`).
"""
- files = util.iter_tree(root, on_error=on_error,
follow_links=follow_links)
+ files = util.iter_tree_files(root, on_error=on_error,
follow_links=follow_links)
return self.match_files(files)
+
+ # Alias `match_tree_files()` as `match_tree()`.
+ match_tree = match_tree_files
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec/tests/test_util.py
new/pathspec-0.8.0/pathspec/tests/test_util.py
--- old/pathspec-0.7.0/pathspec/tests/test_util.py 2018-12-01
18:58:00.000000000 +0100
+++ new/pathspec-0.8.0/pathspec/tests/test_util.py 2020-04-07
04:21:51.000000000 +0200
@@ -7,15 +7,16 @@
import os
import os.path
import shutil
+import sys
import tempfile
import unittest
-from pathspec.util import iter_tree, RecursionError
+from pathspec.util import iter_tree_entries, iter_tree_files, RecursionError,
normalize_file
class IterTreeTest(unittest.TestCase):
"""
- The ``IterTreeTest`` class tests `pathspec.util.iter_tree()`.
+ The ``IterTreeTest`` class tests `pathspec.util.iter_tree_files()`.
"""
def make_dirs(self, dirs):
@@ -98,7 +99,7 @@
'Dir/Inner/e',
'Dir/Inner/f',
])
- results = set(iter_tree(self.temp_dir))
+ results = set(iter_tree_files(self.temp_dir))
self.assertEqual(results, set(map(self.ospath, [
'a',
'b',
@@ -176,7 +177,7 @@
('Dir/dx', 'Dir/d'),
('DirX', 'Dir'),
])
- results = set(iter_tree(self.temp_dir))
+ results = set(iter_tree_files(self.temp_dir))
self.assertEqual(results, set(map(self.ospath, [
'a',
'ax',
@@ -213,7 +214,7 @@
('Dir/Ex', 'Dir/Target'),
('Dir/Fx', 'Dir/Target'),
])
- results = set(iter_tree(self.temp_dir))
+ results = set(iter_tree_files(self.temp_dir))
self.assertEqual(results, set(map(self.ospath, [
'Ax/Ex/file',
'Ax/Fx/file',
@@ -244,7 +245,7 @@
('Dir/Self', 'Dir'),
])
with self.assertRaises(RecursionError) as context:
- set(iter_tree(self.temp_dir))
+ set(iter_tree_files(self.temp_dir))
self.assertEqual(context.exception.first_path, 'Dir')
self.assertEqual(context.exception.second_path,
self.ospath('Dir/Self'))
@@ -270,7 +271,7 @@
('C/Ax', 'A'),
])
with self.assertRaises(RecursionError) as context:
- set(iter_tree(self.temp_dir))
+ set(iter_tree_files(self.temp_dir))
self.assertIn(context.exception.first_path, ('A', 'B', 'C'))
self.assertEqual(context.exception.second_path, {
'A': self.ospath('A/Bx/Cx/Ax'),
@@ -290,7 +291,7 @@
('A', 'DOES_NOT_EXIST'),
])
with self.assertRaises(OSError) as context:
- set(iter_tree(self.temp_dir, on_error=reraise))
+ set(iter_tree_files(self.temp_dir, on_error=reraise))
self.assertEqual(context.exception.errno, errno.ENOENT)
def test_2_7_ignore_broken_links(self):
@@ -301,7 +302,7 @@
self.make_links([
('A', 'DOES_NOT_EXIST'),
])
- results = set(iter_tree(self.temp_dir))
+ results = set(iter_tree_files(self.temp_dir))
self.assertEqual(results, set())
def test_2_8_no_follow_links(self):
@@ -325,7 +326,7 @@
('Dir/Dx', 'Dir/D'),
('DirX', 'Dir'),
])
- results = set(iter_tree(self.temp_dir, follow_links=False))
+ results = set(iter_tree_files(self.temp_dir,
follow_links=False))
self.assertEqual(results, set(map(self.ospath, [
'A',
'Ax',
@@ -335,4 +336,45 @@
'Dir/Cx',
'Dir/D',
'Dir/Dx',
+ 'DirX',
])))
+
+ def test_3_entries(self):
+ """
+ Tests to make sure all files are found.
+ """
+ self.make_dirs([
+ 'Empty',
+ 'Dir',
+ 'Dir/Inner',
+ ])
+ self.make_files([
+ 'a',
+ 'b',
+ 'Dir/c',
+ 'Dir/d',
+ 'Dir/Inner/e',
+ 'Dir/Inner/f',
+ ])
+ results = {entry.path for entry in
iter_tree_entries(self.temp_dir)}
+ self.assertEqual(results, set(map(self.ospath, [
+ 'a',
+ 'b',
+ 'Dir',
+ 'Dir/c',
+ 'Dir/d',
+ 'Dir/Inner',
+ 'Dir/Inner/e',
+ 'Dir/Inner/f',
+ 'Empty',
+ ])))
+
+ @unittest.skipIf(sys.version_info < (3, 4), "pathlib entered stdlib in
Python 3.4")
+ def test_4_normalizing_pathlib_path(self):
+ """
+ Tests passing pathlib.Path as argument.
+ """
+ from pathlib import Path
+ first_spec = normalize_file(Path('a.txt'))
+ second_spec = normalize_file('a.txt')
+ self.assertEqual(first_spec, second_spec)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec/util.py
new/pathspec-0.8.0/pathspec/util.py
--- old/pathspec-0.7.0/pathspec/util.py 2018-12-01 18:57:36.000000000 +0100
+++ new/pathspec-0.8.0/pathspec/util.py 2020-04-07 05:43:06.000000000 +0200
@@ -8,7 +8,7 @@
import posixpath
import stat
-from .compat import collection_type, string_types
+from .compat import Collection, Iterable, string_types, unicode
NORMALIZE_PATH_SEPS = [sep for sep in [os.sep, os.altsep] if sep and sep !=
posixpath.sep]
"""
@@ -20,11 +20,96 @@
_registered_patterns = {}
"""
-*_registered_patterns* (``dict``) maps a name (``str``) to the
-registered pattern factory (``callable``).
+*_registered_patterns* (:class:`dict`) maps a name (:class:`str`) to the
+registered pattern factory (:class:`~collections.abc.Callable`).
"""
-def iter_tree(root, on_error=None, follow_links=None):
+
+def detailed_match_files(patterns, files, all_matches=None):
+ """
+ Matches the files to the patterns, and returns which patterns matched
+ the files.
+
+ *patterns* (:class:`~collections.abc.Iterable` of
:class:`~pathspec.pattern.Pattern`)
+ contains the patterns to use.
+
+ *files* (:class:`~collections.abc.Iterable` of :class:`str`) contains
+ the normalized file paths to be matched against *patterns*.
+
+ *all_matches* (:class:`boot` or :data:`None`) is whether to return all
+ matches patterns (:data:`True`), or only the last matched pattern
+ (:data:`False`). Default is :data:`None` for :data:`False`.
+
+ Returns the matched files (:class:`dict`) which maps each matched file
+ (:class:`str`) to the patterns that matched in order
(:class:`.MatchDetail`).
+ """
+ all_files = files if isinstance(files, Collection) else list(files)
+ return_files = {}
+ for pattern in patterns:
+ if pattern.include is not None:
+ result_files = pattern.match(all_files)
+ if pattern.include:
+ # Add files and record pattern.
+ for result_file in result_files:
+ if result_file in return_files:
+ if all_matches:
+
return_files[result_file].patterns.append(pattern)
+ else:
+
return_files[result_file].patterns[0] = pattern
+ else:
+ return_files[result_file] =
MatchDetail([pattern])
+
+ else:
+ # Remove files.
+ for file in result_files:
+ del return_files[file]
+
+ return return_files
+
+
+def _is_iterable(value):
+ """
+ Check whether the value is an iterable (excludes strings).
+
+ *value* is the value to check,
+
+ Returns whether *value* is a iterable (:class:`bool`).
+ """
+ return isinstance(value, Iterable) and not isinstance(value, (unicode,
bytes))
+
+
+def iter_tree_entries(root, on_error=None, follow_links=None):
+ """
+ Walks the specified directory for all files and directories.
+
+ *root* (:class:`str`) is the root directory to search.
+
+ *on_error* (:class:`~collections.abc.Callable` or :data:`None`)
+ optionally is the error handler for file-system exceptions. It will be
+ called with the exception (:exc:`OSError`). Reraise the exception to
+ abort the walk. Default is :data:`None` to ignore file-system
+ exceptions.
+
+ *follow_links* (:class:`bool` or :data:`None`) optionally is whether
+ to walk symbolic links that resolve to directories. Default is
+ :data:`None` for :data:`True`.
+
+ Raises :exc:`RecursionError` if recursion is detected.
+
+ Returns an :class:`~collections.abc.Iterable` yielding each file or
+ directory entry (:class:`.TreeEntry`) relative to *root*.
+ """
+ if on_error is not None and not callable(on_error):
+ raise TypeError("on_error:{!r} is not
callable.".format(on_error))
+
+ if follow_links is None:
+ follow_links = True
+
+ for entry in _iter_tree_entries_next(os.path.abspath(root), '', {},
on_error, follow_links):
+ yield entry
+
+
+def iter_tree_files(root, on_error=None, follow_links=None):
"""
Walks the specified directory for all files.
@@ -37,7 +122,7 @@
exceptions.
*follow_links* (:class:`bool` or :data:`None`) optionally is whether
- to walk symbolik links that resolve to directories. Default is
+ to walk symbolic links that resolve to directories. Default is
:data:`None` for :data:`True`.
Raises :exc:`RecursionError` if recursion is detected.
@@ -51,10 +136,16 @@
if follow_links is None:
follow_links = True
- for file_rel in _iter_tree_next(os.path.abspath(root), '', {},
on_error, follow_links):
- yield file_rel
+ for entry in _iter_tree_entries_next(os.path.abspath(root), '', {},
on_error, follow_links):
+ if not entry.is_dir(follow_links):
+ yield entry.path
-def _iter_tree_next(root_full, dir_rel, memo, on_error, follow_links):
+
+# Alias `iter_tree_files()` as `iter_tree()`.
+iter_tree = iter_tree_files
+
+
+def _iter_tree_entries_next(root_full, dir_rel, memo, on_error, follow_links):
"""
Scan the directory for all descendant files.
@@ -64,14 +155,16 @@
*root_full*.
*memo* (:class:`dict`) keeps track of ancestor directories
- encountered. Maps each ancestor real path (:class:`str``) to relative
+ encountered. Maps each ancestor real path (:class:`str`) to relative
path (:class:`str`).
*on_error* (:class:`~collections.abc.Callable` or :data:`None`)
optionally is the error handler for file-system exceptions.
- *follow_links* (:class:`bool`) is whether to walk symbolik links that
+ *follow_links* (:class:`bool`) is whether to walk symbolic links that
resolve to directories.
+
+ Yields each entry (:class:`.TreeEntry`).
"""
dir_full = os.path.join(root_full, dir_rel)
dir_real = os.path.realpath(dir_full)
@@ -84,19 +177,19 @@
else:
raise RecursionError(real_path=dir_real,
first_path=memo[dir_real], second_path=dir_rel)
- for node in os.listdir(dir_full):
- node_rel = os.path.join(dir_rel, node)
+ for node_name in os.listdir(dir_full):
+ node_rel = os.path.join(dir_rel, node_name)
node_full = os.path.join(root_full, node_rel)
# Inspect child node.
try:
- node_stat = os.lstat(node_full)
+ node_lstat = os.lstat(node_full)
except OSError as e:
if on_error is not None:
on_error(e)
continue
- if stat.S_ISLNK(node_stat.st_mode):
+ if stat.S_ISLNK(node_lstat.st_mode):
# Child node is a link, inspect the target node.
is_link = True
try:
@@ -107,24 +200,28 @@
continue
else:
is_link = False
+ node_stat = node_lstat
if stat.S_ISDIR(node_stat.st_mode) and (follow_links or not
is_link):
# Child node is a directory, recurse into it and yield
its
- # decendant files.
- for file_rel in _iter_tree_next(root_full, node_rel,
memo, on_error, follow_links):
- yield file_rel
-
- elif stat.S_ISREG(node_stat.st_mode):
- # Child node is a file, yield it.
- yield node_rel
+ # descendant files.
+ yield TreeEntry(node_name, node_rel, node_lstat,
node_stat)
+
+ for entry in _iter_tree_entries_next(root_full,
node_rel, memo, on_error, follow_links):
+ yield entry
+
+ elif stat.S_ISREG(node_stat.st_mode) or is_link:
+ # Child node is either a file or an unfollowed link,
yield it.
+ yield TreeEntry(node_name, node_rel, node_lstat,
node_stat)
# NOTE: Make sure to remove the canonical (real) path of the directory
# from the ancestors memo once we are done with it. This allows the
# same directory to appear multiple times. If this is not done, the
- # second occurance of the directory will be incorrectly interpreted as
- # a recursion. See
<https://github.com/cpburnz/python-path-specification/pull/7>.
+ # second occurrence of the directory will be incorrectly interpreted
+ # as a recursion. See
<https://github.com/cpburnz/python-path-specification/pull/7>.
del memo[dir_real]
+
def lookup_pattern(name):
"""
Lookups a registered pattern factory by name.
@@ -136,6 +233,7 @@
"""
return _registered_patterns[name]
+
def match_file(patterns, file):
"""
Matches the file to the patterns.
@@ -155,6 +253,7 @@
matched = pattern.include
return matched
+
def match_files(patterns, files):
"""
Matches the files to the patterns.
@@ -167,7 +266,7 @@
Returns the matched files (:class:`set` of :class:`str`).
"""
- all_files = files if isinstance(files, collection_type) else list(files)
+ all_files = files if isinstance(files, Collection) else list(files)
return_files = set()
for pattern in patterns:
if pattern.include is not None:
@@ -178,11 +277,32 @@
return_files.difference_update(result_files)
return return_files
+
+def _normalize_entries(entries, separators=None):
+ """
+ Normalizes the entry paths to use the POSIX path separator.
+
+ *entries* (:class:`~collections.abc.Iterable` of :class:`.TreeEntry`)
+ contains the entries to be normalized.
+
+ *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
+ :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 entry (:class:`.TreeEntry`)
+ """
+ norm_files = {}
+ for entry in entries:
+ norm_files[normalize_file(entry.path, separators=separators)] =
entry
+ return norm_files
+
+
def normalize_file(file, separators=None):
"""
Normalizes the file path to use the POSIX path separator (i.e.,
``'/'``).
- *file* (:class:`str`) is the file path.
+ *file* (:class:`str` or :class:`pathlib.PurePath`) is the file path.
*separators* (:class:`~collections.abc.Collection` of :class:`str`; or
:data:`None`) optionally contains the path separators to normalize.
@@ -196,7 +316,10 @@
# Normalize path separators.
if separators is None:
separators = NORMALIZE_PATH_SEPS
- norm_file = file
+
+ # Convert path object to string.
+ norm_file = str(file)
+
for sep in separators:
norm_file = norm_file.replace(sep, posixpath.sep)
@@ -206,12 +329,13 @@
return norm_file
+
def normalize_files(files, separators=None):
"""
Normalizes the file paths to use the POSIX path separator.
- *files* (:class:`~collections.abc.Iterable` of :class:`str`) contains
- the file paths to be normalized.
+ *files* (:class:`~collections.abc.Iterable` of :class:`str` or
+ :class:`pathlib.PurePath`) contains the file paths to be normalized.
*separators* (:class:`~collections.abc.Collection` of :class:`str`; or
:data:`None`) optionally contains the path separators to normalize.
@@ -225,6 +349,7 @@
norm_files[normalize_file(path, separators=separators)] = path
return norm_files
+
def register_pattern(name, pattern_factory, override=None):
"""
Registers the specified pattern factory.
@@ -348,3 +473,128 @@
:attr:`self.real_path <RecursionError.real_path>`.
"""
return self.args[2]
+
+
+class MatchDetail(object):
+ """
+ The :class:`.MatchDetail` class contains information about
+ """
+
+ #: Make the class dict-less.
+ __slots__ = ('patterns',)
+
+ def __init__(self, patterns):
+ """
+ Initialize the :class:`.MatchDetail` instance.
+
+ *patterns* (:class:`~collections.abc.Sequence` of
:class:`~pathspec.pattern.Pattern`)
+ contains the patterns that matched the file in the order they
were
+ encountered.
+ """
+
+ self.patterns = patterns
+ """
+ *patterns* (:class:`~collections.abc.Sequence` of
:class:`~pathspec.pattern.Pattern`)
+ contains the patterns that matched the file in the order they
were
+ encountered.
+ """
+
+
+class TreeEntry(object):
+ """
+ The :class:`.TreeEntry` class contains information about a file-system
+ entry.
+ """
+
+ #: Make the class dict-less.
+ __slots__ = ('_lstat', 'name', 'path', '_stat')
+
+ def __init__(self, name, path, lstat, stat):
+ """
+ Initialize the :class:`.TreeEntry` instance.
+
+ *name* (:class:`str`) is the base name of the entry.
+
+ *path* (:class:`str`) is the relative path of the entry.
+
+ *lstat* (:class:`~os.stat_result`) is the stat result of the
direct
+ entry.
+
+ *stat* (:class:`~os.stat_result`) is the stat result of the
entry,
+ potentially linked.
+ """
+
+ self._lstat = lstat
+ """
+ *_lstat* (:class:`~os.stat_result`) is the stat result of the
direct
+ entry.
+ """
+
+ self.name = name
+ """
+ *name* (:class:`str`) is the base name of the entry.
+ """
+
+ self.path = path
+ """
+ *path* (:class:`str`) is the path of the entry.
+ """
+
+ self._stat = stat
+ """
+ *_stat* (:class:`~os.stat_result`) is the stat result of the
linked
+ entry.
+ """
+
+ def is_dir(self, follow_links=None):
+ """
+ Get whether the entry is a directory.
+
+ *follow_links* (:class:`bool` or :data:`None`) is whether to
follow
+ symbolic links. If this is :data:`True`, a symlink to a
directory
+ will result in :data:`True`. Default is :data:`None` for
:data:`True`.
+
+ Returns whether the entry is a directory (:class:`bool`).
+ """
+ if follow_links is None:
+ follow_links = True
+
+ node_stat = self._stat if follow_links else self._lstat
+ return stat.S_ISDIR(node_stat.st_mode)
+
+ def is_file(self, follow_links=None):
+ """
+ Get whether the entry is a regular file.
+
+ *follow_links* (:class:`bool` or :data:`None`) is whether to
follow
+ symbolic links. If this is :data:`True`, a symlink to a regular
file
+ will result in :data:`True`. Default is :data:`None` for
:data:`True`.
+
+ Returns whether the entry is a regular file (:class:`bool`).
+ """
+ if follow_links is None:
+ follow_links = True
+
+ node_stat = self._stat if follow_links else self._lstat
+ return stat.S_ISREG(node_stat.st_mode)
+
+ def is_symlink(self):
+ """
+ Returns whether the entry is a symbolic link (:class:`bool`).
+ """
+ return stat.S_ISLNK(self._lstat.st_mode)
+
+ def stat(self, follow_links=None):
+ """
+ Get the cached stat result for the entry.
+
+ *follow_links* (:class:`bool` or :data:`None`) is whether to
follow
+ symbolic links. If this is :data:`True`, the stat result of the
+ linked file will be returned. Default is :data:`None` for
:data:`True`.
+
+ Returns that stat result (:class:`~os.stat_result`).
+ """
+ if follow_links is None:
+ follow_links = True
+
+ return self._stat if follow_links else self._lstat
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pathspec-0.7.0/pathspec.egg-info/PKG-INFO
new/pathspec-0.8.0/pathspec.egg-info/PKG-INFO
--- old/pathspec-0.7.0/pathspec.egg-info/PKG-INFO 2019-12-27
16:31:53.000000000 +0100
+++ new/pathspec-0.8.0/pathspec.egg-info/PKG-INFO 2020-04-09
15:08:33.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: pathspec
-Version: 0.7.0
+Version: 0.8.0
Summary: Utility library for gitignore style pattern matching of file paths.
Home-page: https://github.com/cpburnz/python-path-specification
Author: Caleb P. Burns
@@ -163,6 +163,19 @@
==============
+ 0.8.0 (2020-04-09)
+ ------------------
+
+ - `Issue #30`_: Expose what patterns matched paths. Added
`util.detailed_match_files()`.
+ - `Issue #31`_: `match_tree()` doesn't return symlinks.
+ - Add `PathSpec.match_tree_entries` and `util.iter_tree_entries()` to
support directories and symlinks.
+ - API change: `match_tree()` has been renamed to `match_tree_files()`.
The old name `match_tree()` is still available as an alias.
+ - API change: `match_tree_files()` now returns symlinks. This is a bug
fix but it will change the returned results.
+
+ .. _`Issue #30`:
https://github.com/cpburnz/python-path-specification/issues/30
+ .. _`Issue #31`:
https://github.com/cpburnz/python-path-specification/issues/31
+
+
0.7.0 (2019-12-27)
------------------