| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 1 | import fnmatch | 
 | 2 | import functools | 
 | 3 | import io | 
 | 4 | import ntpath | 
 | 5 | import os | 
 | 6 | import posixpath | 
 | 7 | import re | 
 | 8 | import sys | 
| Antoine Pitrou | 069a5e1 | 2013-12-03 09:41:35 +0100 | [diff] [blame] | 9 | from collections import Sequence | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 10 | from contextlib import contextmanager | 
 | 11 | from errno import EINVAL, ENOENT | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 12 | from operator import attrgetter | 
 | 13 | from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO | 
| Antoine Pitrou | 069a5e1 | 2013-12-03 09:41:35 +0100 | [diff] [blame] | 14 | from urllib.parse import quote_from_bytes as urlquote_from_bytes | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 15 |  | 
 | 16 |  | 
 | 17 | supports_symlinks = True | 
 | 18 | try: | 
 | 19 |     import nt | 
 | 20 | except ImportError: | 
 | 21 |     nt = None | 
 | 22 | else: | 
 | 23 |     if sys.getwindowsversion()[:2] >= (6, 0): | 
 | 24 |         from nt import _getfinalpathname | 
 | 25 |     else: | 
 | 26 |         supports_symlinks = False | 
 | 27 |         _getfinalpathname = None | 
 | 28 |  | 
 | 29 |  | 
 | 30 | __all__ = [ | 
 | 31 |     "PurePath", "PurePosixPath", "PureWindowsPath", | 
 | 32 |     "Path", "PosixPath", "WindowsPath", | 
 | 33 |     ] | 
 | 34 |  | 
 | 35 | # | 
 | 36 | # Internals | 
 | 37 | # | 
 | 38 |  | 
 | 39 | def _is_wildcard_pattern(pat): | 
 | 40 |     # Whether this pattern needs actual matching using fnmatch, or can | 
 | 41 |     # be looked up directly as a file. | 
 | 42 |     return "*" in pat or "?" in pat or "[" in pat | 
 | 43 |  | 
 | 44 |  | 
 | 45 | class _Flavour(object): | 
 | 46 |     """A flavour implements a particular (platform-specific) set of path | 
 | 47 |     semantics.""" | 
 | 48 |  | 
 | 49 |     def __init__(self): | 
 | 50 |         self.join = self.sep.join | 
 | 51 |  | 
 | 52 |     def parse_parts(self, parts): | 
 | 53 |         parsed = [] | 
 | 54 |         sep = self.sep | 
 | 55 |         altsep = self.altsep | 
 | 56 |         drv = root = '' | 
 | 57 |         it = reversed(parts) | 
 | 58 |         for part in it: | 
 | 59 |             if not part: | 
 | 60 |                 continue | 
 | 61 |             if altsep: | 
 | 62 |                 part = part.replace(altsep, sep) | 
 | 63 |             drv, root, rel = self.splitroot(part) | 
 | 64 |             if sep in rel: | 
 | 65 |                 for x in reversed(rel.split(sep)): | 
 | 66 |                     if x and x != '.': | 
 | 67 |                         parsed.append(sys.intern(x)) | 
 | 68 |             else: | 
 | 69 |                 if rel and rel != '.': | 
 | 70 |                     parsed.append(sys.intern(rel)) | 
 | 71 |             if drv or root: | 
 | 72 |                 if not drv: | 
 | 73 |                     # If no drive is present, try to find one in the previous | 
 | 74 |                     # parts. This makes the result of parsing e.g. | 
 | 75 |                     # ("C:", "/", "a") reasonably intuitive. | 
 | 76 |                     for part in it: | 
 | 77 |                         drv = self.splitroot(part)[0] | 
 | 78 |                         if drv: | 
 | 79 |                             break | 
 | 80 |                 break | 
 | 81 |         if drv or root: | 
 | 82 |             parsed.append(drv + root) | 
 | 83 |         parsed.reverse() | 
 | 84 |         return drv, root, parsed | 
 | 85 |  | 
 | 86 |     def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2): | 
 | 87 |         """ | 
 | 88 |         Join the two paths represented by the respective | 
 | 89 |         (drive, root, parts) tuples.  Return a new (drive, root, parts) tuple. | 
 | 90 |         """ | 
 | 91 |         if root2: | 
| Serhiy Storchaka | a993902 | 2013-12-06 17:14:12 +0200 | [diff] [blame] | 92 |             if not drv2 and drv: | 
 | 93 |                 return drv, root2, [drv + root2] + parts2[1:] | 
 | 94 |         elif drv2: | 
 | 95 |             if drv2 == drv or self.casefold(drv2) == self.casefold(drv): | 
 | 96 |                 # Same drive => second path is relative to the first | 
 | 97 |                 return drv, root, parts + parts2[1:] | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 98 |         else: | 
