diff --git a/lib/zipp/__init__.py b/lib/zipp/__init__.py index ddfa0a64..3354c2bb 100644 --- a/lib/zipp/__init__.py +++ b/lib/zipp/__init__.py @@ -5,9 +5,9 @@ import itertools import contextlib import pathlib import re -import fnmatch from .py310compat import text_encoding +from .glob import translate __all__ = ['Path'] @@ -298,21 +298,24 @@ class Path: encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) return io.TextIOWrapper(stream, encoding, *args, **kwargs) + def _base(self): + return pathlib.PurePosixPath(self.at or self.root.filename) + @property def name(self): - return pathlib.Path(self.at).name or self.filename.name + return self._base().name @property def suffix(self): - return pathlib.Path(self.at).suffix or self.filename.suffix + return self._base().suffix @property def suffixes(self): - return pathlib.Path(self.at).suffixes or self.filename.suffixes + return self._base().suffixes @property def stem(self): - return pathlib.Path(self.at).stem or self.filename.stem + return self._base().stem @property def filename(self): @@ -349,7 +352,7 @@ class Path: return filter(self._is_child, subs) def match(self, path_pattern): - return pathlib.Path(self.at).match(path_pattern) + return pathlib.PurePosixPath(self.at).match(path_pattern) def is_symlink(self): """ @@ -357,22 +360,13 @@ class Path: """ return False - def _descendants(self): - for child in self.iterdir(): - yield child - if child.is_dir(): - yield from child._descendants() - def glob(self, pattern): if not pattern: raise ValueError(f"Unacceptable pattern: {pattern!r}") - matches = re.compile(fnmatch.translate(pattern)).fullmatch - return ( - child - for child in self._descendants() - if matches(str(child.relative_to(self))) - ) + prefix = re.escape(self.at) + matches = re.compile(prefix + translate(pattern)).fullmatch + return map(self._next, filter(matches, self.root.namelist())) def rglob(self, pattern): return self.glob(f'**/{pattern}') diff --git a/lib/zipp/glob.py b/lib/zipp/glob.py new file mode 100644 index 00000000..4a2e665e --- /dev/null +++ b/lib/zipp/glob.py @@ -0,0 +1,40 @@ +import re + + +def translate(pattern): + r""" + Given a glob pattern, produce a regex that matches it. + + >>> translate('*.txt') + '[^/]*\\.txt' + >>> translate('a?txt') + 'a.txt' + >>> translate('**/*') + '.*/[^/]*' + """ + return ''.join(map(replace, separate(pattern))) + + +def separate(pattern): + """ + Separate out character sets to avoid translating their contents. + + >>> [m.group(0) for m in separate('*.txt')] + ['*.txt'] + >>> [m.group(0) for m in separate('a[?]txt')] + ['a', '[?]', 'txt'] + """ + return re.finditer(r'([^\[]+)|(?P[\[].*?[\]])|([\[][^\]]*$)', pattern) + + +def replace(match): + """ + Perform the replacements for a match from :func:`separate`. + """ + + return match.group('set') or ( + re.escape(match.group(0)) + .replace('\\*\\*', r'.*') + .replace('\\*', r'[^/]*') + .replace('\\?', r'.') + ) diff --git a/lib/zipp/py310compat.py b/lib/zipp/py310compat.py index 8244124c..d5ca53e0 100644 --- a/lib/zipp/py310compat.py +++ b/lib/zipp/py310compat.py @@ -2,9 +2,8 @@ import sys import io -te_impl = 'lambda encoding, stacklevel=2, /: encoding' -te_impl_37 = te_impl.replace(', /', '') -_text_encoding = eval(te_impl) if sys.version_info > (3, 8) else eval(te_impl_37) +def _text_encoding(encoding, stacklevel=2, /): # pragma: no cover + return encoding text_encoding = ( diff --git a/requirements.txt b/requirements.txt index d4e4442f..7157e7b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -49,7 +49,7 @@ urllib3<2 webencodings==0.5.1 websocket-client==1.6.2 xmltodict==0.13.0 -zipp==3.15.0 +zipp==3.16.2 # configobj==5.1.0 # sgmllib3k==1.0.0