diff options
author | Mariatta Wijaya <mariatta.wijaya@gmail.com> | 2017-02-06 20:16:58 -0800 |
---|---|---|
committer | Mariatta Wijaya <mariatta.wijaya@gmail.com> | 2017-02-06 20:16:58 -0800 |
commit | da79bcf8ac7ae72218ab023e1ed54390bc1a3a27 (patch) | |
tree | 74845e2dbd9521d9748b9c32f1922f4123083bf3 /Lib/pathlib.py | |
parent | e3c7e835bdfc97750eb9b7fc0ad2493108c2d438 (diff) | |
parent | 1fe806ac56f8b83694d24ab604eb695d00bc8497 (diff) | |
download | cpython-da79bcf8ac7ae72218ab023e1ed54390bc1a3a27.tar.gz |
Issue #29371: merge with 3.5
Diffstat (limited to 'Lib/pathlib.py')
-rw-r--r-- | Lib/pathlib.py | 147 |
1 files changed, 80 insertions, 67 deletions
diff --git a/Lib/pathlib.py b/Lib/pathlib.py index c0d858ef56..9f347216b1 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -178,12 +178,28 @@ class _WindowsFlavour(_Flavour): def casefold_parts(self, parts): return [p.lower() for p in parts] - def resolve(self, path): + def resolve(self, path, strict=False): s = str(path) if not s: return os.getcwd() + previous_s = None if _getfinalpathname is not None: - return self._ext_to_normal(_getfinalpathname(s)) + if strict: + return self._ext_to_normal(_getfinalpathname(s)) + else: + while True: + try: + s = self._ext_to_normal(_getfinalpathname(s)) + except FileNotFoundError: + previous_s = s + s = os.path.dirname(s) + if previous_s == s: + return path + else: + if previous_s is None: + return s + else: + return s + os.path.sep + os.path.basename(previous_s) # Means fallback on absolute return None @@ -285,7 +301,7 @@ class _PosixFlavour(_Flavour): def casefold_parts(self, parts): return parts - def resolve(self, path): + def resolve(self, path, strict=False): sep = self.sep accessor = path._accessor seen = {} @@ -315,7 +331,10 @@ class _PosixFlavour(_Flavour): target = accessor.readlink(newpath) except OSError as e: if e.errno != EINVAL: - raise + if strict: + raise + else: + return newpath # Not a symlink path = newpath else: @@ -385,6 +404,8 @@ class _NormalAccessor(_Accessor): listdir = _wrap_strfunc(os.listdir) + scandir = _wrap_strfunc(os.scandir) + chmod = _wrap_strfunc(os.chmod) if hasattr(os, "lchmod"): @@ -429,25 +450,6 @@ _normal_accessor = _NormalAccessor() # Globbing helpers # -@contextmanager -def _cached(func): - try: - func.__cached__ - yield func - except AttributeError: - cache = {} - def wrapper(*args): - try: - return cache[args] - except KeyError: - value = cache[args] = func(*args) - return value - wrapper.__cached__ = True - try: - yield wrapper - finally: - cache.clear() - def _make_selector(pattern_parts): pat = pattern_parts[0] child_parts = pattern_parts[1:] @@ -473,8 +475,10 @@ class _Selector: self.child_parts = child_parts if child_parts: self.successor = _make_selector(child_parts) + self.dironly = True else: self.successor = _TerminatingSelector() + self.dironly = False def select_from(self, parent_path): """Iterate over all child paths of `parent_path` matched by this @@ -482,13 +486,15 @@ class _Selector: path_cls = type(parent_path) is_dir = path_cls.is_dir exists = path_cls.exists - listdir = parent_path._accessor.listdir - return self._select_from(parent_path, is_dir, exists, listdir) + scandir = parent_path._accessor.scandir + if not is_dir(parent_path): + return iter([]) + return self._select_from(parent_path, is_dir, exists, scandir) class _TerminatingSelector: - def _select_from(self, parent_path, is_dir, exists, listdir): + def _select_from(self, parent_path, is_dir, exists, scandir): yield parent_path @@ -498,13 +504,11 @@ class _PreciseSelector(_Selector): self.name = name _Selector.__init__(self, child_parts) - def _select_from(self, parent_path, is_dir, exists, listdir): + def _select_from(self, parent_path, is_dir, exists, scandir): try: - if not is_dir(parent_path): - return path = parent_path._make_child_relpath(self.name) - if exists(path): - for p in self.successor._select_from(path, is_dir, exists, listdir): + if (is_dir if self.dironly else exists)(path): + for p in self.successor._select_from(path, is_dir, exists, scandir): yield p except PermissionError: return @@ -516,17 +520,18 @@ class _WildcardSelector(_Selector): self.pat = re.compile(fnmatch.translate(pat)) _Selector.__init__(self, child_parts) - def _select_from(self, parent_path, is_dir, exists, listdir): + def _select_from(self, parent_path, is_dir, exists, scandir): try: - if not is_dir(parent_path): - return cf = parent_path._flavour.casefold - for name in listdir(parent_path): - casefolded = cf(name) - if self.pat.match(casefolded): - path = parent_path._make_child_relpath(name) - for p in self.successor._select_from(path, is_dir, exists, listdir): - yield p + entries = list(scandir(parent_path)) + for entry in entries: + if not self.dironly or entry.is_dir(): + name = entry.name + casefolded = cf(name) + if self.pat.match(casefolded): + path = parent_path._make_child_relpath(name) + for p in self.successor._select_from(path, is_dir, exists, scandir): + yield p except PermissionError: return @@ -537,32 +542,30 @@ class _RecursiveWildcardSelector(_Selector): def __init__(self, pat, child_parts): _Selector.__init__(self, child_parts) - def _iterate_directories(self, parent_path, is_dir, listdir): + def _iterate_directories(self, parent_path, is_dir, scandir): yield parent_path try: - for name in listdir(parent_path): - path = parent_path._make_child_relpath(name) - if is_dir(path) and not path.is_symlink(): - for p in self._iterate_directories(path, is_dir, listdir): + entries = list(scandir(parent_path)) + for entry in entries: + if entry.is_dir() and not entry.is_symlink(): + path = parent_path._make_child_relpath(entry.name) + for p in self._iterate_directories(path, is_dir, scandir): yield p except PermissionError: return - def _select_from(self, parent_path, is_dir, exists, listdir): + def _select_from(self, parent_path, is_dir, exists, scandir): try: - if not is_dir(parent_path): - return - with _cached(listdir) as listdir: - yielded = set() - try: - successor_select = self.successor._select_from - for starting_point in self._iterate_directories(parent_path, is_dir, listdir): - for p in successor_select(starting_point, is_dir, exists, listdir): - if p not in yielded: - yield p - yielded.add(p) - finally: - yielded.clear() + yielded = set() + try: + successor_select = self.successor._select_from + for starting_point in self._iterate_directories(parent_path, is_dir, scandir): + for p in successor_select(starting_point, is_dir, exists, scandir): + if p not in yielded: + yield p + yielded.add(p) + finally: + yielded.clear() except PermissionError: return @@ -634,13 +637,16 @@ class PurePath(object): for a in args: if isinstance(a, PurePath): parts += a._parts - elif isinstance(a, str): - # Force-cast str subclasses to str (issue #21127) - parts.append(str(a)) else: - raise TypeError( - "argument should be a path or str object, not %r" - % type(a)) + a = os.fspath(a) + if isinstance(a, str): + # Force-cast str subclasses to str (issue #21127) + parts.append(str(a)) + else: + raise TypeError( + "argument should be a str object or an os.PathLike " + "object returning str, not %r" + % type(a)) return cls._flavour.parse_parts(parts) @classmethod @@ -693,6 +699,9 @@ class PurePath(object): self._parts) or '.' return self._str + def __fspath__(self): + return str(self) + def as_posix(self): """Return the string representation of the path with forward (/) slashes.""" @@ -943,6 +952,10 @@ class PurePath(object): return False return True +# Can't subclass os.PathLike from PurePath and keep the constructor +# optimizations in PurePath._parse_args(). +os.PathLike.register(PurePath) + class PurePosixPath(PurePath): _flavour = _posix_flavour @@ -1098,7 +1111,7 @@ class Path(PurePath): obj._init(template=self) return obj - def resolve(self): + def resolve(self, strict=False): """ Make the path absolute, resolving all symlinks on the way and also normalizing it (for example turning slashes into backslashes under @@ -1106,7 +1119,7 @@ class Path(PurePath): """ if self._closed: self._raise_closed() - s = self._flavour.resolve(self) + s = self._flavour.resolve(self, strict=strict) if s is None: # No symlink resolution => for consistency, raise an error if # the path doesn't exist or is forbidden |