| Serhiy Storchaka | a993902 | 2013-12-06 17:14:12 +0200 | [diff] [blame] | 99 |             # Second path is non-anchored (common case) | 
 | 100 |             return drv, root, parts + parts2 | 
 | 101 |         return drv2, root2, parts2 | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 102 |  | 
 | 103 |  | 
 | 104 | class _WindowsFlavour(_Flavour): | 
 | 105 |     # Reference for Windows paths can be found at | 
 | 106 |     # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx | 
 | 107 |  | 
 | 108 |     sep = '\\' | 
 | 109 |     altsep = '/' | 
 | 110 |     has_drv = True | 
 | 111 |     pathmod = ntpath | 
 | 112 |  | 
 | 113 |     is_supported = (nt is not None) | 
 | 114 |  | 
 | 115 |     drive_letters = ( | 
 | 116 |         set(chr(x) for x in range(ord('a'), ord('z') + 1)) | | 
 | 117 |         set(chr(x) for x in range(ord('A'), ord('Z') + 1)) | 
 | 118 |     ) | 
 | 119 |     ext_namespace_prefix = '\\\\?\\' | 
 | 120 |  | 
 | 121 |     reserved_names = ( | 
 | 122 |         {'CON', 'PRN', 'AUX', 'NUL'} | | 
 | 123 |         {'COM%d' % i for i in range(1, 10)} | | 
 | 124 |         {'LPT%d' % i for i in range(1, 10)} | 
 | 125 |         ) | 
 | 126 |  | 
 | 127 |     # Interesting findings about extended paths: | 
 | 128 |     # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported | 
 | 129 |     #   but '\\?\c:/a' is not | 
 | 130 |     # - extended paths are always absolute; "relative" extended paths will | 
 | 131 |     #   fail. | 
 | 132 |  | 
 | 133 |     def splitroot(self, part, sep=sep): | 
 | 134 |         first = part[0:1] | 
 | 135 |         second = part[1:2] | 
 | 136 |         if (second == sep and first == sep): | 
 | 137 |             # XXX extended paths should also disable the collapsing of "." | 
 | 138 |             # components (according to MSDN docs). | 
 | 139 |             prefix, part = self._split_extended_path(part) | 
 | 140 |             first = part[0:1] | 
 | 141 |             second = part[1:2] | 
 | 142 |         else: | 
 | 143 |             prefix = '' | 
 | 144 |         third = part[2:3] | 
 | 145 |         if (second == sep and first == sep and third != sep): | 
 | 146 |             # is a UNC path: | 
 | 147 |             # vvvvvvvvvvvvvvvvvvvvv root | 
 | 148 |             # \\machine\mountpoint\directory\etc\... | 
 | 149 |             #            directory ^^^^^^^^^^^^^^ | 
 | 150 |             index = part.find(sep, 2) | 
 | 151 |             if index != -1: | 
 | 152 |                 index2 = part.find(sep, index + 1) | 
 | 153 |                 # a UNC path can't have two slashes in a row | 
 | 154 |                 # (after the initial two) | 
 | 155 |                 if index2 != index + 1: | 
 | 156 |                     if index2 == -1: | 
 | 157 |                         index2 = len(part) | 
 | 158 |                     if prefix: | 
 | 159 |                         return prefix + part[1:index2], sep, part[index2+1:] | 
 | 160 |                     else: | 
 | 161 |                         return part[:index2], sep, part[index2+1:] | 
 | 162 |         drv = root = '' | 
 | 163 |         if second == ':' and first in self.drive_letters: | 
 | 164 |             drv = part[:2] | 
 | 165 |             part = part[2:] | 
 | 166 |             first = third | 
 | 167 |         if first == sep: | 
 | 168 |             root = first | 
 | 169 |             part = part.lstrip(sep) | 
 | 170 |         return prefix + drv, root, part | 
 | 171 |  | 
 | 172 |     def casefold(self, s): | 
 | 173 |         return s.lower() | 
 | 174 |  | 
 | 175 |     def casefold_parts(self, parts): | 
 | 176 |         return [p.lower() for p in parts] | 
 | 177 |  | 
 | 178 |     def resolve(self, path): | 
 | 179 |         s = str(path) | 
 | 180 |         if not s: | 
 | 181 |             return os.getcwd() | 
 | 182 |         if _getfinalpathname is not None: | 
 | 183 |             return self._ext_to_normal(_getfinalpathname(s)) | 
 | 184 |         # Means fallback on absolute | 
 | 185 |         return None | 
 | 186 |  | 
 | 187 |     def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix): | 
 | 188 |         prefix = '' | 
 | 189 |         if s.startswith(ext_prefix): | 
 | 190 |             prefix = s[:4] | 
 | 191 |             s = s[4:] | 
 | 192 |             if s.startswith('UNC\\'): | 
 | 193 |                 prefix += s[:3] | 
 | 194 |                 s = '\\' + s[3:] | 
 | 195 |         return prefix, s | 
 | 196 |  | 
 | 197 |     def _ext_to_normal(self, s): | 
 | 198 |         # Turn back an extended path into a normal DOS-like path | 
 | 199 |         return self._split_extended_path(s)[1] | 
 | 200 |  | 
 | 201 |     def is_reserved(self, parts): | 
 | 202 |         # NOTE: the rules for reserved names seem somewhat complicated | 
 | 203 |         # (e.g. r"..\NUL" is reserved but not r"foo\NUL"). | 
 | 204 |         # We err on the side of caution and return True for paths which are | 
 | 205 |         # not considered reserved by Windows. | 
 | 206 |         if not parts: | 
 | 207 |             return False | 
 | 208 |         if parts[0].startswith('\\\\'): | 
 | 209 |             # UNC paths are never reserved | 
 | 210 |             return False | 
 | 211 |         return parts[-1].partition('.')[0].upper() in self.reserved_names | 
 | 212 |  | 
 | 213 |     def make_uri(self, path): | 
 | 214 |         # Under Windows, file URIs use the UTF-8 encoding. | 
 | 215 |         drive = path.drive | 
 | 216 |         if len(drive) == 2 and drive[1] == ':': | 
 | 217 |             # It's a path on a local drive => 'file:///c:/a/b' | 
 | 218 |             rest = path.as_posix()[2:].lstrip('/') | 
 | 219 |             return 'file:///%s/%s' % ( | 
 | 220 |                 drive, urlquote_from_bytes(rest.encode('utf-8'))) | 
 | 221 |         else: | 
 | 222 |             # It's a path on a network drive => 'file://host/share/a/b' | 
 | 223 |             return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8')) | 
 | 224 |  | 
 | 225 |  | 
 | 226 | class _PosixFlavour(_Flavour): | 
 | 227 |     sep = '/' | 
 | 228 |     altsep = '' | 
 | 229 |     has_drv = False | 
 | 230 |     pathmod = posixpath | 
 | 231 |  | 
 | 232 |     is_supported = (os.name != 'nt') | 
 | 233 |  | 
 | 234 |     def splitroot(self, part, sep=sep): | 
 | 235 |         if part and part[0] == sep: | 
 | 236 |             stripped_part = part.lstrip(sep) | 
 | 237 |             # According to POSIX path resolution: | 
 | 238 |             # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11 | 
 | 239 |             # "A pathname that begins with two successive slashes may be | 
 | 240 |             # interpreted in an implementation-defined manner, although more | 
 | 241 |             # than two leading slashes shall be treated as a single slash". | 
 | 242 |             if len(part) - len(stripped_part) == 2: | 
 | 243 |                 return '', sep * 2, stripped_part | 
 | 244 |             else: | 
 | 245 |                 return '', sep, stripped_part | 
 | 246 |         else: | 
 | 247 |             return '', '', part | 
 | 248 |  | 
 | 249 |     def casefold(self, s): | 
 | 250 |         return s | 
 | 251 |  | 
 | 252 |     def casefold_parts(self, parts): | 
 | 253 |         return parts | 
 | 254 |  | 
 | 255 |     def resolve(self, path): | 
 | 256 |         sep = self.sep | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 257 |         accessor = path._accessor | 
