diff --git a/Lib/glob.py b/Lib/glob.py index fbb1d35aab71fa..574e5ad51b601d 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -328,8 +328,8 @@ def _compile_pattern(pat, sep, case_sensitive, recursive=True): return re.compile(regex, flags=flags).match -class _Globber: - """Class providing shell-style pattern matching and globbing. +class _GlobberBase: + """Abstract class providing shell-style pattern matching and globbing. """ def __init__(self, sep, case_sensitive, case_pedantic=False, recursive=False): @@ -338,29 +338,37 @@ def __init__(self, sep, case_sensitive, case_pedantic=False, recursive=False): self.case_pedantic = case_pedantic self.recursive = recursive - # Low-level methods + # Abstract methods - lexists = operator.methodcaller('exists', follow_symlinks=False) - add_slash = operator.methodcaller('joinpath', '') + @staticmethod + def lexists(path): + """Implements os.path.lexists(). + """ + raise NotImplementedError @staticmethod def scandir(path): - """Emulates os.scandir(), which returns an object that can be used as - a context manager. This method is called by walk() and glob(). + """Implements os.scandir(). + """ + raise NotImplementedError + + @staticmethod + def add_slash(path): + """Returns a path with a trailing slash added. """ - return contextlib.nullcontext(path.iterdir()) + raise NotImplementedError @staticmethod def concat_path(path, text): - """Appends text to the given path. + """Implements path concatenation. """ - return path.with_segments(path._raw_path + text) + raise NotImplementedError @staticmethod def parse_entry(entry): """Returns the path of an entry yielded from scandir(). """ - return entry + raise NotImplementedError # High-level methods @@ -520,7 +528,9 @@ def select_exists(self, path, exists=False): yield path -class _StringGlobber(_Globber): +class _StringGlobber(_GlobberBase): + """Provides shell-style pattern matching and globbing for string paths. + """ lexists = staticmethod(os.path.lexists) scandir = staticmethod(os.scandir) parse_entry = operator.attrgetter('path') diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 1a74f457c3f5a7..ecea8e88d1a2e3 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -12,8 +12,9 @@ """ import functools +import operator import posixpath -from glob import _Globber, _no_recurse_symlinks +from glob import _GlobberBase, _no_recurse_symlinks from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO @@ -84,6 +85,33 @@ def isabs(self, path): raise UnsupportedOperation(self._unsupported_msg('isabs()')) +class PathGlobber(_GlobberBase): + """ + Class providing shell-style globbing for path objects. + """ + + lexists = operator.methodcaller('exists', follow_symlinks=False) + add_slash = operator.methodcaller('joinpath', '') + + @staticmethod + def scandir(path): + """Emulates os.scandir(), which returns an object that can be used as + a context manager. This method is called by walk() and glob(). + """ + import contextlib + return contextlib.nullcontext(path.iterdir()) + + @staticmethod + def concat_path(path, text): + """Appends text to the given path.""" + return path.with_segments(path._raw_path + text) + + @staticmethod + def parse_entry(entry): + """Returns the path of an entry yielded from scandir().""" + return entry + + class PurePathBase: """Base class for pure path objects. @@ -104,7 +132,7 @@ class PurePathBase: '_resolving', ) parser = ParserBase() - _globber = _Globber + _globber = PathGlobber def __init__(self, path, *paths): self._raw_path = self.parser.join(path, *paths) if paths else path