mirror of
https://github.com/python/cpython.git
synced 2025-11-20 10:57:44 +00:00
GH-104484: Add case_sensitive argument to pathlib.PurePath.match() (GH-104565)
Co-authored-by: Barney Gale <barney.gale@gmail.com>
This commit is contained in:
parent
cfa517d5a6
commit
dcdc90d384
5 changed files with 30 additions and 8 deletions
|
|
@ -546,7 +546,7 @@ Pure paths provide the following methods and properties:
|
||||||
PureWindowsPath('c:/Program Files')
|
PureWindowsPath('c:/Program Files')
|
||||||
|
|
||||||
|
|
||||||
.. method:: PurePath.match(pattern)
|
.. method:: PurePath.match(pattern, *, case_sensitive=None)
|
||||||
|
|
||||||
Match this path against the provided glob-style pattern. Return ``True``
|
Match this path against the provided glob-style pattern. Return ``True``
|
||||||
if matching is successful, ``False`` otherwise.
|
if matching is successful, ``False`` otherwise.
|
||||||
|
|
@ -576,6 +576,11 @@ Pure paths provide the following methods and properties:
|
||||||
>>> PureWindowsPath('b.py').match('*.PY')
|
>>> PureWindowsPath('b.py').match('*.PY')
|
||||||
True
|
True
|
||||||
|
|
||||||
|
Set *case_sensitive* to ``True`` or ``False`` to override this behaviour.
|
||||||
|
|
||||||
|
.. versionadded:: 3.12
|
||||||
|
The *case_sensitive* argument.
|
||||||
|
|
||||||
|
|
||||||
.. method:: PurePath.relative_to(other, walk_up=False)
|
.. method:: PurePath.relative_to(other, walk_up=False)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,9 @@ pathlib
|
||||||
* Add :meth:`pathlib.Path.is_junction` as a proxy to :func:`os.path.isjunction`.
|
* Add :meth:`pathlib.Path.is_junction` as a proxy to :func:`os.path.isjunction`.
|
||||||
(Contributed by Charles Machalow in :gh:`99547`.)
|
(Contributed by Charles Machalow in :gh:`99547`.)
|
||||||
|
|
||||||
|
* Add *case_sensitive* optional parameter to :meth:`pathlib.Path.glob`,
|
||||||
|
:meth:`pathlib.Path.rglob` and :meth:`pathlib.PurePath.match` for matching
|
||||||
|
the path's case sensitivity, allowing for more precise control over the matching process.
|
||||||
|
|
||||||
dis
|
dis
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,12 @@ def _make_selector(pattern_parts, flavour, case_sensitive):
|
||||||
return cls(pat, child_parts, flavour, case_sensitive)
|
return cls(pat, child_parts, flavour, case_sensitive)
|
||||||
|
|
||||||
|
|
||||||
|
@functools.lru_cache(maxsize=256)
|
||||||
|
def _compile_pattern(pat, case_sensitive):
|
||||||
|
flags = re.NOFLAG if case_sensitive else re.IGNORECASE
|
||||||
|
return re.compile(fnmatch.translate(pat), flags).match
|
||||||
|
|
||||||
|
|
||||||
class _Selector:
|
class _Selector:
|
||||||
"""A selector matches a specific glob pattern part against the children
|
"""A selector matches a specific glob pattern part against the children
|
||||||
of a given path."""
|
of a given path."""
|
||||||
|
|
@ -133,8 +139,7 @@ class _WildcardSelector(_Selector):
|
||||||
if case_sensitive is None:
|
if case_sensitive is None:
|
||||||
# TODO: evaluate case-sensitivity of each directory in _select_from()
|
# TODO: evaluate case-sensitivity of each directory in _select_from()
|
||||||
case_sensitive = _is_case_sensitive(flavour)
|
case_sensitive = _is_case_sensitive(flavour)
|
||||||
flags = re.NOFLAG if case_sensitive else re.IGNORECASE
|
self.match = _compile_pattern(pat, case_sensitive)
|
||||||
self.match = re.compile(fnmatch.translate(pat), flags=flags).fullmatch
|
|
||||||
|
|
||||||
def _select_from(self, parent_path, scandir):
|
def _select_from(self, parent_path, scandir):
|
||||||
try:
|
try:
|
||||||
|
|
@ -680,22 +685,25 @@ class PurePath(object):
|
||||||
name = self._tail[-1].partition('.')[0].partition(':')[0].rstrip(' ')
|
name = self._tail[-1].partition('.')[0].partition(':')[0].rstrip(' ')
|
||||||
return name.upper() in _WIN_RESERVED_NAMES
|
return name.upper() in _WIN_RESERVED_NAMES
|
||||||
|
|
||||||
def match(self, path_pattern):
|
def match(self, path_pattern, *, case_sensitive=None):
|
||||||
"""
|
"""
|
||||||
Return True if this path matches the given pattern.
|
Return True if this path matches the given pattern.
|
||||||
"""
|
"""
|
||||||
|
if case_sensitive is None:
|
||||||
|
case_sensitive = _is_case_sensitive(self._flavour)
|
||||||
pat = self.with_segments(path_pattern)
|
pat = self.with_segments(path_pattern)
|
||||||
if not pat.parts:
|
if not pat.parts:
|
||||||
raise ValueError("empty pattern")
|
raise ValueError("empty pattern")
|
||||||
pat_parts = pat._parts_normcase
|
pat_parts = pat.parts
|
||||||
parts = self._parts_normcase
|
parts = self.parts
|
||||||
if pat.drive or pat.root:
|
if pat.drive or pat.root:
|
||||||
if len(pat_parts) != len(parts):
|
if len(pat_parts) != len(parts):
|
||||||
return False
|
return False
|
||||||
elif len(pat_parts) > len(parts):
|
elif len(pat_parts) > len(parts):
|
||||||
return False
|
return False
|
||||||
for part, pat in zip(reversed(parts), reversed(pat_parts)):
|
for part, pat in zip(reversed(parts), reversed(pat_parts)):
|
||||||
if not fnmatch.fnmatchcase(part, pat):
|
match = _compile_pattern(pat, case_sensitive)
|
||||||
|
if not match(part):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -312,6 +312,11 @@ class _BasePurePathTest(object):
|
||||||
# Multi-part glob-style pattern.
|
# Multi-part glob-style pattern.
|
||||||
self.assertFalse(P('/a/b/c.py').match('/**/*.py'))
|
self.assertFalse(P('/a/b/c.py').match('/**/*.py'))
|
||||||
self.assertTrue(P('/a/b/c.py').match('/a/**/*.py'))
|
self.assertTrue(P('/a/b/c.py').match('/a/**/*.py'))
|
||||||
|
# Case-sensitive flag
|
||||||
|
self.assertFalse(P('A.py').match('a.PY', case_sensitive=True))
|
||||||
|
self.assertTrue(P('A.py').match('a.PY', case_sensitive=False))
|
||||||
|
self.assertFalse(P('c:/a/B.Py').match('C:/A/*.pY', case_sensitive=True))
|
||||||
|
self.assertTrue(P('/a/b/c.py').match('/A/*/*.Py', case_sensitive=False))
|
||||||
|
|
||||||
def test_ordering_common(self):
|
def test_ordering_common(self):
|
||||||
# Ordering is tuple-alike.
|
# Ordering is tuple-alike.
|
||||||
|
|
@ -916,7 +921,7 @@ class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase):
|
||||||
self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(),
|
self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(),
|
||||||
'file://some/share/a/b%25%23c%C3%A9')
|
'file://some/share/a/b%25%23c%C3%A9')
|
||||||
|
|
||||||
def test_match_common(self):
|
def test_match(self):
|
||||||
P = self.cls
|
P = self.cls
|
||||||
# Absolute patterns.
|
# Absolute patterns.
|
||||||
self.assertTrue(P('c:/b.py').match('*:/*.py'))
|
self.assertTrue(P('c:/b.py').match('*:/*.py'))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Added *case_sensitive* argument to :meth:`pathlib.PurePath.match`
|
||||||
Loading…
Add table
Add a link
Reference in a new issue