| Antoine Pitrou | c274fd2 | 2013-12-16 19:57:41 +0100 | [diff] [blame] | 258 |         seen = {} | 
 | 259 |         def _resolve(path, rest): | 
 | 260 |             if rest.startswith(sep): | 
 | 261 |                 path = '' | 
 | 262 |  | 
 | 263 |             for name in rest.split(sep): | 
 | 264 |                 if not name or name == '.': | 
 | 265 |                     # current dir | 
 | 266 |                     continue | 
 | 267 |                 if name == '..': | 
 | 268 |                     # parent dir | 
 | 269 |                     path, _, _ = path.rpartition(sep) | 
 | 270 |                     continue | 
 | 271 |                 newpath = path + sep + name | 
 | 272 |                 if newpath in seen: | 
 | 273 |                     # Already seen this path | 
 | 274 |                     path = seen[newpath] | 
 | 275 |                     if path is not None: | 
 | 276 |                         # use cached value | 
 | 277 |                         continue | 
 | 278 |                     # The symlink is not resolved, so we must have a symlink loop. | 
 | 279 |                     raise RuntimeError("Symlink loop from %r" % newpath) | 
 | 280 |                 # Resolve the symbolic link | 
 | 281 |                 try: | 
 | 282 |                     target = accessor.readlink(newpath) | 
 | 283 |                 except OSError as e: | 
 | 284 |                     if e.errno != EINVAL: | 
 | 285 |                         raise | 
 | 286 |                     # Not a symlink | 
 | 287 |                     path = newpath | 
 | 288 |                 else: | 
 | 289 |                     seen[newpath] = None # not resolved symlink | 
 | 290 |                     path = _resolve(path, target) | 
 | 291 |                     seen[newpath] = path # resolved symlink | 
 | 292 |  | 
 | 293 |             return path | 
 | 294 |         # NOTE: according to POSIX, getcwd() cannot contain path components | 
 | 295 |         # which are symlinks. | 
 | 296 |         base = '' if path.is_absolute() else os.getcwd() | 
 | 297 |         return _resolve(base, str(path)) or sep | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 298 |  | 
 | 299 |     def is_reserved(self, parts): | 
 | 300 |         return False | 
 | 301 |  | 
 | 302 |     def make_uri(self, path): | 
 | 303 |         # We represent the path using the local filesystem encoding, | 
 | 304 |         # for portability to other applications. | 
 | 305 |         bpath = bytes(path) | 
 | 306 |         return 'file://' + urlquote_from_bytes(bpath) | 
 | 307 |  | 
 | 308 |  | 
 | 309 | _windows_flavour = _WindowsFlavour() | 
 | 310 | _posix_flavour = _PosixFlavour() | 
 | 311 |  | 
 | 312 |  | 
 | 313 | class _Accessor: | 
 | 314 |     """An accessor implements a particular (system-specific or not) way of | 
 | 315 |     accessing paths on the filesystem.""" | 
 | 316 |  | 
 | 317 |  | 
 | 318 | class _NormalAccessor(_Accessor): | 
 | 319 |  | 
 | 320 |     def _wrap_strfunc(strfunc): | 
 | 321 |         @functools.wraps(strfunc) | 
 | 322 |         def wrapped(pathobj, *args): | 
 | 323 |             return strfunc(str(pathobj), *args) | 
 | 324 |         return staticmethod(wrapped) | 
 | 325 |  | 
 | 326 |     def _wrap_binary_strfunc(strfunc): | 
 | 327 |         @functools.wraps(strfunc) | 
 | 328 |         def wrapped(pathobjA, pathobjB, *args): | 
 | 329 |             return strfunc(str(pathobjA), str(pathobjB), *args) | 
 | 330 |         return staticmethod(wrapped) | 
 | 331 |  | 
 | 332 |     stat = _wrap_strfunc(os.stat) | 
 | 333 |  | 
 | 334 |     lstat = _wrap_strfunc(os.lstat) | 
 | 335 |  | 
 | 336 |     open = _wrap_strfunc(os.open) | 
 | 337 |  | 
 | 338 |     listdir = _wrap_strfunc(os.listdir) | 
 | 339 |  | 
 | 340 |     chmod = _wrap_strfunc(os.chmod) | 
 | 341 |  | 
 | 342 |     if hasattr(os, "lchmod"): | 
 | 343 |         lchmod = _wrap_strfunc(os.lchmod) | 
 | 344 |     else: | 
 | 345 |         def lchmod(self, pathobj, mode): | 
 | 346 |             raise NotImplementedError("lchmod() not available on this system") | 
 | 347 |  | 
 | 348 |     mkdir = _wrap_strfunc(os.mkdir) | 
 | 349 |  | 
 | 350 |     unlink = _wrap_strfunc(os.unlink) | 
 | 351 |  | 
 | 352 |     rmdir = _wrap_strfunc(os.rmdir) | 
 | 353 |  | 
 | 354 |     rename = _wrap_binary_strfunc(os.rename) | 
 | 355 |  | 
 | 356 |     replace = _wrap_binary_strfunc(os.replace) | 
 | 357 |  | 
 | 358 |     if nt: | 
 | 359 |         if supports_symlinks: | 
 | 360 |             symlink = _wrap_binary_strfunc(os.symlink) | 
 | 361 |         else: | 
 | 362 |             def symlink(a, b, target_is_directory): | 
 | 363 |                 raise NotImplementedError("symlink() not available on this system") | 
 | 364 |     else: | 
 | 365 |         # Under POSIX, os.symlink() takes two args | 
 | 366 |         @staticmethod | 
 | 367 |         def symlink(a, b, target_is_directory): | 
 | 368 |             return os.symlink(str(a), str(b)) | 
 | 369 |  | 
 | 370 |     utime = _wrap_strfunc(os.utime) | 
 | 371 |  | 
 | 372 |     # Helper for resolve() | 
 | 373 |     def readlink(self, path): | 
 | 374 |         return os.readlink(path) | 
 | 375 |  | 
 | 376 |  | 
 | 377 | _normal_accessor = _NormalAccessor() | 
 | 378 |  | 
 | 379 |  | 
 | 380 | # | 
 | 381 | # Globbing helpers | 
 | 382 | # | 
 | 383 |  | 
 | 384 | @contextmanager | 
 | 385 | def _cached(func): | 
 | 386 |     try: | 
 | 387 |         func.__cached__ | 
 | 388 |         yield func | 
 | 389 |     except AttributeError: | 
 | 390 |         cache = {} | 
 | 391 |         def wrapper(*args): | 
 | 392 |             try: | 
 | 393 |                 return cache[args] | 
 | 394 |             except KeyError: | 
 | 395 |                 value = cache[args] = func(*args) | 
 | 396 |                 return value | 
 | 397 |         wrapper.__cached__ = True | 
 | 398 |         try: | 
 | 399 |             yield wrapper | 
 | 400 |         finally: | 
 | 401 |             cache.clear() | 
 | 402 |  | 
 | 403 | def _make_selector(pattern_parts): | 
 | 404 |     pat = pattern_parts[0] | 
 | 405 |     child_parts = pattern_parts[1:] | 
 | 406 |     if pat == '**': | 
 | 407 |         cls = _RecursiveWildcardSelector | 
 | 408 |     elif '**' in pat: | 
 | 409 |         raise ValueError("Invalid pattern: '**' can only be an entire path component") | 
 | 410 |     elif _is_wildcard_pattern(pat): | 
 | 411 |         cls = _WildcardSelector | 
 | 412 |     else: | 
 | 413 |         cls = _PreciseSelector | 
 | 414 |     return cls(pat, child_parts) | 
 | 415 |  | 
 | 416 | if hasattr(functools, "lru_cache"): | 
 | 417 |     _make_selector = functools.lru_cache()(_make_selector) | 
 | 418 |  | 
 | 419 |  | 
 | 420 | class _Selector: | 
 | 421 |     """A selector matches a specific glob pattern part against the children | 
 | 422 |     of a given path.""" | 
 | 423 |  | 
 | 424 |     def __init__(self, child_parts): | 
 | 425 |         self.child_parts = child_parts | 
 | 426 |         if child_parts: | 
 | 427 |             self.successor = _make_selector(child_parts) | 
 | 428 |         else: | 
 | 429 |             self.successor = _TerminatingSelector() | 
 | 430 |  | 
 | 431 |     def select_from(self, parent_path): | 
 | 432 |         """Iterate over all child paths of `parent_path` matched by this | 
 | 433 |         selector.  This can contain parent_path itself.""" | 
 | 434 |         path_cls = type(parent_path) | 
 | 435 |         is_dir = path_cls.is_dir | 
 | 436 |         exists = path_cls.exists | 
 | 437 |         listdir = parent_path._accessor.listdir | 
 | 438 |         return self._select_from(parent_path, is_dir, exists, listdir) | 
 | 439 |  | 
 | 440 |  | 
 | 441 | class _TerminatingSelector: | 
 | 442 |  | 
 | 443 |     def _select_from(self, parent_path, is_dir, exists, listdir): | 
 | 444 |         yield parent_path | 
 | 445 |  | 
 | 446 |  | 
 | 447 | class _PreciseSelector(_Selector): | 
 | 448 |  | 
 | 449 |     def __init__(self, name, child_parts): | 
 | 450 |         self.name = name | 
 | 451 |         _Selector.__init__(self, child_parts) | 
 | 452 |  | 
 | 453 |     def _select_from(self, parent_path, is_dir, exists, listdir): | 
 | 454 |         if not is_dir(parent_path): | 
 | 455 |             return | 
 | 456 |         path = parent_path._make_child_relpath(self.name) | 
 | 457 |         if exists(path): | 
 | 458 |             for p in self.successor._select_from(path, is_dir, exists, listdir): | 
 | 459 |                 yield p | 
 | 460 |  | 
 | 461 |  | 
 | 462 | class _WildcardSelector(_Selector): | 
 | 463 |  | 
 | 464 |     def __init__(self, pat, child_parts): | 
 | 465 |         self.pat = re.compile(fnmatch.translate(pat)) | 
 | 466 |         _Selector.__init__(self, child_parts) | 
 | 467 |  | 
 | 468 |     def _select_from(self, parent_path, is_dir, exists, listdir): | 
 | 469 |         if not is_dir(parent_path): | 
 | 470 |             return | 
 | 471 |         cf = parent_path._flavour.casefold | 
 | 472 |         for name in listdir(parent_path): | 
 | 473 |             casefolded = cf(name) | 
 | 474 |             if self.pat.match(casefolded): | 
 | 475 |                 path = parent_path._make_child_relpath(name) | 
 | 476 |                 for p in self.successor._select_from(path, is_dir, exists, listdir): | 
 | 477 |                     yield p | 
 | 478 |  | 
 | 479 |  | 
 | 480 | class _RecursiveWildcardSelector(_Selector): | 
 | 481 |  | 
 | 482 |     def __init__(self, pat, child_parts): | 
 | 483 |         _Selector.__init__(self, child_parts) | 
 | 484 |  | 
 | 485 |     def _iterate_directories(self, parent_path, is_dir, listdir): | 
 | 486 |         yield parent_path | 
 | 487 |         for name in listdir(parent_path): | 
 | 488 |             path = parent_path._make_child_relpath(name) | 
 | 489 |             if is_dir(path): | 
 | 490 |                 for p in self._iterate_directories(path, is_dir, listdir): | 
 | 491 |                     yield p | 
 | 492 |  | 
 | 493 |     def _select_from(self, parent_path, is_dir, exists, listdir): | 
 | 494 |         if not is_dir(parent_path): | 
 | 495 |             return | 
 | 496 |         with _cached(listdir) as listdir: | 
 | 497 |             yielded = set() | 
 | 498 |             try: | 
 | 499 |                 successor_select = self.successor._select_from | 
 | 500 |                 for starting_point in self._iterate_directories(parent_path, is_dir, listdir): | 
 | 501 |                     for p in successor_select(starting_point, is_dir, exists, listdir): | 
 | 502 |                         if p not in yielded: | 
 | 503 |                             yield p | 
 | 504 |                             yielded.add(p) | 
 | 505 |             finally: | 
 | 506 |                 yielded.clear() | 
 | 507 |  | 
 | 508 |  | 
 | 509 | # | 
 | 510 | # Public API | 
 | 511 | # | 
 | 512 |  | 
 | 513 | class _PathParents(Sequence): | 
 | 514 |     """This object provides sequence-like access to the logical ancestors | 
 | 515 |     of a path.  Don't try to construct it yourself.""" | 
 | 516 |     __slots__ = ('_pathcls', '_drv', '_root', '_parts') | 
 | 517 |  | 
 | 518 |     def __init__(self, path): | 
 | 519 |         # We don't store the instance to avoid reference cycles | 
 | 520 |         self._pathcls = type(path) | 
 | 521 |         self._drv = path._drv | 
 | 522 |         self._root = path._root | 
 | 523 |         self._parts = path._parts | 
 | 524 |  | 
 | 525 |     def __len__(self): | 
 | 526 |         if self._drv or self._root: | 
 | 527 |             return len(self._parts) - 1 | 
 | 528 |         else: | 
 | 529 |             return len(self._parts) | 
 | 530 |  | 
 | 531 |     def __getitem__(self, idx): | 
 | 532 |         if idx < 0 or idx >= len(self): | 
 | 533 |             raise IndexError(idx) | 
 | 534 |         return self._pathcls._from_parsed_parts(self._drv, self._root, | 
 | 535 |                                                 self._parts[:-idx - 1]) | 
 | 536 |  | 
 | 537 |     def __repr__(self): | 
 | 538 |         return "<{}.parents>".format(self._pathcls.__name__) | 
 | 539 |  | 
 | 540 |  | 
 | 541 | class PurePath(object): | 
 | 542 |     """PurePath represents a filesystem path and offers operations which | 
 | 543 |     don't imply any actual filesystem I/O.  Depending on your system, | 
 | 544 |     instantiating a PurePath will return either a PurePosixPath or a | 
 | 545 |     PureWindowsPath object.  You can also instantiate either of these classes | 
 | 546 |     directly, regardless of your system. | 
 | 547 |     """ | 
 | 548 |     __slots__ = ( | 
 | 549 |         '_drv', '_root', '_parts', | 
 | 550 |         '_str', '_hash', '_pparts', '_cached_cparts', | 
 | 551 |     ) | 
 | 552 |  | 
 | 553 |     def __new__(cls, *args): | 
 | 554 |         """Construct a PurePath from one or several strings and or existing | 
 | 555 |         PurePath objects.  The strings and path objects are combined so as | 
 | 556 |         to yield a canonicalized path, which is incorporated into the | 
 | 557 |         new PurePath object. | 
 | 558 |         """ | 
 | 559 |         if cls is PurePath: | 
 | 560 |             cls = PureWindowsPath if os.name == 'nt' else PurePosixPath | 
 | 561 |         return cls._from_parts(args) | 
 | 562 |  | 
 | 563 |     def __reduce__(self): | 
 | 564 |         # Using the parts tuple helps share interned path parts | 
 | 565 |         # when pickling related paths. | 
 | 566 |         return (self.__class__, tuple(self._parts)) | 
 | 567 |  | 
 | 568 |     @classmethod | 
 | 569 |     def _parse_args(cls, args): | 
 | 570 |         # This is useful when you don't want to create an instance, just | 
 | 571 |         # canonicalize some constructor arguments. | 
 | 572 |         parts = [] | 
 | 573 |         for a in args: | 
 | 574 |             if isinstance(a, PurePath): | 
 | 575 |                 parts += a._parts | 
 | 576 |             elif isinstance(a, str): | 
 | 577 |                 # Assuming a str | 
 | 578 |                 parts.append(a) | 
 | 579 |             else: | 
 | 580 |                 raise TypeError( | 
 | 581 |                     "argument should be a path or str object, not %r" | 
 | 582 |                     % type(a)) | 
 | 583 |         return cls._flavour.parse_parts(parts) | 
 | 584 |  | 
 | 585 |     @classmethod | 
 | 586 |     def _from_parts(cls, args, init=True): | 
 | 587 |         # We need to call _parse_args on the instance, so as to get the | 
 | 588 |         # right flavour. | 
 | 589 |         self = object.__new__(cls) | 
 | 590 |         drv, root, parts = self._parse_args(args) | 
 | 591 |         self._drv = drv | 
 | 592 |         self._root = root | 
 | 593 |         self._parts = parts | 
 | 594 |         if init: | 
 | 595 |             self._init() | 
 | 596 |         return self | 
 | 597 |  | 
 | 598 |     @classmethod | 
 | 599 |     def _from_parsed_parts(cls, drv, root, parts, init=True): | 
 | 600 |         self = object.__new__(cls) | 
 | 601 |         self._drv = drv | 
 | 602 |         self._root = root | 
 | 603 |         self._parts = parts | 
 | 604 |         if init: | 
 | 605 |             self._init() | 
 | 606 |         return self | 
 | 607 |  | 
 | 608 |     @classmethod | 
 | 609 |     def _format_parsed_parts(cls, drv, root, parts): | 
 | 610 |         if drv or root: | 
 | 611 |             return drv + root + cls._flavour.join(parts[1:]) | 
 | 612 |         else: | 
 | 613 |             return cls._flavour.join(parts) | 
 | 614 |  | 
 | 615 |     def _init(self): | 
 | 616 |         # Overriden in concrete Path | 
 | 617 |         pass | 
 | 618 |  | 
 | 619 |     def _make_child(self, args): | 
 | 620 |         drv, root, parts = self._parse_args(args) | 
 | 621 |         drv, root, parts = self._flavour.join_parsed_parts( | 
 | 622 |             self._drv, self._root, self._parts, drv, root, parts) | 
 | 623 |         return self._from_parsed_parts(drv, root, parts) | 
 | 624 |  | 
 | 625 |     def __str__(self): | 
 | 626 |         """Return the string representation of the path, suitable for | 
 | 627 |         passing to system calls.""" | 
 | 628 |         try: | 
 | 629 |             return self._str | 
 | 630 |         except AttributeError: | 
 | 631 |             self._str = self._format_parsed_parts(self._drv, self._root, | 
 | 632 |                                                   self._parts) or '.' | 
 | 633 |             return self._str | 
 | 634 |  | 
 | 635 |     def as_posix(self): | 
 | 636 |         """Return the string representation of the path with forward (/) | 
 | 637 |         slashes.""" | 
 | 638 |         f = self._flavour | 
 | 639 |         return str(self).replace(f.sep, '/') | 
 | 640 |  | 
 | 641 |     def __bytes__(self): | 
 | 642 |         """Return the bytes representation of the path.  This is only | 
 | 643 |         recommended to use under Unix.""" | 
 | 644 |         return os.fsencode(str(self)) | 
 | 645 |  | 
 | 646 |     def __repr__(self): | 
 | 647 |         return "{}({!r})".format(self.__class__.__name__, self.as_posix()) | 
 | 648 |  | 
 | 649 |     def as_uri(self): | 
 | 650 |         """Return the path as a 'file' URI.""" | 
 | 651 |         if not self.is_absolute(): | 
 | 652 |             raise ValueError("relative path can't be expressed as a file URI") | 
 | 653 |         return self._flavour.make_uri(self) | 
 | 654 |  | 
 | 655 |     @property | 
 | 656 |     def _cparts(self): | 
 | 657 |         # Cached casefolded parts, for hashing and comparison | 
 | 658 |         try: | 
 | 659 |             return self._cached_cparts | 
 | 660 |         except AttributeError: | 
 | 661 |             self._cached_cparts = self._flavour.casefold_parts(self._parts) | 
 | 662 |             return self._cached_cparts | 
 | 663 |  | 
 | 664 |     def __eq__(self, other): | 
 | 665 |         if not isinstance(other, PurePath): | 
 | 666 |             return NotImplemented | 
 | 667 |         return self._cparts == other._cparts and self._flavour is other._flavour | 
 | 668 |  | 
 | 669 |     def __ne__(self, other): | 
 | 670 |         return not self == other | 
 | 671 |  | 
 | 672 |     def __hash__(self): | 
 | 673 |         try: | 
 | 674 |             return self._hash | 
 | 675 |         except AttributeError: | 
 | 676 |             self._hash = hash(tuple(self._cparts)) | 
 | 677 |             return self._hash | 
 | 678 |  | 
 | 679 |     def __lt__(self, other): | 
 | 680 |         if not isinstance(other, PurePath) or self._flavour is not other._flavour: | 
 | 681 |             return NotImplemented | 
 | 682 |         return self._cparts < other._cparts | 
 | 683 |  | 
 | 684 |     def __le__(self, other): | 
 | 685 |         if not isinstance(other, PurePath) or self._flavour is not other._flavour: | 
 | 686 |             return NotImplemented | 
 | 687 |         return self._cparts <= other._cparts | 
 | 688 |  | 
 | 689 |     def __gt__(self, other): | 
 | 690 |         if not isinstance(other, PurePath) or self._flavour is not other._flavour: | 
 | 691 |             return NotImplemented | 
 | 692 |         return self._cparts > other._cparts | 
 | 693 |  | 
 | 694 |     def __ge__(self, other): | 
 | 695 |         if not isinstance(other, PurePath) or self._flavour is not other._flavour: | 
 | 696 |             return NotImplemented | 
 | 697 |         return self._cparts >= other._cparts | 
 | 698 |  | 
 | 699 |     drive = property(attrgetter('_drv'), | 
 | 700 |                      doc="""The drive prefix (letter or UNC path), if any.""") | 
 | 701 |  | 
 | 702 |     root = property(attrgetter('_root'), | 
 | 703 |                     doc="""The root of the path, if any.""") | 
 | 704 |  | 
 | 705 |     @property | 
 | 706 |     def anchor(self): | 
 | 707 |         """The concatenation of the drive and root, or ''.""" | 
 | 708 |         anchor = self._drv + self._root | 
 | 709 |         return anchor | 
 | 710 |  | 
 | 711 |     @property | 
 | 712 |     def name(self): | 
 | 713 |         """The final path component, if any.""" | 
 | 714 |         parts = self._parts | 
 | 715 |         if len(parts) == (1 if (self._drv or self._root) else 0): | 
 | 716 |             return '' | 
 | 717 |         return parts[-1] | 
 | 718 |  | 
 | 719 |     @property | 
 | 720 |     def suffix(self): | 
 | 721 |         """The final component's last suffix, if any.""" | 
 | 722 |         name = self.name | 
 | 723 |         i = name.rfind('.') | 
 | 724 |         if 0 < i < len(name) - 1: | 
 | 725 |             return name[i:] | 
 | 726 |         else: | 
 | 727 |             return '' | 
 | 728 |  | 
 | 729 |     @property | 
 | 730 |     def suffixes(self): | 
 | 731 |         """A list of the final component's suffixes, if any.""" | 
 | 732 |         name = self.name | 
 | 733 |         if name.endswith('.'): | 
 | 734 |             return [] | 
 | 735 |         name = name.lstrip('.') | 
 | 736 |         return ['.' + suffix for suffix in name.split('.')[1:]] | 
 | 737 |  | 
 | 738 |     @property | 
 | 739 |     def stem(self): | 
 | 740 |         """The final path component, minus its last suffix.""" | 
 | 741 |         name = self.name | 
 | 742 |         i = name.rfind('.') | 
 | 743 |         if 0 < i < len(name) - 1: | 
 | 744 |             return name[:i] | 
 | 745 |         else: | 
 | 746 |             return name | 
 | 747 |  | 
 | 748 |     def with_name(self, name): | 
 | 749 |         """Return a new path with the file name changed.""" | 
 | 750 |         if not self.name: | 
 | 751 |             raise ValueError("%r has an empty name" % (self,)) | 
 | 752 |         return self._from_parsed_parts(self._drv, self._root, | 
 | 753 |                                        self._parts[:-1] + [name]) | 
 | 754 |  | 
 | 755 |     def with_suffix(self, suffix): | 
 | 756 |         """Return a new path with the file suffix changed (or added, if none).""" | 
 | 757 |         # XXX if suffix is None, should the current suffix be removed? | 
