https://github.com/python/cpython/commit/707d066193c26ab66c8e5e45e72c3a37f48daf45
commit: 707d066193c26ab66c8e5e45e72c3a37f48daf45
branch: main
author: Barney Gale <[email protected]>
committer: barneygale <[email protected]>
date: 2025-02-08T06:47:09Z
summary:
GH-129835: Yield path with trailing slash from `ReadablePath.glob('')` (#129836)
In the private pathlib ABCs, make `ReadablePath.glob('')` yield a path with
a trailing slash (if it yields anything at all). As a result, `glob()`
works similarly to `joinpath()` when given a non-magic pattern.
In the globbing implementation, we preemptively add trailing slashes to
intermediate paths if there are pattern parts remaining; this removes the
need to check for existing trailing slashes (in the removed `add_slash()`
method) at subsequent steps.
files:
M Lib/glob.py
M Lib/pathlib/_abc.py
M Lib/pathlib/_local.py
M Lib/test/test_pathlib/test_pathlib_abc.py
diff --git a/Lib/glob.py b/Lib/glob.py
index a834ea7f7ce556..cd8859e63318f3 100644
--- a/Lib/glob.py
+++ b/Lib/glob.py
@@ -352,12 +352,6 @@ def scandir(path):
"""
raise NotImplementedError
- @staticmethod
- def add_slash(path):
- """Returns a path with a trailing slash added.
- """
- raise NotImplementedError
-
@staticmethod
def concat_path(path, text):
"""Implements path concatenation.
@@ -389,10 +383,12 @@ def selector(self, parts):
def special_selector(self, part, parts):
"""Returns a function that selects special children of the given path.
"""
+ if parts:
+ part += self.sep
select_next = self.selector(parts)
def select_special(path, exists=False):
- path = self.concat_path(self.add_slash(path), part)
+ path = self.concat_path(path, part)
return select_next(path, exists)
return select_special
@@ -402,14 +398,16 @@ def literal_selector(self, part, parts):
# Optimization: consume and join any subsequent literal parts here,
# rather than leaving them for the next selector. This reduces the
- # number of string concatenation operations and calls to add_slash().
+ # number of string concatenation operations.
while parts and magic_check.search(parts[-1]) is None:
part += self.sep + parts.pop()
+ if parts:
+ part += self.sep
select_next = self.selector(parts)
def select_literal(path, exists=False):
- path = self.concat_path(self.add_slash(path), part)
+ path = self.concat_path(path, part)
return select_next(path, exists=False)
return select_literal
@@ -437,7 +435,7 @@ def select_wildcard(path, exists=False):
continue
except OSError:
continue
- if dir_only:
+ entry_path = self.concat_path(entry_path, self.sep)
yield from select_next(entry_path, exists=True)
else:
yield entry_path
@@ -467,7 +465,6 @@ def recursive_selector(self, part, parts):
select_next = self.selector(parts)
def select_recursive(path, exists=False):
- path = self.add_slash(path)
match_pos = len(str(path))
if match is None or match(str(path), match_pos):
yield from select_next(path, exists)
@@ -491,7 +488,10 @@ def select_recursive_step(stack, match_pos):
pass
if is_dir or not dir_only:
- if match is None or match(str(entry_path), match_pos):
+ entry_path_str = str(entry_path)
+ if dir_only:
+ entry_path = self.concat_path(entry_path, self.sep)
+ if match is None or match(entry_path_str, match_pos):
if dir_only:
yield from select_next(entry_path, exists=True)
else:
@@ -528,27 +528,12 @@ def scandir(path):
entries = list(scandir_it)
return ((entry, entry.name, entry.path) for entry in entries)
- if os.name == 'nt':
- @staticmethod
- def add_slash(pathname):
- tail = os.path.splitroot(pathname)[2]
- if not tail or tail[-1] in '\\/':
- return pathname
- return f'{pathname}\\'
- else:
- @staticmethod
- def add_slash(pathname):
- if not pathname or pathname[-1] == '/':
- return pathname
- return f'{pathname}/'
-
class _PathGlobber(_GlobberBase):
"""Provides shell-style pattern matching and globbing for pathlib paths.
"""
lexists = operator.methodcaller('exists', follow_symlinks=False)
- add_slash = operator.methodcaller('joinpath', '')
@staticmethod
def scandir(path):
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index d20f04fc5b6dc3..65d91e4d67b463 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -460,7 +460,7 @@ def glob(self, pattern, *, case_sensitive=None,
recurse_symlinks=True):
recursive = True if recurse_symlinks else _no_recurse_symlinks
globber = _PathGlobber(self.parser.sep, case_sensitive, case_pedantic,
recursive)
select = globber.selector(parts)
- return select(self)
+ return select(self.joinpath(''))
def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):
"""Recursively yield all existing files (of any kind, including
diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py
index 07d361d7b1352c..6cdcd448991c8c 100644
--- a/Lib/pathlib/_local.py
+++ b/Lib/pathlib/_local.py
@@ -959,7 +959,7 @@ def glob(self, pattern, *, case_sensitive=None,
recurse_symlinks=False):
globber = _StringGlobber(self.parser.sep, case_sensitive,
case_pedantic, recursive)
select = globber.selector(parts[::-1])
root = str(self)
- paths = select(root)
+ paths = select(self.parser.join(root, ''))
# Normalize results
if root == '.':
diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py
b/Lib/test/test_pathlib/test_pathlib_abc.py
index 696874273a21fd..836d8387bdc433 100644
--- a/Lib/test/test_pathlib/test_pathlib_abc.py
+++ b/Lib/test/test_pathlib/test_pathlib_abc.py
@@ -1125,7 +1125,7 @@ def test_glob_windows(self):
def test_glob_empty_pattern(self):
P = self.cls
p = P(self.base)
- self.assertEqual(list(p.glob("")), [p])
+ self.assertEqual(list(p.glob("")), [p.joinpath("")])
def test_glob_case_sensitive(self):
P = self.cls
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]