Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pathspec for openSUSE:Factory checked in at 2022-01-07 12:44:54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pathspec (Old) and /work/SRC/openSUSE:Factory/.python-pathspec.new.1896 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pathspec" Fri Jan 7 12:44:54 2022 rev:9 rq:943953 version:0.9.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pathspec/python-pathspec.changes 2021-06-11 22:29:53.322048556 +0200 +++ /work/SRC/openSUSE:Factory/.python-pathspec.new.1896/python-pathspec.changes 2022-01-07 12:45:28.031807374 +0100 @@ -1,0 +2,11 @@ +Wed Jan 5 10:29:55 UTC 2022 - Dirk M??ller <dmuel...@suse.com> + +- update to 0.9.0: + * Raise `GitWildMatchPatternError` for invalid git patterns. + * Fix for duplicate leading double-asterisk, and edge cases. + * Fix matching absolute paths. + * API change: `util.normalize_files()` now returns a + `Dict[str, List[pathlike]]` instead of a `Dict[str, pathlike]`. + * Added type hinting. + +------------------------------------------------------------------- Old: ---- pathspec-0.8.1.tar.gz New: ---- pathspec-0.9.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pathspec.spec ++++++ --- /var/tmp/diff_new_pack.8XLIJT/_old 2022-01-07 12:45:28.739807866 +0100 +++ /var/tmp/diff_new_pack.8XLIJT/_new 2022-01-07 12:45:28.743807869 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-pathspec # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-pathspec -Version: 0.8.1 +Version: 0.9.0 Release: 0 Summary: Utility library for gitignore style pattern matching of file paths License: MPL-2.0 ++++++ pathspec-0.8.1.tar.gz -> pathspec-0.9.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/CHANGES.rst new/pathspec-0.9.0/CHANGES.rst --- old/pathspec-0.8.1/CHANGES.rst 2020-11-07 20:31:55.000000000 +0100 +++ new/pathspec-0.9.0/CHANGES.rst 2021-07-18 02:13:17.000000000 +0200 @@ -2,6 +2,21 @@ Change History ============== +0.9.0 (2021-07-17) +------------------ + +- `Issue #44`_/`Issue #50`_: Raise `GitWildMatchPatternError` for invalid git patterns. +- `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]`. +- Added type hinting. + +.. _`Issue #44`: https://github.com/cpburnz/python-path-specification/issues/44 +.. _`Issue #45`: https://github.com/cpburnz/python-path-specification/pull/45 +.. _`Issue #46`: https://github.com/cpburnz/python-path-specification/issues/46 +.. _`Issue #50`: https://github.com/cpburnz/python-path-specification/pull/50 + + 0.8.1 (2020-11-07) ------------------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/MANIFEST.in new/pathspec-0.9.0/MANIFEST.in --- old/pathspec-0.8.1/MANIFEST.in 2020-01-29 03:45:39.000000000 +0100 +++ new/pathspec-0.9.0/MANIFEST.in 2021-06-03 05:48:54.000000000 +0200 @@ -1,2 +1,3 @@ +include *.py include *.rst include LICENSE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/PKG-INFO new/pathspec-0.9.0/PKG-INFO --- old/pathspec-0.8.1/PKG-INFO 2020-11-07 20:37:55.000000000 +0100 +++ new/pathspec-0.9.0/PKG-INFO 2021-07-18 02:27:56.000000000 +0200 @@ -1,12 +1,13 @@ -Metadata-Version: 1.2 +Metadata-Version: 2.1 Name: pathspec -Version: 0.8.1 +Version: 0.9.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 Author-email: cpbu...@gmail.com License: MPL 2.0 -Description: *pathspec*: Path Specification +Description: + *pathspec*: Path Specification ============================== *pathspec* is a utility library for pattern matching of file paths. So @@ -159,9 +160,25 @@ .. _`Ruby gem`: https://github.com/highb/pathspec-ruby + Change History ============== + 0.9.0 (2021-07-17) + ------------------ + + - `Issue #44`_/`Issue #50`_: Raise `GitWildMatchPatternError` for invalid git patterns. + - `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]`. + - Added type hinting. + + .. _`Issue #44`: https://github.com/cpburnz/python-path-specification/issues/44 + .. _`Issue #45`: https://github.com/cpburnz/python-path-specification/pull/45 + .. _`Issue #46`: https://github.com/cpburnz/python-path-specification/issues/46 + .. _`Issue #50`: https://github.com/cpburnz/python-path-specification/pull/50 + + 0.8.1 (2020-11-07) ------------------ @@ -369,6 +386,7 @@ ------------------ - Initial release. + Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers @@ -382,8 +400,10 @@ Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Utilities -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7 +Description-Content-Type: text/x-rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec/__init__.py new/pathspec-0.9.0/pathspec/__init__.py --- old/pathspec-0.8.1/pathspec/__init__.py 2020-11-07 20:35:24.000000000 +0100 +++ new/pathspec-0.9.0/pathspec/__init__.py 2021-06-03 05:48:54.000000000 +0200 @@ -23,44 +23,18 @@ """ from __future__ import unicode_literals -__author__ = "Caleb P. Burns" -__copyright__ = "Copyright ?? 2013-2020 Caleb P. Burns" -__created__ = "2013-10-12" -__credits__ = [ - "dahlia <https://github.com/dahlia>", - "highb <https://github.com/highb>", - "029xue <https://github.com/029xue>", - "mikexstudios <https://github.com/mikexstudios>", - "nhumrich <https://github.com/nhumrich>", - "davidfraser <https://github.com/davidfraser>", - "demurgos <https://github.com/demurgos>", - "ghickman <https://github.com/ghickman>", - "nvie <https://github.com/nvie>", - "adrienverge <https://github.com/adrienverge>", - "AndersBlomdell <https://github.com/AndersBlomdell>", - "highb <https://github.com/highb>", - "thmxv <https://github.com/thmxv>", - "wimglenn <https://github.com/wimglenn>", - "hugovk <https://github.com/hugovk>", - "dcecile <https://github.com/dcecile>", - "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>", - "nhhollander <https://github.com/nhhollander>", -] -__email__ = "cpbu...@gmail.com" -__license__ = "MPL 2.0" -__project__ = "pathspec" -__status__ = "Development" -__updated__ = "2020-11-07" -__version__ = "0.8.1" - from .pathspec import PathSpec from .pattern import Pattern, RegexPattern from .util import iter_tree, lookup_pattern, match_files, RecursionError +from ._meta import ( + __author__, + __copyright__, + __credits__, + __license__, + __version__, +) + # Load pattern implementations. from . import patterns diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec/_meta.py new/pathspec-0.9.0/pathspec/_meta.py --- old/pathspec-0.8.1/pathspec/_meta.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pathspec-0.9.0/pathspec/_meta.py 2021-07-18 02:13:02.000000000 +0200 @@ -0,0 +1,43 @@ +# encoding: utf-8 +""" +This module contains the project meta-data. +""" + +__author__ = "Caleb P. Burns" +__copyright__ = "Copyright ?? 2013-2021 Caleb P. Burns" +__credits__ = [ + "dahlia <https://github.com/dahlia>", + "highb <https://github.com/highb>", + "029xue <https://github.com/029xue>", + "mikexstudios <https://github.com/mikexstudios>", + "nhumrich <https://github.com/nhumrich>", + "davidfraser <https://github.com/davidfraser>", + "demurgos <https://github.com/demurgos>", + "ghickman <https://github.com/ghickman>", + "nvie <https://github.com/nvie>", + "adrienverge <https://github.com/adrienverge>", + "AndersBlomdell <https://github.com/AndersBlomdell>", + "highb <https://github.com/highb>", + "thmxv <https://github.com/thmxv>", + "wimglenn <https://github.com/wimglenn>", + "hugovk <https://github.com/hugovk>", + "dcecile <https://github.com/dcecile>", + "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>", + "nhhollander <https://github.com/nhhollander>", + "KOLANICH <https://github.com/KOLANICH>", + "JonjonHays <https://github.com/JonjonHays>", + "Isaac0616 <https://github.com/Isaac0616>", + "SebastiaanZ <https://github.com/SebastiaanZ>", + "RoelAdriaans <https://github.com/RoelAdriaans>", + "raviselker <https://github.com/raviselker>", + "johanvergeer <https://github.com/johanvergeer>", + "danjer <https://github.com/danjer>", + "jhbuhrman <https://github.com/jhbuhrman>", + "WPDOrdina <https://github.com/WPDOrdina>", +] +__license__ = "MPL 2.0" +__version__ = "0.9.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec/compat.py new/pathspec-0.9.0/pathspec/compat.py --- old/pathspec-0.8.1/pathspec/compat.py 2020-04-07 05:10:57.000000000 +0200 +++ new/pathspec-0.9.0/pathspec/compat.py 2021-06-12 18:47:43.000000000 +0200 @@ -36,3 +36,6 @@ except ImportError: # Python 2.7 - 3.5. from collections import Container as Collection + +CollectionType = Collection +IterableType = Iterable diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec/pathspec.py new/pathspec-0.9.0/pathspec/pathspec.py --- old/pathspec-0.8.1/pathspec/pathspec.py 2020-11-07 20:27:35.000000000 +0100 +++ new/pathspec-0.9.0/pathspec/pathspec.py 2021-06-12 20:06:53.000000000 +0200 @@ -4,8 +4,34 @@ of files. """ +try: + from typing import ( + Any, + AnyStr, + Callable, + Iterable, + Iterator, + Optional, + Text, + Union) +except ImportError: + pass + +try: + # Python 3.6+ type hints. + from os import PathLike + from typing import Collection +except ImportError: + pass + from . import util -from .compat import Collection, iterkeys, izip_longest, string_types, unicode +from .compat import ( + CollectionType, + iterkeys, + izip_longest, + string_types) +from .pattern import Pattern +from .util import TreeEntry class PathSpec(object): @@ -15,6 +41,7 @@ """ def __init__(self, patterns): + # type: (Iterable[Pattern]) -> None """ Initializes the :class:`PathSpec` instance. @@ -22,13 +49,14 @@ yields each compiled pattern (:class:`.Pattern`). """ - self.patterns = patterns if isinstance(patterns, Collection) else list(patterns) + self.patterns = patterns if isinstance(patterns, CollectionType) else list(patterns) """ *patterns* (:class:`~collections.abc.Collection` of :class:`.Pattern`) contains the compiled patterns. """ def __eq__(self, other): + # type: (PathSpec) -> bool """ Tests the equality of this path-spec with *other* (:class:`PathSpec`) by comparing their :attr:`~PathSpec.patterns` attributes. @@ -47,6 +75,7 @@ return len(self.patterns) def __add__(self, other): + # type: (PathSpec) -> PathSpec """ Combines the :attr:`Pathspec.patterns` patterns from two :class:`PathSpec` instances. @@ -57,6 +86,7 @@ return NotImplemented def __iadd__(self, other): + # type: (PathSpec) -> PathSpec """ Adds the :attr:`Pathspec.patterns` patterns from one :class:`PathSpec` instance to this instance. @@ -69,6 +99,7 @@ @classmethod def from_lines(cls, pattern_factory, lines): + # type: (Union[Text, Callable[[AnyStr], Pattern]], Iterable[AnyStr]) -> PathSpec """ Compiles the pattern lines. @@ -92,10 +123,11 @@ 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] - return cls(lines) + patterns = [pattern_factory(line) for line in lines if line] + return cls(patterns) def match_file(self, file, separators=None): + # type: (Union[Text, PathLike], Optional[Collection[Text]]) -> bool """ Matches the file to this path-spec. @@ -112,6 +144,7 @@ return util.match_file(self.patterns, norm_file) def match_entries(self, entries, separators=None): + # type: (Iterable[TreeEntry], Optional[Collection[Text]]) -> Iterator[TreeEntry] """ Matches the entries to this path-spec. @@ -123,7 +156,7 @@ normalize. See :func:`~pathspec.util.normalize_file` for more information. - Returns the matched entries (:class:`~collections.abc.Iterable` of + Returns the matched entries (:class:`~collections.abc.Iterator` of :class:`~util.TreeEntry`). """ if not util._is_iterable(entries): @@ -135,6 +168,7 @@ yield entry_map[path] def match_files(self, files, separators=None): + # type: (Iterable[Union[Text, PathLike]], Optional[Collection[Text]]) -> Iterator[Union[Text, PathLike]] """ Matches the files to this path-spec. @@ -147,18 +181,20 @@ normalize. See :func:`~pathspec.util.normalize_file` for more information. - Returns the matched files (:class:`~collections.abc.Iterable` of - :class:`str`). + Returns the matched files (:class:`~collections.abc.Iterator` of + :class:`str` or :class:`pathlib.PurePath`). """ if not util._is_iterable(files): raise TypeError("files:{!r} is not an iterable.".format(files)) 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): + # type: (Text, Optional[Callable], Optional[bool]) -> Iterator[TreeEntry] """ Walks the specified root path for all files and matches them to this path-spec. @@ -174,13 +210,14 @@ 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`). + Returns the matched files (:class:`~collections.abc.Iterator` of + :class:`.TreeEntry`). """ 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): + # type: (Text, Optional[Callable], Optional[bool]) -> Iterator[Text] """ Walks the specified root path for all files and matches them to this path-spec. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec/pattern.py new/pathspec-0.9.0/pathspec/pattern.py --- old/pathspec-0.8.1/pathspec/pattern.py 2020-02-03 04:22:42.000000000 +0100 +++ new/pathspec-0.9.0/pathspec/pattern.py 2021-06-12 19:34:35.000000000 +0200 @@ -4,6 +4,18 @@ """ import re +try: + from typing import ( + AnyStr, + Iterable, + Iterator, + Optional, + Pattern as RegexHint, + Text, + Tuple, + Union) +except ImportError: + pass from .compat import unicode @@ -17,6 +29,7 @@ __slots__ = ('include',) def __init__(self, include): + # type: (Optional[bool]) -> None """ Initializes the :class:`Pattern` instance. @@ -33,6 +46,7 @@ """ def match(self, files): + # type: (Iterable[Text]) -> Iterator[Text] """ Matches this pattern against the specified files. @@ -55,6 +69,7 @@ __slots__ = ('regex',) def __init__(self, pattern, include=None): + # type: (Union[AnyStr, RegexHint], Optional[bool]) -> None """ Initializes the :class:`RegexPattern` instance. @@ -103,6 +118,7 @@ self.regex = regex def __eq__(self, other): + # type: (RegexPattern) -> bool """ Tests the equality of this regex pattern with *other* (:class:`RegexPattern`) by comparing their :attr:`~Pattern.include` and :attr:`~RegexPattern.regex` @@ -114,6 +130,7 @@ return NotImplemented def match(self, files): + # type: (Iterable[Text]) -> Iterable[Text] """ Matches this pattern against the specified files. @@ -130,6 +147,7 @@ @classmethod def pattern_to_regex(cls, pattern): + # type: (Text) -> Tuple[Text, bool] """ Convert the pattern into an uncompiled regular expression. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec/patterns/gitwildmatch.py new/pathspec-0.9.0/pathspec/patterns/gitwildmatch.py --- old/pathspec-0.8.1/pathspec/patterns/gitwildmatch.py 2020-02-03 04:22:42.000000000 +0100 +++ new/pathspec-0.9.0/pathspec/patterns/gitwildmatch.py 2021-07-18 02:05:09.000000000 +0200 @@ -8,6 +8,14 @@ import re import warnings +try: + from typing import ( + AnyStr, + Optional, + Text, + Tuple) +except ImportError: + pass from .. import util from ..compat import unicode @@ -17,6 +25,14 @@ _BYTES_ENCODING = 'latin1' +class GitWildMatchPatternError(ValueError): + """ + The :class:`GitWildMatchPatternError` indicates an invalid git wild match + pattern. + """ + pass + + class GitWildMatchPattern(RegexPattern): """ The :class:`GitWildMatchPattern` class represents a compiled Git @@ -28,6 +44,7 @@ @classmethod def pattern_to_regex(cls, pattern): + # type: (AnyStr) -> Tuple[Optional[AnyStr], Optional[bool]] """ Convert the pattern into a regular expression. @@ -47,6 +64,7 @@ else: raise TypeError("pattern:{!r} is not a unicode or byte string.".format(pattern)) + original_pattern = pattern pattern = pattern.strip() if pattern.startswith('#'): @@ -63,7 +81,6 @@ include = None elif pattern: - if pattern.startswith('!'): # A pattern starting with an exclamation mark ('!') negates the # pattern (exclude instead of include). Escape the exclamation @@ -80,11 +97,31 @@ # exclamation mark ('!'). pattern = pattern[1:] + # Allow a regex override for edge cases that cannot be handled + # through normalization. + override_regex = None + # Split pattern into segments. pattern_segs = pattern.split('/') # Normalize pattern to make processing easier. + # EDGE CASE: Deal with duplicate double-asterisk sequences. + # Collapse each sequence down to one double-asterisk. Iterate over + # the segments in reverse and remove the duplicate double + # asterisks as we go. + for i in range(len(pattern_segs) - 1, 0, -1): + prev = pattern_segs[i-1] + seg = pattern_segs[i] + if prev == '**' and seg == '**': + del pattern_segs[i] + + if len(pattern_segs) == 2 and pattern_segs[0] == '**' and not pattern_segs[1]: + # EDGE CASE: The '**/' pattern should match everything except + # individual files in the root directory. This case cannot be + # adequately handled through normalization. Use the override. + override_regex = '^.+/.*$' + if not pattern_segs[0]: # A pattern beginning with a slash ('/') will only match paths # directly on the root directory instead of any descendant @@ -109,58 +146,75 @@ # according to `git check-ignore` (v2.4.1). pass + if not pattern_segs: + # After resolving the edge cases, we end up with no + # pattern at all. This must be because the pattern is + # invalid. + raise GitWildMatchPatternError("Invalid git pattern: %r" % (original_pattern,)) + if not pattern_segs[-1] and len(pattern_segs) > 1: - # A pattern ending with a slash ('/') will match all descendant - # paths if it is a directory but not if it is a regular file. - # This is equivilent to "{pattern}/**". So, set last segment to - # double asterisks to include all descendants. + # A pattern ending with a slash ('/') will match all + # descendant paths if it is a directory but not if it is a + # regular file. This is equivalent to "{pattern}/**". So, set + # last segment to a double-asterisk to include all + # descendants. pattern_segs[-1] = '**' - # Build regular expression from pattern. - output = ['^'] - need_slash = False - end = len(pattern_segs) - 1 - for i, seg in enumerate(pattern_segs): - if seg == '**': - if i == 0 and i == end: - # A pattern consisting solely of double-asterisks ('**') - # will match every path. - output.append('.+') - elif i == 0: - # A normalized pattern beginning with double-asterisks - # ('**') will match any leading path segments. - output.append('(?:.+/)?') - need_slash = False - elif i == end: - # A normalized pattern ending with double-asterisks ('**') - # will match any trailing path segments. - output.append('/.*') + if override_regex is None: + # Build regular expression from pattern. + output = ['^'] + need_slash = False + end = len(pattern_segs) - 1 + for i, seg in enumerate(pattern_segs): + if seg == '**': + if i == 0 and i == end: + # A pattern consisting solely of double-asterisks ('**') + # will match every path. + output.append('.+') + elif i == 0: + # A normalized pattern beginning with double-asterisks + # ('**') will match any leading path segments. + output.append('(?:.+/)?') + need_slash = False + elif i == end: + # A normalized pattern ending with double-asterisks ('**') + # will match any trailing path segments. + output.append('/.*') + else: + # A pattern with inner double-asterisks ('**') will match + # multiple (or zero) inner path segments. + output.append('(?:/.+)?') + need_slash = True + + elif seg == '*': + # Match single path segment. + if need_slash: + output.append('/') + output.append('[^/]+') + need_slash = True + else: - # A pattern with inner double-asterisks ('**') will match - # multiple (or zero) inner path segments. - output.append('(?:/.+)?') + # Match segment glob pattern. + if need_slash: + output.append('/') + + output.append(cls._translate_segment_glob(seg)) + if i == end and include is True: + # A pattern ending without a slash ('/') will match a file + # or a directory (with paths underneath it). E.g., "foo" + # matches "foo", "foo/bar", "foo/bar/baz", etc. + # EDGE CASE: However, this does not hold for exclusion cases + # according to `git check-ignore` (v2.4.1). + output.append('(?:/.*)?') + need_slash = True - elif seg == '*': - # Match single path segment. - if need_slash: - output.append('/') - output.append('[^/]+') - need_slash = True - else: - # Match segment glob pattern. - if need_slash: - output.append('/') - output.append(cls._translate_segment_glob(seg)) - if i == end and include is True: - # A pattern ending without a slash ('/') will match a file - # or a directory (with paths underneath it). E.g., "foo" - # matches "foo", "foo/bar", "foo/bar/baz", etc. - # EDGE CASE: However, this does not hold for exclusion cases - # according to `git check-ignore` (v2.4.1). - output.append('(?:/.*)?') - need_slash = True - output.append('$') - regex = ''.join(output) + + output.append('$') + regex = ''.join(output) + + else: + # Use regex override. + regex = override_regex else: # A blank pattern is a null-operation (neither includes nor @@ -175,6 +229,7 @@ @staticmethod def _translate_segment_glob(pattern): + # type: (Text) -> Text """ Translates the glob pattern to a regular expression. This is used in the constructor to translate a path segment glob pattern to its @@ -215,28 +270,28 @@ regex += '[^/]' elif char == '[': - # Braket expression wildcard. Except for the beginning - # exclamation mark, the whole braket expression can be used + # Bracket expression wildcard. Except for the beginning + # exclamation mark, the whole bracket expression can be used # directly as regex but we have to find where the expression # ends. - # - "[][!]" matchs ']', '[' and '!'. - # - "[]-]" matchs ']' and '-'. - # - "[!]a-]" matchs any character except ']', 'a' and '-'. + # - "[][!]" matches ']', '[' and '!'. + # - "[]-]" matches ']' and '-'. + # - "[!]a-]" matches any character except ']', 'a' and '-'. j = i # Pass brack expression negation. if j < end and pattern[j] == '!': j += 1 - # Pass first closing braket if it is at the beginning of the + # Pass first closing bracket if it is at the beginning of the # expression. if j < end and pattern[j] == ']': j += 1 - # Find closing braket. Stop once we reach the end or find it. + # Find closing bracket. Stop once we reach the end or find it. while j < end and pattern[j] != ']': j += 1 if j < end: - # Found end of braket expression. Increment j to be one past - # the closing braket: + # Found end of bracket expression. Increment j to be one past + # the closing bracket: # # [...] # ^ ^ @@ -250,7 +305,7 @@ expr += '^' i += 1 elif pattern[i] == '^': - # POSIX declares that the regex braket expression negation + # POSIX declares that the regex bracket expression negation # "[^...]" is undefined in a glob pattern. Python's # `fnmatch.translate()` escapes the caret ('^') as a # literal. To maintain consistency with undefined behavior, @@ -258,19 +313,19 @@ expr += '\\^' i += 1 - # Build regex braket expression. Escape slashes so they are + # Build regex bracket expression. Escape slashes so they are # treated as literal slashes by regex as defined by POSIX. expr += pattern[i:j].replace('\\', '\\\\') - # Add regex braket expression to regex result. + # Add regex bracket expression to regex result. regex += expr - # Set i to one past the closing braket. + # Set i to one past the closing bracket. i = j else: - # Failed to find closing braket, treat opening braket as a - # braket literal instead of as an expression. + # Failed to find closing bracket, treat opening bracket as a + # bracket literal instead of as an expression. regex += '\\[' else: @@ -281,18 +336,33 @@ @staticmethod def escape(s): + # type: (AnyStr) -> AnyStr """ Escape special characters in the given string. *s* (:class:`unicode` or :class:`bytes`) a filename or a string that you want to escape, usually before adding it to a `.gitignore` - Returns the escaped string (:class:`unicode`, :class:`bytes`) + Returns the escaped string (:class:`unicode` or :class:`bytes`) """ + if isinstance(s, unicode): + return_type = unicode + string = s + elif isinstance(s, bytes): + return_type = bytes + string = s.decode(_BYTES_ENCODING) + else: + raise TypeError("s:{!r} is not a unicode or byte string.".format(s)) + # Reference: https://git-scm.com/docs/gitignore#_pattern_format meta_characters = r"[]!*#?" - return "".join("\\" + x if x in meta_characters else x for x in s) + out_string = "".join("\\" + x if x in meta_characters else x for x in string) + + if return_type is bytes: + return out_string.encode(_BYTES_ENCODING) + else: + return out_string util.register_pattern('gitwildmatch', GitWildMatchPattern) @@ -308,7 +378,7 @@ Warn about deprecation. """ self._deprecated() - return super(GitIgnorePattern, self).__init__(*args, **kw) + super(GitIgnorePattern, self).__init__(*args, **kw) @staticmethod def _deprecated(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec/tests/test_gitwildmatch.py new/pathspec-0.9.0/pathspec/tests/test_gitwildmatch.py --- old/pathspec-0.8.1/pathspec/tests/test_gitwildmatch.py 2020-02-03 04:22:42.000000000 +0100 +++ new/pathspec-0.9.0/pathspec/tests/test_gitwildmatch.py 2021-07-18 02:15:39.000000000 +0200 @@ -10,7 +10,7 @@ import pathspec.patterns.gitwildmatch import pathspec.util -from pathspec.patterns.gitwildmatch import GitWildMatchPattern +from pathspec.patterns.gitwildmatch import GitWildMatchPattern, GitWildMatchPatternError if sys.version_info[0] >= 3: unichr = chr @@ -184,6 +184,7 @@ This should match: + left/right left/bar/right left/foo/bar/right left/bar/right/foo @@ -198,12 +199,14 @@ pattern = GitWildMatchPattern(re.compile(regex), include) results = set(pattern.match([ + 'left/right', 'left/bar/right', 'left/foo/bar/right', 'left/bar/right/foo', 'foo/left/bar/right', ])) self.assertEqual(results, { + 'left/right', 'left/bar/right', 'left/foo/bar/right', 'left/bar/right/foo', @@ -223,6 +226,7 @@ This should match: + spam foo/spam foo/spam/bar """ @@ -232,14 +236,66 @@ pattern = GitWildMatchPattern(re.compile(regex), include) results = set(pattern.match([ + 'spam', 'foo/spam', 'foo/spam/bar', ])) self.assertEqual(results, { + 'spam', 'foo/spam', 'foo/spam/bar', }) + def test_03_duplicate_leading_double_asterisk_edge_case(self): + """ + Regression test for duplicate leading **/ bug. + """ + regex, include = GitWildMatchPattern.pattern_to_regex('**') + self.assertTrue(include) + self.assertEqual(regex, '^.+$') + + equivalent_regex, include = GitWildMatchPattern.pattern_to_regex('**/**') + self.assertTrue(include) + self.assertEqual(equivalent_regex, regex) + + equivalent_regex, include = GitWildMatchPattern.pattern_to_regex('**/**/**') + self.assertTrue(include) + self.assertEqual(equivalent_regex, regex) + + regex, include = GitWildMatchPattern.pattern_to_regex('**/api') + self.assertTrue(include) + self.assertEqual(regex, '^(?:.+/)?api(?:/.*)?$') + + equivalent_regex, include = GitWildMatchPattern.pattern_to_regex('**/**/api') + self.assertTrue(include) + self.assertEqual(equivalent_regex, regex) + + regex, include = GitWildMatchPattern.pattern_to_regex('**/api/') + self.assertTrue(include) + self.assertEqual(regex, '^(?:.+/)?api/.*$') + + equivalent_regex, include = GitWildMatchPattern.pattern_to_regex('**/api/**') + self.assertTrue(include) + self.assertEqual(equivalent_regex, regex) + + equivalent_regex, include = GitWildMatchPattern.pattern_to_regex('**/**/api/**/**') + self.assertTrue(include) + self.assertEqual(equivalent_regex, regex) + + def test_03_double_asterisk_trailing_slash_edge_case(self): + """ + Tests the edge-case **/ pattern. + + This should match everything except individual files in the root directory. + """ + regex, include = GitWildMatchPattern.pattern_to_regex('**/') + self.assertTrue(include) + self.assertEqual(regex, '^.+/.*$') + + equivalent_regex, include = GitWildMatchPattern.pattern_to_regex('**/**/') + self.assertTrue(include) + self.assertEqual(equivalent_regex, regex) + def test_04_infix_wildcard(self): """ Tests a pattern with an infix wildcard. @@ -472,3 +528,21 @@ escaped = r"file\!with\*weird\#naming_\[1\].t\?t" result = GitWildMatchPattern.escape(fname) self.assertEqual(result, escaped) + + def test_09_single_escape_fail(self): + """ + Test an escape on a line by itself. + """ + self._check_invalid_pattern("\\") + + def test_09_single_exclamation_mark_fail(self): + """ + Test an escape on a line by itself. + """ + self._check_invalid_pattern("!") + + def _check_invalid_pattern(self, git_ignore_pattern): + expected_message_pattern = re.escape(repr(git_ignore_pattern)) + with self.assertRaisesRegexp(GitWildMatchPatternError, expected_message_pattern): + GitWildMatchPattern(git_ignore_pattern) + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec/tests/test_pathspec.py new/pathspec-0.9.0/pathspec/tests/test_pathspec.py --- old/pathspec-0.8.1/pathspec/tests/test_pathspec.py 2020-11-07 20:27:35.000000000 +0100 +++ new/pathspec-0.9.0/pathspec/tests/test_pathspec.py 2021-06-12 18:27:25.000000000 +0200 @@ -13,6 +13,52 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec/util.py new/pathspec-0.9.0/pathspec/util.py --- old/pathspec-0.8.1/pathspec/util.py 2020-04-07 05:43:06.000000000 +0200 +++ new/pathspec-0.9.0/pathspec/util.py 2021-06-12 19:28:58.000000000 +0200 @@ -7,8 +7,35 @@ import os.path import posixpath import stat - -from .compat import Collection, Iterable, string_types, unicode +try: + from typing import ( + Any, + AnyStr, + Callable, + Dict, + Iterable, + Iterator, + List, + Optional, + Sequence, + Set, + Text, + Union) +except ImportError: + pass +try: + # Python 3.6+ type hints. + from os import PathLike + from typing import Collection +except ImportError: + pass + +from .compat import ( + CollectionType, + IterableType, + string_types, + unicode) +from .pattern import Pattern NORMALIZE_PATH_SEPS = [sep for sep in [os.sep, os.altsep] if sep and sep != posixpath.sep] """ @@ -26,6 +53,7 @@ def detailed_match_files(patterns, files, all_matches=None): + # type: (Iterable[Pattern], Iterable[Text], Optional[bool]) -> Dict[Text, 'MatchDetail'] """ Matches the files to the patterns, and returns which patterns matched the files. @@ -43,7 +71,7 @@ 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) + all_files = files if isinstance(files, CollectionType) else list(files) return_files = {} for pattern in patterns: if pattern.include is not None: @@ -68,6 +96,7 @@ def _is_iterable(value): + # type: (Any) -> bool """ Check whether the value is an iterable (excludes strings). @@ -75,10 +104,11 @@ Returns whether *value* is a iterable (:class:`bool`). """ - return isinstance(value, Iterable) and not isinstance(value, (unicode, bytes)) + return isinstance(value, IterableType) and not isinstance(value, (unicode, bytes)) def iter_tree_entries(root, on_error=None, follow_links=None): + # type: (Text, Optional[Callable], Optional[bool]) -> Iterator['TreeEntry'] """ Walks the specified directory for all files and directories. @@ -96,7 +126,7 @@ Raises :exc:`RecursionError` if recursion is detected. - Returns an :class:`~collections.abc.Iterable` yielding each file or + Returns an :class:`~collections.abc.Iterator` yielding each file or directory entry (:class:`.TreeEntry`) relative to *root*. """ if on_error is not None and not callable(on_error): @@ -110,6 +140,7 @@ def iter_tree_files(root, on_error=None, follow_links=None): + # type: (Text, Optional[Callable], Optional[bool]) -> Iterator[Text] """ Walks the specified directory for all files. @@ -127,7 +158,7 @@ Raises :exc:`RecursionError` if recursion is detected. - Returns an :class:`~collections.abc.Iterable` yielding the path to + Returns an :class:`~collections.abc.Iterator` yielding the path to each file (:class:`str`) relative to *root*. """ if on_error is not None and not callable(on_error): @@ -146,6 +177,7 @@ def _iter_tree_entries_next(root_full, dir_rel, memo, on_error, follow_links): + # type: (Text, Text, Dict[Text, Text], Callable, bool) -> Iterator['TreeEntry'] """ Scan the directory for all descendant files. @@ -223,6 +255,7 @@ def lookup_pattern(name): + # type: (Text) -> Callable[[AnyStr], Pattern] """ Lookups a registered pattern factory by name. @@ -235,6 +268,7 @@ def match_file(patterns, file): + # type: (Iterable[Pattern], Text) -> bool """ Matches the file to the patterns. @@ -255,6 +289,7 @@ def match_files(patterns, files): + # type: (Iterable[Pattern], Iterable[Text]) -> Set[Text] """ Matches the files to the patterns. @@ -266,7 +301,7 @@ Returns the matched files (:class:`set` of :class:`str`). """ - all_files = files if isinstance(files, Collection) else list(files) + all_files = files if isinstance(files, CollectionType) else list(files) return_files = set() for pattern in patterns: if pattern.include is not None: @@ -279,6 +314,7 @@ def _normalize_entries(entries, separators=None): + # type: (Iterable['TreeEntry'], Optional[Collection[Text]]) -> Dict[Text, 'TreeEntry'] """ Normalizes the entry paths to use the POSIX path separator. @@ -299,8 +335,10 @@ def normalize_file(file, separators=None): + # type: (Union[Text, PathLike], Optional[Collection[Text]]) -> Text """ - 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,14 +361,19 @@ 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 def normalize_files(files, separators=None): + # type: (Iterable[Union[str, PathLike]], Optional[Collection[Text]]) -> Dict[Text, List[Union[str, PathLike]]] """ Normalizes the file paths to use the POSIX path separator. @@ -341,16 +384,23 @@ :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` or :class:`pathlib.PurePath`). """ 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 def register_pattern(name, pattern_factory, override=None): + # type: (Text, Callable[[AnyStr], Pattern], Optional[bool]) -> None """ Registers the specified pattern factory. @@ -382,6 +432,7 @@ """ def __init__(self, name, pattern_factory): + # type: (Text, Callable[[AnyStr], Pattern]) -> None """ Initializes the :exc:`AlreadyRegisteredError` instance. @@ -394,6 +445,7 @@ @property def message(self): + # type: () -> Text """ *message* (:class:`str`) is the error message. """ @@ -404,6 +456,7 @@ @property def name(self): + # type: () -> Text """ *name* (:class:`str`) is the name of the registered pattern. """ @@ -411,6 +464,7 @@ @property def pattern_factory(self): + # type: () -> Callable[[AnyStr], Pattern] """ *pattern_factory* (:class:`~collections.abc.Callable`) is the registered pattern factory. @@ -425,6 +479,7 @@ """ def __init__(self, real_path, first_path, second_path): + # type: (Text, Text, Text) -> None """ Initializes the :exc:`RecursionError` instance. @@ -441,6 +496,7 @@ @property def first_path(self): + # type: () -> Text """ *first_path* (:class:`str`) is the first path encountered for :attr:`self.real_path <RecursionError.real_path>`. @@ -449,6 +505,7 @@ @property def message(self): + # type: () -> Text """ *message* (:class:`str`) is the error message. """ @@ -460,6 +517,7 @@ @property def real_path(self): + # type: () -> Text """ *real_path* (:class:`str`) is the real path that recursion was encountered on. @@ -468,6 +526,7 @@ @property def second_path(self): + # type: () -> Text """ *second_path* (:class:`str`) is the second path encountered for :attr:`self.real_path <RecursionError.real_path>`. @@ -484,6 +543,7 @@ __slots__ = ('patterns',) def __init__(self, patterns): + # type: (Sequence[Pattern]) -> None """ Initialize the :class:`.MatchDetail` instance. @@ -510,6 +570,7 @@ __slots__ = ('_lstat', 'name', 'path', '_stat') def __init__(self, name, path, lstat, stat): + # type: (Text, Text, os.stat_result, os.stat_result) -> None """ Initialize the :class:`.TreeEntry` instance. @@ -547,6 +608,7 @@ """ def is_dir(self, follow_links=None): + # type: (Optional[bool]) -> bool """ Get whether the entry is a directory. @@ -563,6 +625,7 @@ return stat.S_ISDIR(node_stat.st_mode) def is_file(self, follow_links=None): + # type: (Optional[bool]) -> bool """ Get whether the entry is a regular file. @@ -579,12 +642,14 @@ return stat.S_ISREG(node_stat.st_mode) def is_symlink(self): + # type: () -> bool """ Returns whether the entry is a symbolic link (:class:`bool`). """ return stat.S_ISLNK(self._lstat.st_mode) def stat(self, follow_links=None): + # type: (Optional[bool]) -> os.stat_result """ Get the cached stat result for the entry. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec.egg-info/PKG-INFO new/pathspec-0.9.0/pathspec.egg-info/PKG-INFO --- old/pathspec-0.8.1/pathspec.egg-info/PKG-INFO 2020-11-07 20:37:54.000000000 +0100 +++ new/pathspec-0.9.0/pathspec.egg-info/PKG-INFO 2021-07-18 02:27:56.000000000 +0200 @@ -1,12 +1,13 @@ -Metadata-Version: 1.2 +Metadata-Version: 2.1 Name: pathspec -Version: 0.8.1 +Version: 0.9.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 Author-email: cpbu...@gmail.com License: MPL 2.0 -Description: *pathspec*: Path Specification +Description: + *pathspec*: Path Specification ============================== *pathspec* is a utility library for pattern matching of file paths. So @@ -159,9 +160,25 @@ .. _`Ruby gem`: https://github.com/highb/pathspec-ruby + Change History ============== + 0.9.0 (2021-07-17) + ------------------ + + - `Issue #44`_/`Issue #50`_: Raise `GitWildMatchPatternError` for invalid git patterns. + - `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]`. + - Added type hinting. + + .. _`Issue #44`: https://github.com/cpburnz/python-path-specification/issues/44 + .. _`Issue #45`: https://github.com/cpburnz/python-path-specification/pull/45 + .. _`Issue #46`: https://github.com/cpburnz/python-path-specification/issues/46 + .. _`Issue #50`: https://github.com/cpburnz/python-path-specification/pull/50 + + 0.8.1 (2020-11-07) ------------------ @@ -369,6 +386,7 @@ ------------------ - Initial release. + Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers @@ -382,8 +400,10 @@ Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Utilities -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7 +Description-Content-Type: text/x-rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec.egg-info/SOURCES.txt new/pathspec-0.9.0/pathspec.egg-info/SOURCES.txt --- old/pathspec-0.8.1/pathspec.egg-info/SOURCES.txt 2020-11-07 20:37:54.000000000 +0100 +++ new/pathspec-0.9.0/pathspec.egg-info/SOURCES.txt 2021-07-18 02:27:56.000000000 +0200 @@ -2,9 +2,12 @@ LICENSE MANIFEST.in README.rst +pathspec_meta.py setup.cfg setup.py +tox_pip_install.py pathspec/__init__.py +pathspec/_meta.py pathspec/compat.py pathspec/pathspec.py pathspec/pattern.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/pathspec_meta.py new/pathspec-0.9.0/pathspec_meta.py --- old/pathspec-0.8.1/pathspec_meta.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pathspec-0.9.0/pathspec_meta.py 2021-07-18 02:13:02.000000000 +0200 @@ -0,0 +1,43 @@ +# encoding: utf-8 +""" +This module contains the project meta-data. +""" + +__author__ = "Caleb P. Burns" +__copyright__ = "Copyright ?? 2013-2021 Caleb P. Burns" +__credits__ = [ + "dahlia <https://github.com/dahlia>", + "highb <https://github.com/highb>", + "029xue <https://github.com/029xue>", + "mikexstudios <https://github.com/mikexstudios>", + "nhumrich <https://github.com/nhumrich>", + "davidfraser <https://github.com/davidfraser>", + "demurgos <https://github.com/demurgos>", + "ghickman <https://github.com/ghickman>", + "nvie <https://github.com/nvie>", + "adrienverge <https://github.com/adrienverge>", + "AndersBlomdell <https://github.com/AndersBlomdell>", + "highb <https://github.com/highb>", + "thmxv <https://github.com/thmxv>", + "wimglenn <https://github.com/wimglenn>", + "hugovk <https://github.com/hugovk>", + "dcecile <https://github.com/dcecile>", + "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>", + "nhhollander <https://github.com/nhhollander>", + "KOLANICH <https://github.com/KOLANICH>", + "JonjonHays <https://github.com/JonjonHays>", + "Isaac0616 <https://github.com/Isaac0616>", + "SebastiaanZ <https://github.com/SebastiaanZ>", + "RoelAdriaans <https://github.com/RoelAdriaans>", + "raviselker <https://github.com/raviselker>", + "johanvergeer <https://github.com/johanvergeer>", + "danjer <https://github.com/danjer>", + "jhbuhrman <https://github.com/jhbuhrman>", + "WPDOrdina <https://github.com/WPDOrdina>", +] +__license__ = "MPL 2.0" +__version__ = "0.9.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/setup.cfg new/pathspec-0.9.0/setup.cfg --- old/pathspec-0.8.1/setup.cfg 2020-11-07 20:37:55.000000000 +0100 +++ new/pathspec-0.9.0/setup.cfg 2021-07-18 02:27:56.000000000 +0200 @@ -1,3 +1,38 @@ +[metadata] +author = Caleb P. Burns +author_email = cpbu...@gmail.com +classifiers = + Development Status :: 4 - Beta + Intended Audience :: Developers + License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) + Operating System :: OS Independent + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: Implementation :: PyPy + Topic :: Software Development :: Libraries :: Python Modules + Topic :: Utilities +description = Utility library for gitignore style pattern matching of file paths. +license = MPL 2.0 +long_description = file: README.rst, CHANGES.rst +long_description_content_type = text/x-rst +name = pathspec +version = attr: pathspec_meta.__version__ +url = https://github.com/cpburnz/python-path-specification + +[options] +packages = find: +python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +setup_requires = setuptools >=39.2.0 +test_suite = pathspec.tests + [bdist_wheel] universal = 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/setup.py new/pathspec-0.9.0/setup.py --- old/pathspec-0.8.1/setup.py 2020-01-29 03:45:39.000000000 +0100 +++ new/pathspec-0.9.0/setup.py 2021-06-03 05:48:54.000000000 +0200 @@ -1,44 +1,5 @@ # encoding: utf-8 -import io -from setuptools import setup, find_packages +from setuptools import setup -from pathspec import __author__, __email__, __license__, __project__, __version__ - -# Read readme and changes files. -with io.open("README.rst", mode='r', encoding='UTF-8') as fh: - readme = fh.read().strip() -with io.open("CHANGES.rst", mode='r', encoding='UTF-8') as fh: - changes = fh.read().strip() - -setup( - name=__project__, - version=__version__, - author=__author__, - author_email=__email__, - url="https://github.com/cpburnz/python-path-specification", - description="Utility library for gitignore style pattern matching of file paths.", - long_description=readme + "\n\n" + changes, - python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Utilities", - ], - license=__license__, - packages=find_packages(), - test_suite='pathspec.tests', -) +setup() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pathspec-0.8.1/tox_pip_install.py new/pathspec-0.9.0/tox_pip_install.py --- old/pathspec-0.8.1/tox_pip_install.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pathspec-0.9.0/tox_pip_install.py 2021-06-03 05:48:54.000000000 +0200 @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# This file is part of packagename <https://github.com/kevinoid/packagename> +# Made available under CC0 1.0 Universal, see LICENSE.txt +# Copyright 2019-2020 Kevin Locke <ke...@kevinlocke.name> +""" +Script to reinstall pip before running `pip install`. + +Workaround for https://bugs.debian.org/962654 +""" + +import os +import sys + +# Must be invoked with pip package (optionally version-constrained) as first +# argument, install options+packages as subsequent options. +if len(sys.argv) < 3 or not sys.argv[1].startswith('pip'): + sys.stderr.write( + 'Usage: ' + sys.argv[0] + ' <pip version> [options] <packages...>\n' + ) + sys.exit(1) + +# Workaround is only needed on Debian (and derivatives) +if os.path.exists('/etc/debian_version'): + pip_exit_code = os.spawnl( + os.P_WAIT, + sys.executable, + sys.executable, + '-m', + 'pip', + 'install', + '--force-reinstall', + '--no-compile', + sys.argv[1], + ) + if pip_exit_code != 0: + sys.exit(pip_exit_code) + +os.execv( + sys.executable, [sys.executable, '-m', 'pip', 'install'] + sys.argv[2:] +)