| Antoine Pitrou | 1b02da9 | 2014-01-03 00:07:17 +0100 | [diff] [blame] | 758 |         drv, root, parts = self._flavour.parse_parts((suffix,)) | 
 | 759 |         if drv or root or len(parts) != 1: | 
 | 760 |             raise ValueError("Invalid suffix %r" % (suffix)) | 
 | 761 |         suffix = parts[0] | 
 | 762 |         if not suffix.startswith('.'): | 
 | 763 |             raise ValueError("Invalid suffix %r" % (suffix)) | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 764 |         name = self.name | 
 | 765 |         if not name: | 
 | 766 |             raise ValueError("%r has an empty name" % (self,)) | 
 | 767 |         old_suffix = self.suffix | 
 | 768 |         if not old_suffix: | 
 | 769 |             name = name + suffix | 
 | 770 |         else: | 
 | 771 |             name = name[:-len(old_suffix)] + suffix | 
 | 772 |         return self._from_parsed_parts(self._drv, self._root, | 
 | 773 |                                        self._parts[:-1] + [name]) | 
 | 774 |  | 
 | 775 |     def relative_to(self, *other): | 
 | 776 |         """Return the relative path to another path identified by the passed | 
 | 777 |         arguments.  If the operation is not possible (because this is not | 
 | 778 |         a subpath of the other path), raise ValueError. | 
 | 779 |         """ | 
 | 780 |         # For the purpose of this method, drive and root are considered | 
 | 781 |         # separate parts, i.e.: | 
 | 782 |         #   Path('c:/').relative_to('c:')  gives Path('/') | 
 | 783 |         #   Path('c:/').relative_to('/')   raise ValueError | 
 | 784 |         if not other: | 
 | 785 |             raise TypeError("need at least one argument") | 
 | 786 |         parts = self._parts | 
 | 787 |         drv = self._drv | 
 | 788 |         root = self._root | 
| Antoine Pitrou | 156b361 | 2013-12-28 19:49:04 +0100 | [diff] [blame] | 789 |         if root: | 
 | 790 |             abs_parts = [drv, root] + parts[1:] | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 791 |         else: | 
 | 792 |             abs_parts = parts | 
 | 793 |         to_drv, to_root, to_parts = self._parse_args(other) | 
| Antoine Pitrou | 156b361 | 2013-12-28 19:49:04 +0100 | [diff] [blame] | 794 |         if to_root: | 
 | 795 |             to_abs_parts = [to_drv, to_root] + to_parts[1:] | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 796 |         else: | 
 | 797 |             to_abs_parts = to_parts | 
 | 798 |         n = len(to_abs_parts) | 
| Antoine Pitrou | 156b361 | 2013-12-28 19:49:04 +0100 | [diff] [blame] | 799 |         cf = self._flavour.casefold_parts | 
 | 800 |         if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts): | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 801 |             formatted = self._format_parsed_parts(to_drv, to_root, to_parts) | 
 | 802 |             raise ValueError("{!r} does not start with {!r}" | 
 | 803 |                              .format(str(self), str(formatted))) | 
| Antoine Pitrou | 156b361 | 2013-12-28 19:49:04 +0100 | [diff] [blame] | 804 |         return self._from_parsed_parts('', root if n == 1 else '', | 
 | 805 |                                        abs_parts[n:]) | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 806 |  | 
 | 807 |     @property | 
 | 808 |     def parts(self): | 
 | 809 |         """An object providing sequence-like access to the | 
 | 810 |         components in the filesystem path.""" | 
 | 811 |         # We cache the tuple to avoid building a new one each time .parts | 
 | 812 |         # is accessed.  XXX is this necessary? | 
 | 813 |         try: | 
 | 814 |             return self._pparts | 
 | 815 |         except AttributeError: | 
 | 816 |             self._pparts = tuple(self._parts) | 
 | 817 |             return self._pparts | 
 | 818 |  | 
 | 819 |     def joinpath(self, *args): | 
 | 820 |         """Combine this path with one or several arguments, and return a | 
 | 821 |         new path representing either a subpath (if all arguments are relative | 
 | 822 |         paths) or a totally different path (if one of the arguments is | 
 | 823 |         anchored). | 
 | 824 |         """ | 
 | 825 |         return self._make_child(args) | 
 | 826 |  | 
 | 827 |     def __truediv__(self, key): | 
 | 828 |         return self._make_child((key,)) | 
 | 829 |  | 
 | 830 |     def __rtruediv__(self, key): | 
 | 831 |         return self._from_parts([key] + self._parts) | 
 | 832 |  | 
 | 833 |     @property | 
 | 834 |     def parent(self): | 
 | 835 |         """The logical parent of the path.""" | 
 | 836 |         drv = self._drv | 
 | 837 |         root = self._root | 
 | 838 |         parts = self._parts | 
 | 839 |         if len(parts) == 1 and (drv or root): | 
 | 840 |             return self | 
 | 841 |         return self._from_parsed_parts(drv, root, parts[:-1]) | 
 | 842 |  | 
 | 843 |     @property | 
 | 844 |     def parents(self): | 
 | 845 |         """A sequence of this path's logical parents.""" | 
 | 846 |         return _PathParents(self) | 
 | 847 |  | 
 | 848 |     def is_absolute(self): | 
 | 849 |         """True if the path is absolute (has both a root and, if applicable, | 
 | 850 |         a drive).""" | 
 | 851 |         if not self._root: | 
 | 852 |             return False | 
 | 853 |         return not self._flavour.has_drv or bool(self._drv) | 
 | 854 |  | 
 | 855 |     def is_reserved(self): | 
 | 856 |         """Return True if the path contains one of the special names reserved | 
 | 857 |         by the system, if any.""" | 
 | 858 |         return self._flavour.is_reserved(self._parts) | 
 | 859 |  | 
 | 860 |     def match(self, path_pattern): | 
 | 861 |         """ | 
 | 862 |         Return True if this path matches the given pattern. | 
 | 863 |         """ | 
 | 864 |         cf = self._flavour.casefold | 
 | 865 |         path_pattern = cf(path_pattern) | 
 | 866 |         drv, root, pat_parts = self._flavour.parse_parts((path_pattern,)) | 
 | 867 |         if not pat_parts: | 
 | 868 |             raise ValueError("empty pattern") | 
 | 869 |         if drv and drv != cf(self._drv): | 
 | 870 |             return False | 
 | 871 |         if root and root != cf(self._root): | 
 | 872 |             return False | 
 | 873 |         parts = self._cparts | 
 | 874 |         if drv or root: | 
 | 875 |             if len(pat_parts) != len(parts): | 
 | 876 |                 return False | 
 | 877 |             pat_parts = pat_parts[1:] | 
 | 878 |         elif len(pat_parts) > len(parts): | 
 | 879 |             return False | 
 | 880 |         for part, pat in zip(reversed(parts), reversed(pat_parts)): | 
 | 881 |             if not fnmatch.fnmatchcase(part, pat): | 
 | 882 |                 return False | 
 | 883 |         return True | 
 | 884 |  | 
 | 885 |  | 
 | 886 | class PurePosixPath(PurePath): | 
 | 887 |     _flavour = _posix_flavour | 
 | 888 |     __slots__ = () | 
 | 889 |  | 
 | 890 |  | 
 | 891 | class PureWindowsPath(PurePath): | 
 | 892 |     _flavour = _windows_flavour | 
 | 893 |     __slots__ = () | 
 | 894 |  | 
 | 895 |  | 
 | 896 | # Filesystem-accessing classes | 
 | 897 |  | 
 | 898 |  | 
 | 899 | class Path(PurePath): | 
 | 900 |     __slots__ = ( | 
 | 901 |         '_accessor', | 
 | 902 |         '_closed', | 
 | 903 |     ) | 
 | 904 |  | 
 | 905 |     def __new__(cls, *args, **kwargs): | 
 | 906 |         if cls is Path: | 
 | 907 |             cls = WindowsPath if os.name == 'nt' else PosixPath | 
 | 908 |         self = cls._from_parts(args, init=False) | 
 | 909 |         if not self._flavour.is_supported: | 
 | 910 |             raise NotImplementedError("cannot instantiate %r on your system" | 
 | 911 |                                       % (cls.__name__,)) | 
 | 912 |         self._init() | 
 | 913 |         return self | 
 | 914 |  | 
 | 915 |     def _init(self, | 
 | 916 |               # Private non-constructor arguments | 
 | 917 |               template=None, | 
 | 918 |               ): | 
 | 919 |         self._closed = False | 
 | 920 |         if template is not None: | 
 | 921 |             self._accessor = template._accessor | 
 | 922 |         else: | 
 | 923 |             self._accessor = _normal_accessor | 
 | 924 |  | 
 | 925 |     def _make_child_relpath(self, part): | 
 | 926 |         # This is an optimization used for dir walking.  `part` must be | 
 | 927 |         # a single part relative to this path. | 
 | 928 |         parts = self._parts + [part] | 
 | 929 |         return self._from_parsed_parts(self._drv, self._root, parts) | 
 | 930 |  | 
 | 931 |     def __enter__(self): | 
 | 932 |         if self._closed: | 
 | 933 |             self._raise_closed() | 
 | 934 |         return self | 
 | 935 |  | 
 | 936 |     def __exit__(self, t, v, tb): | 
 | 937 |         self._closed = True | 
 | 938 |  | 
 | 939 |     def _raise_closed(self): | 
 | 940 |         raise ValueError("I/O operation on closed path") | 
 | 941 |  | 
 | 942 |     def _opener(self, name, flags, mode=0o666): | 
 | 943 |         # A stub for the opener argument to built-in open() | 
 | 944 |         return self._accessor.open(self, flags, mode) | 
 | 945 |  | 
| Antoine Pitrou | 4a60d42 | 2013-12-02 21:25:18 +0100 | [diff] [blame] | 946 |     def _raw_open(self, flags, mode=0o777): | 
 | 947 |         """ | 
 | 948 |         Open the file pointed by this path and return a file descriptor, | 
 | 949 |         as os.open() does. | 
 | 950 |         """ | 
 | 951 |         if self._closed: | 
 | 952 |             self._raise_closed() | 
 | 953 |         return self._accessor.open(self, flags, mode) | 
 | 954 |  | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 955 |     # Public API | 
 | 956 |  | 
 | 957 |     @classmethod | 
 | 958 |     def cwd(cls): | 
 | 959 |         """Return a new path pointing to the current working directory | 
 | 960 |         (as returned by os.getcwd()). | 
 | 961 |         """ | 
 | 962 |         return cls(os.getcwd()) | 
 | 963 |  | 
 | 964 |     def iterdir(self): | 
 | 965 |         """Iterate over the files in this directory.  Does not yield any | 
 | 966 |         result for the special paths '.' and '..'. | 
 | 967 |         """ | 
 | 968 |         if self._closed: | 
 | 969 |             self._raise_closed() | 
 | 970 |         for name in self._accessor.listdir(self): | 
 | 971 |             if name in {'.', '..'}: | 
 | 972 |                 # Yielding a path object for these makes little sense | 
 | 973 |                 continue | 
 | 974 |             yield self._make_child_relpath(name) | 
 | 975 |             if self._closed: | 
 | 976 |                 self._raise_closed() | 
 | 977 |  | 
 | 978 |     def glob(self, pattern): | 
 | 979 |         """Iterate over this subtree and yield all existing files (of any | 
 | 980 |         kind, including directories) matching the given pattern. | 
 | 981 |         """ | 
 | 982 |         pattern = self._flavour.casefold(pattern) | 
 | 983 |         drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) | 
 | 984 |         if drv or root: | 
 | 985 |             raise NotImplementedError("Non-relative patterns are unsupported") | 
 | 986 |         selector = _make_selector(tuple(pattern_parts)) | 
 | 987 |         for p in selector.select_from(self): | 
 | 988 |             yield p | 
 | 989 |  | 
 | 990 |     def rglob(self, pattern): | 
 | 991 |         """Recursively yield all existing files (of any kind, including | 
 | 992 |         directories) matching the given pattern, anywhere in this subtree. | 
 | 993 |         """ | 
 | 994 |         pattern = self._flavour.casefold(pattern) | 
 | 995 |         drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) | 
 | 996 |         if drv or root: | 
 | 997 |             raise NotImplementedError("Non-relative patterns are unsupported") | 
 | 998 |         selector = _make_selector(("**",) + tuple(pattern_parts)) | 
 | 999 |         for p in selector.select_from(self): | 
 | 1000 |             yield p | 
 | 1001 |  | 
 | 1002 |     def absolute(self): | 
 | 1003 |         """Return an absolute version of this path.  This function works | 
 | 1004 |         even if the path doesn't point to anything. | 
 | 1005 |  | 
 | 1006 |         No normalization is done, i.e. all '.' and '..' will be kept along. | 
 | 1007 |         Use resolve() to get the canonical path to a file. | 
 | 1008 |         """ | 
 | 1009 |         # XXX untested yet! | 
 | 1010 |         if self._closed: | 
 | 1011 |             self._raise_closed() | 
 | 1012 |         if self.is_absolute(): | 
 | 1013 |             return self | 
 | 1014 |         # FIXME this must defer to the specific flavour (and, under Windows, | 
 | 1015 |         # use nt._getfullpathname()) | 
 | 1016 |         obj = self._from_parts([os.getcwd()] + self._parts, init=False) | 
 | 1017 |         obj._init(template=self) | 
 | 1018 |         return obj | 
 | 1019 |  | 
 | 1020 |     def resolve(self): | 
 | 1021 |         """ | 
 | 1022 |         Make the path absolute, resolving all symlinks on the way and also | 
 | 1023 |         normalizing it (for example turning slashes into backslashes under | 
 | 1024 |         Windows). | 
 | 1025 |         """ | 
 | 1026 |         if self._closed: | 
 | 1027 |             self._raise_closed() | 
 | 1028 |         s = self._flavour.resolve(self) | 
 | 1029 |         if s is None: | 
 | 1030 |             # No symlink resolution => for consistency, raise an error if | 
 | 1031 |             # the path doesn't exist or is forbidden | 
 | 1032 |             self.stat() | 
 | 1033 |             s = str(self.absolute()) | 
 | 1034 |         # Now we have no symlinks in the path, it's safe to normalize it. | 
 | 1035 |         normed = self._flavour.pathmod.normpath(s) | 
 | 1036 |         obj = self._from_parts((normed,), init=False) | 
 | 1037 |         obj._init(template=self) | 
 | 1038 |         return obj | 
 | 1039 |  | 
 | 1040 |     def stat(self): | 
 | 1041 |         """ | 
 | 1042 |         Return the result of the stat() system call on this path, like | 
 | 1043 |         os.stat() does. | 
 | 1044 |         """ | 
 | 1045 |         return self._accessor.stat(self) | 
 | 1046 |  | 
 | 1047 |     def owner(self): | 
 | 1048 |         """ | 
 | 1049 |         Return the login name of the file owner. | 
 | 1050 |         """ | 
 | 1051 |         import pwd | 
 | 1052 |         return pwd.getpwuid(self.stat().st_uid).pw_name | 
 | 1053 |  | 
 | 1054 |     def group(self): | 
 | 1055 |         """ | 
 | 1056 |         Return the group name of the file gid. | 
 | 1057 |         """ | 
 | 1058 |         import grp | 
 | 1059 |         return grp.getgrgid(self.stat().st_gid).gr_name | 
 | 1060 |  | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 1061 |     def open(self, mode='r', buffering=-1, encoding=None, | 
 | 1062 |              errors=None, newline=None): | 
 | 1063 |         """ | 
 | 1064 |         Open the file pointed by this path and return a file object, as | 
 | 1065 |         the built-in open() function does. | 
 | 1066 |         """ | 
 | 1067 |         if self._closed: | 
 | 1068 |             self._raise_closed() | 
 | 1069 |         return io.open(str(self), mode, buffering, encoding, errors, newline, | 
 | 1070 |                        opener=self._opener) | 
 | 1071 |  | 
 | 1072 |     def touch(self, mode=0o666, exist_ok=True): | 
 | 1073 |         """ | 
 | 1074 |         Create this file with the given access mode, if it doesn't exist. | 
 | 1075 |         """ | 
 | 1076 |         if self._closed: | 
 | 1077 |             self._raise_closed() | 
 | 1078 |         if exist_ok: | 
 | 1079 |             # First try to bump modification time | 
 | 1080 |             # Implementation note: GNU touch uses the UTIME_NOW option of | 
 | 1081 |             # the utimensat() / futimens() functions. | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 1082 |             try: | 
| Antoine Pitrou | 2cf3917 | 2013-11-23 15:25:59 +0100 | [diff] [blame] | 1083 |                 self._accessor.utime(self, None) | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 1084 |             except OSError: | 
 | 1085 |                 # Avoid exception chaining | 
 | 1086 |                 pass | 
 | 1087 |             else: | 
 | 1088 |                 return | 
 | 1089 |         flags = os.O_CREAT | os.O_WRONLY | 
 | 1090 |         if not exist_ok: | 
 | 1091 |             flags |= os.O_EXCL | 
 | 1092 |         fd = self._raw_open(flags, mode) | 
 | 1093 |         os.close(fd) | 
 | 1094 |  | 
 | 1095 |     def mkdir(self, mode=0o777, parents=False): | 
 | 1096 |         if self._closed: | 
 | 1097 |             self._raise_closed() | 
 | 1098 |         if not parents: | 
 | 1099 |             self._accessor.mkdir(self, mode) | 
 | 1100 |         else: | 
 | 1101 |             try: | 
 | 1102 |                 self._accessor.mkdir(self, mode) | 
 | 1103 |             except OSError as e: | 
 | 1104 |                 if e.errno != ENOENT: | 
 | 1105 |                     raise | 
| Antoine Pitrou | 0048c98 | 2013-12-16 20:22:37 +0100 | [diff] [blame] | 1106 |                 self.parent.mkdir(parents=True) | 
| Antoine Pitrou | 31119e4 | 2013-11-22 17:38:12 +0100 | [diff] [blame] | 1107 |                 self._accessor.mkdir(self, mode) | 
 | 1108 |  | 
 | 1109 |     def chmod(self, mode): | 
 | 1110 |         """ | 
 | 1111 |         Change the permissions of the path, like os.chmod(). | 
 | 1112 |         """ | 
 | 1113 |         if self._closed: | 
 | 1114 |             self._raise_closed() | 
 | 1115 |         self._accessor.chmod(self, mode) | 
 | 1116 |  | 
 | 1117 |     def lchmod(self, mode): | 
 | 1118 |         """ | 
 | 1119 |         Like chmod(), except if the path points to a symlink, the symlink's | 
 | 1120 |         permissions are changed, rather than its target's. | 
 | 1121 |         """ | 
 | 1122 |         if self._closed: | 
 | 1123 |             self._raise_closed() | 
 | 1124 |         self._accessor.lchmod(self, mode) | 
 | 1125 |  | 
 | 1126 |     def unlink(self): | 
 | 1127 |         """ | 
 | 1128 |         Remove this file or link. | 
 | 1129 |         If the path is a directory, use rmdir() instead. | 
 | 1130 |         """ | 
 | 1131 |         if self._closed: | 
 | 1132 |             self._raise_closed() | 
 | 1133 |         self._accessor.unlink(self) | 
 | 1134 |  | 
 | 1135 |     def rmdir(self): | 
 | 1136 |         """ | 
 | 1137 |         Remove this directory.  The directory must be empty. | 
 | 1138 |         """ | 
 | 1139 |         if self._closed: | 
 | 1140 |             self._raise_closed() | 
 | 1141 |         self._accessor.rmdir(self) | 
 | 1142 |  | 
 | 1143 |     def lstat(self): | 
 | 1144 |         """ | 
 | 1145 |         Like stat(), except if the path points to a symlink, the symlink's | 
 | 1146 |         status information is returned, rather than its target's. | 
 | 1147 |         """ | 
 | 1148 |         if self._closed: | 
 | 1149 |             self._raise_closed() | 
 | 1150 |         return self._accessor.lstat(self) | 
 | 1151 |  | 
 | 1152 |     def rename(self, target): | 
 | 1153 |         """ | 
 | 1154 |         Rename this path to the given path. | 
 | 1155 |         """ | 
 | 1156 |         if self._closed: | 
 | 1157 |             self._raise_closed() | 
 | 1158 |         self._accessor.rename(self, target) | 
 | 1159 |  | 
 | 1160 |     def replace(self, target): | 
 | 1161 |         """ | 
 | 1162 |         Rename this path to the given path, clobbering the existing | 
 | 1163 |         destination if it exists. | 
 | 1164 |         """ | 
 | 1165 |         if self._closed: | 
 | 1166 |             self._raise_closed() | 
 | 1167 |         self._accessor.replace(self, target) | 
 | 1168 |  | 
 | 1169 |     def symlink_to(self, target, target_is_directory=False): | 
 | 1170 |         """ | 
 | 1171 |         Make this path a symlink pointing to the given path. | 
 | 1172 |         Note the order of arguments (self, target) is the reverse of os.symlink's. | 
 | 1173 |         """ | 
 | 1174 |         if self._closed: | 
 | 1175 |             self._raise_closed() | 
 | 1176 |         self._accessor.symlink(target, self, target_is_directory) | 
 | 1177 |  | 
 | 1178 |     # Convenience functions for querying the stat results | 
 | 1179 |  | 
 | 1180 |     def exists(self): | 
 | 1181 |         """ | 
 | 1182 |         Whether this path exists. | 
 | 1183 |         """ | 
 | 1184 |         try: | 
 | 1185 |             self.stat() | 
 | 1186 |         except OSError as e: | 
 | 1187 |             if e.errno != ENOENT: | 
 | 1188 |                 raise | 
 | 1189 |             return False | 
 | 1190 |         return True | 
 | 1191 |  | 
 | 1192 |     def is_dir(self): | 
 | 1193 |         """ | 
 | 1194 |         Whether this path is a directory. | 
 | 1195 |         """ | 
 | 1196 |         try: | 
 | 1197 |             return S_ISDIR(self.stat().st_mode) | 
 | 1198 |         except OSError as e: | 
 | 1199 |             if e.errno != ENOENT: | 
 | 1200 |                 raise | 
 | 1201 |             # Path doesn't exist or is a broken symlink | 
 | 1202 |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
 | 1203 |             return False | 
 | 1204 |  | 
 | 1205 |     def is_file(self): | 
 | 1206 |         """ | 
 | 1207 |         Whether this path is a regular file (also True for symlinks pointing | 
 | 1208 |         to regular files). | 
 | 1209 |         """ | 
 | 1210 |         try: | 
 | 1211 |             return S_ISREG(self.stat().st_mode) | 
 | 1212 |         except OSError as e: | 
 | 1213 |             if e.errno != ENOENT: | 
 | 1214 |                 raise | 
 | 1215 |             # Path doesn't exist or is a broken symlink | 
 | 1216 |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
 | 1217 |             return False | 
 | 1218 |  | 
 | 1219 |     def is_symlink(self): | 
 | 1220 |         """ | 
 | 1221 |         Whether this path is a symbolic link. | 
 | 1222 |         """ | 
 | 1223 |         try: | 
 | 1224 |             return S_ISLNK(self.lstat().st_mode) | 
 | 1225 |         except OSError as e: | 
 | 1226 |             if e.errno != ENOENT: | 
 | 1227 |                 raise | 
 | 1228 |             # Path doesn't exist | 
 | 1229 |             return False | 
 | 1230 |  | 
 | 1231 |     def is_block_device(self): | 
 | 1232 |         """ | 
 | 1233 |         Whether this path is a block device. | 
 | 1234 |         """ | 
 | 1235 |         try: | 
 | 1236 |             return S_ISBLK(self.stat().st_mode) | 
 | 1237 |         except OSError as e: | 
 | 1238 |             if e.errno != ENOENT: | 
 | 1239 |                 raise | 
 | 1240 |             # Path doesn't exist or is a broken symlink | 
 | 1241 |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
 | 1242 |             return False | 
 | 1243 |  | 
 | 1244 |     def is_char_device(self): | 
 | 1245 |         """ | 
 | 1246 |         Whether this path is a character device. | 
 | 1247 |         """ | 
 | 1248 |         try: | 
 | 1249 |             return S_ISCHR(self.stat().st_mode) | 
 | 1250 |         except OSError as e: | 
 | 1251 |             if e.errno != ENOENT: | 
 | 1252 |                 raise | 
 | 1253 |             # Path doesn't exist or is a broken symlink | 
 | 1254 |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
 | 1255 |             return False | 
 | 1256 |  | 
 | 1257 |     def is_fifo(self): | 
 | 1258 |         """ | 
 | 1259 |         Whether this path is a FIFO. | 
 | 1260 |         """ | 
 | 1261 |         try: | 
 | 1262 |             return S_ISFIFO(self.stat().st_mode) | 
 | 1263 |         except OSError as e: | 
 | 1264 |             if e.errno != ENOENT: | 
 | 1265 |                 raise | 
 | 1266 |             # Path doesn't exist or is a broken symlink | 
 | 1267 |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
 | 1268 |             return False | 
 | 1269 |  | 
 | 1270 |     def is_socket(self): | 
 | 1271 |         """ | 
 | 1272 |         Whether this path is a socket. | 
 | 1273 |         """ | 
 | 1274 |         try: | 
 | 1275 |             return S_ISSOCK(self.stat().st_mode) | 
 | 1276 |         except OSError as e: | 
 | 1277 |             if e.errno != ENOENT: | 
 | 1278 |                 raise | 
 | 1279 |             # Path doesn't exist or is a broken symlink | 
 | 1280 |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
 | 1281 |             return False | 
 | 1282 |  | 
 | 1283 |  | 
 | 1284 | class PosixPath(Path, PurePosixPath): | 
 | 1285 |     __slots__ = () | 
 | 1286 |  | 
 | 1287 | class WindowsPath(Path, PureWindowsPath): | 
 | 1288 |     __slots__ = () |