mirror of
https://github.com/python/cpython.git
synced 2025-07-24 11:44:31 +00:00
GH-73435: Add pathlib.PurePath.full_match()
(#114350)
In 49f90ba
we added support for the recursive wildcard `**` in
`pathlib.PurePath.match()`. This should allow arbitrary prefix and suffix
matching, like `p.match('foo/**')` or `p.match('**/foo')`, but there's a
problem: for relative patterns only, `match()` implicitly inserts a `**`
token on the left hand side, causing all patterns to match from the right.
As a result, it's impossible to match relative patterns from the left:
`PurePath('foo/bar').match('bar/**')` is true!
This commit reverts the changes to `match()`, and instead adds a new
`full_match()` method that:
- Allows empty patterns
- Supports the recursive wildcard `**`
- Matches the *entire* path when given a relative pattern
This commit is contained in:
parent
841eacd076
commit
b69548a0f5
6 changed files with 158 additions and 75 deletions
|
@ -249,29 +249,8 @@ class DummyPurePathTest(unittest.TestCase):
|
|||
self.assertFalse(P('/ab.py').match('/a/*.py'))
|
||||
self.assertFalse(P('/a/b/c.py').match('/a/*.py'))
|
||||
# Multi-part glob-style pattern.
|
||||
self.assertTrue(P('a').match('**'))
|
||||
self.assertTrue(P('c.py').match('**'))
|
||||
self.assertTrue(P('a/b/c.py').match('**'))
|
||||
self.assertTrue(P('/a/b/c.py').match('**'))
|
||||
self.assertTrue(P('/a/b/c.py').match('/**'))
|
||||
self.assertTrue(P('/a/b/c.py').match('/a/**'))
|
||||
self.assertTrue(P('/a/b/c.py').match('**/*.py'))
|
||||
self.assertTrue(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/b/**/*.py'))
|
||||
self.assertTrue(P('/a/b/c.py').match('/**/**/**/**/*.py'))
|
||||
self.assertFalse(P('c.py').match('**/a.py'))
|
||||
self.assertFalse(P('c.py').match('c/**'))
|
||||
self.assertFalse(P('a/b/c.py').match('**/a'))
|
||||
self.assertFalse(P('a/b/c.py').match('**/a/b'))
|
||||
self.assertFalse(P('a/b/c.py').match('**/a/b/c'))
|
||||
self.assertFalse(P('a/b/c.py').match('**/a/b/c.'))
|
||||
self.assertFalse(P('a/b/c.py').match('**/a/b/c./**'))
|
||||
self.assertFalse(P('a/b/c.py').match('**/a/b/c./**'))
|
||||
self.assertFalse(P('a/b/c.py').match('/a/b/c.py/**'))
|
||||
self.assertFalse(P('a/b/c.py').match('/**/a/b/c.py'))
|
||||
self.assertRaises(ValueError, P('a').match, '**a/b/c')
|
||||
self.assertRaises(ValueError, P('a').match, 'a/b/c**')
|
||||
# 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))
|
||||
|
@ -279,9 +258,82 @@ class DummyPurePathTest(unittest.TestCase):
|
|||
self.assertTrue(P('/a/b/c.py').match('/A/*/*.Py', case_sensitive=False))
|
||||
# Matching against empty path
|
||||
self.assertFalse(P('').match('*'))
|
||||
self.assertTrue(P('').match('**'))
|
||||
self.assertFalse(P('').match('**'))
|
||||
self.assertFalse(P('').match('**/*'))
|
||||
|
||||
def test_full_match_common(self):
|
||||
P = self.cls
|
||||
# Simple relative pattern.
|
||||
self.assertTrue(P('b.py').full_match('b.py'))
|
||||
self.assertFalse(P('a/b.py').full_match('b.py'))
|
||||
self.assertFalse(P('/a/b.py').full_match('b.py'))
|
||||
self.assertFalse(P('a.py').full_match('b.py'))
|
||||
self.assertFalse(P('b/py').full_match('b.py'))
|
||||
self.assertFalse(P('/a.py').full_match('b.py'))
|
||||
self.assertFalse(P('b.py/c').full_match('b.py'))
|
||||
# Wildcard relative pattern.
|
||||
self.assertTrue(P('b.py').full_match('*.py'))
|
||||
self.assertFalse(P('a/b.py').full_match('*.py'))
|
||||
self.assertFalse(P('/a/b.py').full_match('*.py'))
|
||||
self.assertFalse(P('b.pyc').full_match('*.py'))
|
||||
self.assertFalse(P('b./py').full_match('*.py'))
|
||||
self.assertFalse(P('b.py/c').full_match('*.py'))
|
||||
# Multi-part relative pattern.
|
||||
self.assertTrue(P('ab/c.py').full_match('a*/*.py'))
|
||||
self.assertFalse(P('/d/ab/c.py').full_match('a*/*.py'))
|
||||
self.assertFalse(P('a.py').full_match('a*/*.py'))
|
||||
self.assertFalse(P('/dab/c.py').full_match('a*/*.py'))
|
||||
self.assertFalse(P('ab/c.py/d').full_match('a*/*.py'))
|
||||
# Absolute pattern.
|
||||
self.assertTrue(P('/b.py').full_match('/*.py'))
|
||||
self.assertFalse(P('b.py').full_match('/*.py'))
|
||||
self.assertFalse(P('a/b.py').full_match('/*.py'))
|
||||
self.assertFalse(P('/a/b.py').full_match('/*.py'))
|
||||
# Multi-part absolute pattern.
|
||||
self.assertTrue(P('/a/b.py').full_match('/a/*.py'))
|
||||
self.assertFalse(P('/ab.py').full_match('/a/*.py'))
|
||||
self.assertFalse(P('/a/b/c.py').full_match('/a/*.py'))
|
||||
# Multi-part glob-style pattern.
|
||||
self.assertTrue(P('a').full_match('**'))
|
||||
self.assertTrue(P('c.py').full_match('**'))
|
||||
self.assertTrue(P('a/b/c.py').full_match('**'))
|
||||
self.assertTrue(P('/a/b/c.py').full_match('**'))
|
||||
self.assertTrue(P('/a/b/c.py').full_match('/**'))
|
||||
self.assertTrue(P('/a/b/c.py').full_match('/a/**'))
|
||||
self.assertTrue(P('/a/b/c.py').full_match('**/*.py'))
|
||||
self.assertTrue(P('/a/b/c.py').full_match('/**/*.py'))
|
||||
self.assertTrue(P('/a/b/c.py').full_match('/a/**/*.py'))
|
||||
self.assertTrue(P('/a/b/c.py').full_match('/a/b/**/*.py'))
|
||||
self.assertTrue(P('/a/b/c.py').full_match('/**/**/**/**/*.py'))
|
||||
self.assertFalse(P('c.py').full_match('**/a.py'))
|
||||
self.assertFalse(P('c.py').full_match('c/**'))
|
||||
self.assertFalse(P('a/b/c.py').full_match('**/a'))
|
||||
self.assertFalse(P('a/b/c.py').full_match('**/a/b'))
|
||||
self.assertFalse(P('a/b/c.py').full_match('**/a/b/c'))
|
||||
self.assertFalse(P('a/b/c.py').full_match('**/a/b/c.'))
|
||||
self.assertFalse(P('a/b/c.py').full_match('**/a/b/c./**'))
|
||||
self.assertFalse(P('a/b/c.py').full_match('**/a/b/c./**'))
|
||||
self.assertFalse(P('a/b/c.py').full_match('/a/b/c.py/**'))
|
||||
self.assertFalse(P('a/b/c.py').full_match('/**/a/b/c.py'))
|
||||
self.assertRaises(ValueError, P('a').full_match, '**a/b/c')
|
||||
self.assertRaises(ValueError, P('a').full_match, 'a/b/c**')
|
||||
# Case-sensitive flag
|
||||
self.assertFalse(P('A.py').full_match('a.PY', case_sensitive=True))
|
||||
self.assertTrue(P('A.py').full_match('a.PY', case_sensitive=False))
|
||||
self.assertFalse(P('c:/a/B.Py').full_match('C:/A/*.pY', case_sensitive=True))
|
||||
self.assertTrue(P('/a/b/c.py').full_match('/A/*/*.Py', case_sensitive=False))
|
||||
# Matching against empty path
|
||||
self.assertFalse(P('').full_match('*'))
|
||||
self.assertTrue(P('').full_match('**'))
|
||||
self.assertFalse(P('').full_match('**/*'))
|
||||
# Matching with empty pattern
|
||||
self.assertTrue(P('').full_match(''))
|
||||
self.assertTrue(P('.').full_match('.'))
|
||||
self.assertFalse(P('/').full_match(''))
|
||||
self.assertFalse(P('/').full_match('.'))
|
||||
self.assertFalse(P('foo').full_match(''))
|
||||
self.assertFalse(P('foo').full_match('.'))
|
||||
|
||||
def test_parts_common(self):
|
||||
# `parts` returns a tuple.
|
||||
sep = self.sep
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue