Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/pathlib2/__init__.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author | shellac |
---|---|
date | Sat, 02 May 2020 07:14:21 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:26e78fe6e8c4 |
---|---|
1 # Copyright (c) 2014-2017 Matthias C. M. Troffaes | |
2 # Copyright (c) 2012-2014 Antoine Pitrou and contributors | |
3 # Distributed under the terms of the MIT License. | |
4 | |
5 import ctypes | |
6 import fnmatch | |
7 import functools | |
8 import io | |
9 import ntpath | |
10 import os | |
11 import posixpath | |
12 import re | |
13 import six | |
14 import sys | |
15 | |
16 from errno import EINVAL, ENOENT, ENOTDIR, EBADF | |
17 from errno import EEXIST, EPERM, EACCES | |
18 from operator import attrgetter | |
19 from stat import ( | |
20 S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO) | |
21 | |
22 try: | |
23 from collections.abc import Sequence | |
24 except ImportError: | |
25 from collections import Sequence | |
26 | |
27 try: | |
28 from urllib import quote as urlquote_from_bytes | |
29 except ImportError: | |
30 from urllib.parse import quote_from_bytes as urlquote_from_bytes | |
31 | |
32 | |
33 try: | |
34 intern = intern | |
35 except NameError: | |
36 intern = sys.intern | |
37 | |
38 supports_symlinks = True | |
39 if os.name == 'nt': | |
40 import nt | |
41 if sys.getwindowsversion()[:2] >= (6, 0) and sys.version_info >= (3, 2): | |
42 from nt import _getfinalpathname | |
43 else: | |
44 supports_symlinks = False | |
45 _getfinalpathname = None | |
46 else: | |
47 nt = None | |
48 | |
49 try: | |
50 from os import scandir as os_scandir | |
51 except ImportError: | |
52 from scandir import scandir as os_scandir | |
53 | |
54 __all__ = [ | |
55 "PurePath", "PurePosixPath", "PureWindowsPath", | |
56 "Path", "PosixPath", "WindowsPath", | |
57 ] | |
58 | |
59 # | |
60 # Internals | |
61 # | |
62 | |
63 # EBADF - guard agains macOS `stat` throwing EBADF | |
64 _IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF) | |
65 | |
66 _IGNORED_WINERRORS = ( | |
67 21, # ERROR_NOT_READY - drive exists but is not accessible | |
68 ) | |
69 | |
70 | |
71 def _ignore_error(exception): | |
72 return (getattr(exception, 'errno', None) in _IGNORED_ERROS or | |
73 getattr(exception, 'winerror', None) in _IGNORED_WINERRORS) | |
74 | |
75 | |
76 def _py2_fsencode(parts): | |
77 # py2 => minimal unicode support | |
78 assert six.PY2 | |
79 return [part.encode('ascii') if isinstance(part, six.text_type) | |
80 else part for part in parts] | |
81 | |
82 | |
83 def _try_except_fileexistserror(try_func, except_func, else_func=None): | |
84 if sys.version_info >= (3, 3): | |
85 try: | |
86 try_func() | |
87 except FileExistsError as exc: | |
88 except_func(exc) | |
89 else: | |
90 if else_func is not None: | |
91 else_func() | |
92 else: | |
93 try: | |
94 try_func() | |
95 except EnvironmentError as exc: | |
96 if exc.errno != EEXIST: | |
97 raise | |
98 else: | |
99 except_func(exc) | |
100 else: | |
101 if else_func is not None: | |
102 else_func() | |
103 | |
104 | |
105 def _try_except_filenotfounderror(try_func, except_func): | |
106 if sys.version_info >= (3, 3): | |
107 try: | |
108 try_func() | |
109 except FileNotFoundError as exc: | |
110 except_func(exc) | |
111 elif os.name != 'nt': | |
112 try: | |
113 try_func() | |
114 except EnvironmentError as exc: | |
115 if exc.errno != ENOENT: | |
116 raise | |
117 else: | |
118 except_func(exc) | |
119 else: | |
120 try: | |
121 try_func() | |
122 except WindowsError as exc: | |
123 # errno contains winerror | |
124 # 2 = file not found | |
125 # 3 = path not found | |
126 if exc.errno not in (2, 3): | |
127 raise | |
128 else: | |
129 except_func(exc) | |
130 except EnvironmentError as exc: | |
131 if exc.errno != ENOENT: | |
132 raise | |
133 else: | |
134 except_func(exc) | |
135 | |
136 | |
137 def _try_except_permissionerror_iter(try_iter, except_iter): | |
138 if sys.version_info >= (3, 3): | |
139 try: | |
140 for x in try_iter(): | |
141 yield x | |
142 except PermissionError as exc: | |
143 for x in except_iter(exc): | |
144 yield x | |
145 else: | |
146 try: | |
147 for x in try_iter(): | |
148 yield x | |
149 except EnvironmentError as exc: | |
150 if exc.errno not in (EPERM, EACCES): | |
151 raise | |
152 else: | |
153 for x in except_iter(exc): | |
154 yield x | |
155 | |
156 | |
157 def _win32_get_unique_path_id(path): | |
158 # get file information, needed for samefile on older Python versions | |
159 # see http://timgolden.me.uk/python/win32_how_do_i/ | |
160 # see_if_two_files_are_the_same_file.html | |
161 from ctypes import POINTER, Structure, WinError | |
162 from ctypes.wintypes import DWORD, HANDLE, BOOL | |
163 | |
164 class FILETIME(Structure): | |
165 _fields_ = [("datetime_lo", DWORD), | |
166 ("datetime_hi", DWORD), | |
167 ] | |
168 | |
169 class BY_HANDLE_FILE_INFORMATION(Structure): | |
170 _fields_ = [("attributes", DWORD), | |
171 ("created_at", FILETIME), | |
172 ("accessed_at", FILETIME), | |
173 ("written_at", FILETIME), | |
174 ("volume", DWORD), | |
175 ("file_hi", DWORD), | |
176 ("file_lo", DWORD), | |
177 ("n_links", DWORD), | |
178 ("index_hi", DWORD), | |
179 ("index_lo", DWORD), | |
180 ] | |
181 | |
182 CreateFile = ctypes.windll.kernel32.CreateFileW | |
183 CreateFile.argtypes = [ctypes.c_wchar_p, DWORD, DWORD, ctypes.c_void_p, | |
184 DWORD, DWORD, HANDLE] | |
185 CreateFile.restype = HANDLE | |
186 GetFileInformationByHandle = ( | |
187 ctypes.windll.kernel32.GetFileInformationByHandle) | |
188 GetFileInformationByHandle.argtypes = [ | |
189 HANDLE, POINTER(BY_HANDLE_FILE_INFORMATION)] | |
190 GetFileInformationByHandle.restype = BOOL | |
191 CloseHandle = ctypes.windll.kernel32.CloseHandle | |
192 CloseHandle.argtypes = [HANDLE] | |
193 CloseHandle.restype = BOOL | |
194 GENERIC_READ = 0x80000000 | |
195 FILE_SHARE_READ = 0x00000001 | |
196 FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 | |
197 OPEN_EXISTING = 3 | |
198 if os.path.isdir(path): | |
199 flags = FILE_FLAG_BACKUP_SEMANTICS | |
200 else: | |
201 flags = 0 | |
202 hfile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, | |
203 None, OPEN_EXISTING, flags, None) | |
204 if hfile == 0xffffffff: | |
205 if sys.version_info >= (3, 3): | |
206 raise FileNotFoundError(path) | |
207 else: | |
208 exc = OSError("file not found: path") | |
209 exc.errno = ENOENT | |
210 raise exc | |
211 info = BY_HANDLE_FILE_INFORMATION() | |
212 success = GetFileInformationByHandle(hfile, info) | |
213 CloseHandle(hfile) | |
214 if success == 0: | |
215 raise WinError() | |
216 return info.volume, info.index_hi, info.index_lo | |
217 | |
218 | |
219 def _is_wildcard_pattern(pat): | |
220 # Whether this pattern needs actual matching using fnmatch, or can | |
221 # be looked up directly as a file. | |
222 return "*" in pat or "?" in pat or "[" in pat | |
223 | |
224 | |
225 class _Flavour(object): | |
226 | |
227 """A flavour implements a particular (platform-specific) set of path | |
228 semantics.""" | |
229 | |
230 def __init__(self): | |
231 self.join = self.sep.join | |
232 | |
233 def parse_parts(self, parts): | |
234 if six.PY2: | |
235 parts = _py2_fsencode(parts) | |
236 parsed = [] | |
237 sep = self.sep | |
238 altsep = self.altsep | |
239 drv = root = '' | |
240 it = reversed(parts) | |
241 for part in it: | |
242 if not part: | |
243 continue | |
244 if altsep: | |
245 part = part.replace(altsep, sep) | |
246 drv, root, rel = self.splitroot(part) | |
247 if sep in rel: | |
248 for x in reversed(rel.split(sep)): | |
249 if x and x != '.': | |
250 parsed.append(intern(x)) | |
251 else: | |
252 if rel and rel != '.': | |
253 parsed.append(intern(rel)) | |
254 if drv or root: | |
255 if not drv: | |
256 # If no drive is present, try to find one in the previous | |
257 # parts. This makes the result of parsing e.g. | |
258 # ("C:", "/", "a") reasonably intuitive. | |
259 for part in it: | |
260 if not part: | |
261 continue | |
262 if altsep: | |
263 part = part.replace(altsep, sep) | |
264 drv = self.splitroot(part)[0] | |
265 if drv: | |
266 break | |
267 break | |
268 if drv or root: | |
269 parsed.append(drv + root) | |
270 parsed.reverse() | |
271 return drv, root, parsed | |
272 | |
273 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2): | |
274 """ | |
275 Join the two paths represented by the respective | |
276 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple. | |
277 """ | |
278 if root2: | |
279 if not drv2 and drv: | |
280 return drv, root2, [drv + root2] + parts2[1:] | |
281 elif drv2: | |
282 if drv2 == drv or self.casefold(drv2) == self.casefold(drv): | |
283 # Same drive => second path is relative to the first | |
284 return drv, root, parts + parts2[1:] | |
285 else: | |
286 # Second path is non-anchored (common case) | |
287 return drv, root, parts + parts2 | |
288 return drv2, root2, parts2 | |
289 | |
290 | |
291 class _WindowsFlavour(_Flavour): | |
292 # Reference for Windows paths can be found at | |
293 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx | |
294 | |
295 sep = '\\' | |
296 altsep = '/' | |
297 has_drv = True | |
298 pathmod = ntpath | |
299 | |
300 is_supported = (os.name == 'nt') | |
301 | |
302 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') | |
303 ext_namespace_prefix = '\\\\?\\' | |
304 | |
305 reserved_names = ( | |
306 set(['CON', 'PRN', 'AUX', 'NUL']) | | |
307 set(['COM%d' % i for i in range(1, 10)]) | | |
308 set(['LPT%d' % i for i in range(1, 10)]) | |
309 ) | |
310 | |
311 # Interesting findings about extended paths: | |
312 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported | |
313 # but '\\?\c:/a' is not | |
314 # - extended paths are always absolute; "relative" extended paths will | |
315 # fail. | |
316 | |
317 def splitroot(self, part, sep=sep): | |
318 first = part[0:1] | |
319 second = part[1:2] | |
320 if (second == sep and first == sep): | |
321 # XXX extended paths should also disable the collapsing of "." | |
322 # components (according to MSDN docs). | |
323 prefix, part = self._split_extended_path(part) | |
324 first = part[0:1] | |
325 second = part[1:2] | |
326 else: | |
327 prefix = '' | |
328 third = part[2:3] | |
329 if (second == sep and first == sep and third != sep): | |
330 # is a UNC path: | |
331 # vvvvvvvvvvvvvvvvvvvvv root | |
332 # \\machine\mountpoint\directory\etc\... | |
333 # directory ^^^^^^^^^^^^^^ | |
334 index = part.find(sep, 2) | |
335 if index != -1: | |
336 index2 = part.find(sep, index + 1) | |
337 # a UNC path can't have two slashes in a row | |
338 # (after the initial two) | |
339 if index2 != index + 1: | |
340 if index2 == -1: | |
341 index2 = len(part) | |
342 if prefix: | |
343 return prefix + part[1:index2], sep, part[index2 + 1:] | |
344 else: | |
345 return part[:index2], sep, part[index2 + 1:] | |
346 drv = root = '' | |
347 if second == ':' and first in self.drive_letters: | |
348 drv = part[:2] | |
349 part = part[2:] | |
350 first = third | |
351 if first == sep: | |
352 root = first | |
353 part = part.lstrip(sep) | |
354 return prefix + drv, root, part | |
355 | |
356 def casefold(self, s): | |
357 return s.lower() | |
358 | |
359 def casefold_parts(self, parts): | |
360 return [p.lower() for p in parts] | |
361 | |
362 def resolve(self, path, strict=False): | |
363 s = str(path) | |
364 if not s: | |
365 return os.getcwd() | |
366 previous_s = None | |
367 if _getfinalpathname is not None: | |
368 if strict: | |
369 return self._ext_to_normal(_getfinalpathname(s)) | |
370 else: | |
371 # End of the path after the first one not found | |
372 tail_parts = [] | |
373 | |
374 def _try_func(): | |
375 result[0] = self._ext_to_normal(_getfinalpathname(s)) | |
376 # if there was no exception, set flag to 0 | |
377 result[1] = 0 | |
378 | |
379 def _exc_func(exc): | |
380 pass | |
381 | |
382 while True: | |
383 result = [None, 1] | |
384 _try_except_filenotfounderror(_try_func, _exc_func) | |
385 if result[1] == 1: # file not found exception raised | |
386 previous_s = s | |
387 s, tail = os.path.split(s) | |
388 tail_parts.append(tail) | |
389 if previous_s == s: | |
390 return path | |
391 else: | |
392 s = result[0] | |
393 return os.path.join(s, *reversed(tail_parts)) | |
394 # Means fallback on absolute | |
395 return None | |
396 | |
397 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix): | |
398 prefix = '' | |
399 if s.startswith(ext_prefix): | |
400 prefix = s[:4] | |
401 s = s[4:] | |
402 if s.startswith('UNC\\'): | |
403 prefix += s[:3] | |
404 s = '\\' + s[3:] | |
405 return prefix, s | |
406 | |
407 def _ext_to_normal(self, s): | |
408 # Turn back an extended path into a normal DOS-like path | |
409 return self._split_extended_path(s)[1] | |
410 | |
411 def is_reserved(self, parts): | |
412 # NOTE: the rules for reserved names seem somewhat complicated | |
413 # (e.g. r"..\NUL" is reserved but not r"foo\NUL"). | |
414 # We err on the side of caution and return True for paths which are | |
415 # not considered reserved by Windows. | |
416 if not parts: | |
417 return False | |
418 if parts[0].startswith('\\\\'): | |
419 # UNC paths are never reserved | |
420 return False | |
421 return parts[-1].partition('.')[0].upper() in self.reserved_names | |
422 | |
423 def make_uri(self, path): | |
424 # Under Windows, file URIs use the UTF-8 encoding. | |
425 drive = path.drive | |
426 if len(drive) == 2 and drive[1] == ':': | |
427 # It's a path on a local drive => 'file:///c:/a/b' | |
428 rest = path.as_posix()[2:].lstrip('/') | |
429 return 'file:///%s/%s' % ( | |
430 drive, urlquote_from_bytes(rest.encode('utf-8'))) | |
431 else: | |
432 # It's a path on a network drive => 'file://host/share/a/b' | |
433 return 'file:' + urlquote_from_bytes( | |
434 path.as_posix().encode('utf-8')) | |
435 | |
436 def gethomedir(self, username): | |
437 if 'HOME' in os.environ: | |
438 userhome = os.environ['HOME'] | |
439 elif 'USERPROFILE' in os.environ: | |
440 userhome = os.environ['USERPROFILE'] | |
441 elif 'HOMEPATH' in os.environ: | |
442 try: | |
443 drv = os.environ['HOMEDRIVE'] | |
444 except KeyError: | |
445 drv = '' | |
446 userhome = drv + os.environ['HOMEPATH'] | |
447 else: | |
448 raise RuntimeError("Can't determine home directory") | |
449 | |
450 if username: | |
451 # Try to guess user home directory. By default all users | |
452 # directories are located in the same place and are named by | |
453 # corresponding usernames. If current user home directory points | |
454 # to nonstandard place, this guess is likely wrong. | |
455 if os.environ['USERNAME'] != username: | |
456 drv, root, parts = self.parse_parts((userhome,)) | |
457 if parts[-1] != os.environ['USERNAME']: | |
458 raise RuntimeError("Can't determine home directory " | |
459 "for %r" % username) | |
460 parts[-1] = username | |
461 if drv or root: | |
462 userhome = drv + root + self.join(parts[1:]) | |
463 else: | |
464 userhome = self.join(parts) | |
465 return userhome | |
466 | |
467 | |
468 class _PosixFlavour(_Flavour): | |
469 sep = '/' | |
470 altsep = '' | |
471 has_drv = False | |
472 pathmod = posixpath | |
473 | |
474 is_supported = (os.name != 'nt') | |
475 | |
476 def splitroot(self, part, sep=sep): | |
477 if part and part[0] == sep: | |
478 stripped_part = part.lstrip(sep) | |
479 # According to POSIX path resolution: | |
480 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/ | |
481 # xbd_chap04.html#tag_04_11 | |
482 # "A pathname that begins with two successive slashes may be | |
483 # interpreted in an implementation-defined manner, although more | |
484 # than two leading slashes shall be treated as a single slash". | |
485 if len(part) - len(stripped_part) == 2: | |
486 return '', sep * 2, stripped_part | |
487 else: | |
488 return '', sep, stripped_part | |
489 else: | |
490 return '', '', part | |
491 | |
492 def casefold(self, s): | |
493 return s | |
494 | |
495 def casefold_parts(self, parts): | |
496 return parts | |
497 | |
498 def resolve(self, path, strict=False): | |
499 sep = self.sep | |
500 accessor = path._accessor | |
501 seen = {} | |
502 | |
503 def _resolve(path, rest): | |
504 if rest.startswith(sep): | |
505 path = '' | |
506 | |
507 for name in rest.split(sep): | |
508 if not name or name == '.': | |
509 # current dir | |
510 continue | |
511 if name == '..': | |
512 # parent dir | |
513 path, _, _ = path.rpartition(sep) | |
514 continue | |
515 newpath = path + sep + name | |
516 if newpath in seen: | |
517 # Already seen this path | |
518 path = seen[newpath] | |
519 if path is not None: | |
520 # use cached value | |
521 continue | |
522 # The symlink is not resolved, so we must have a symlink | |
523 # loop. | |
524 raise RuntimeError("Symlink loop from %r" % newpath) | |
525 # Resolve the symbolic link | |
526 try: | |
527 target = accessor.readlink(newpath) | |
528 except OSError as e: | |
529 if e.errno != EINVAL and strict: | |
530 raise | |
531 # Not a symlink, or non-strict mode. We just leave the path | |
532 # untouched. | |
533 path = newpath | |
534 else: | |
535 seen[newpath] = None # not resolved symlink | |
536 path = _resolve(path, target) | |
537 seen[newpath] = path # resolved symlink | |
538 | |
539 return path | |
540 # NOTE: according to POSIX, getcwd() cannot contain path components | |
541 # which are symlinks. | |
542 base = '' if path.is_absolute() else os.getcwd() | |
543 return _resolve(base, str(path)) or sep | |
544 | |
545 def is_reserved(self, parts): | |
546 return False | |
547 | |
548 def make_uri(self, path): | |
549 # We represent the path using the local filesystem encoding, | |
550 # for portability to other applications. | |
551 bpath = bytes(path) | |
552 return 'file://' + urlquote_from_bytes(bpath) | |
553 | |
554 def gethomedir(self, username): | |
555 if not username: | |
556 try: | |
557 return os.environ['HOME'] | |
558 except KeyError: | |
559 import pwd | |
560 return pwd.getpwuid(os.getuid()).pw_dir | |
561 else: | |
562 import pwd | |
563 try: | |
564 return pwd.getpwnam(username).pw_dir | |
565 except KeyError: | |
566 raise RuntimeError("Can't determine home directory " | |
567 "for %r" % username) | |
568 | |
569 | |
570 _windows_flavour = _WindowsFlavour() | |
571 _posix_flavour = _PosixFlavour() | |
572 | |
573 | |
574 class _Accessor: | |
575 | |
576 """An accessor implements a particular (system-specific or not) way of | |
577 accessing paths on the filesystem.""" | |
578 | |
579 | |
580 class _NormalAccessor(_Accessor): | |
581 | |
582 def _wrap_strfunc(strfunc): | |
583 @functools.wraps(strfunc) | |
584 def wrapped(pathobj, *args): | |
585 return strfunc(str(pathobj), *args) | |
586 return staticmethod(wrapped) | |
587 | |
588 def _wrap_binary_strfunc(strfunc): | |
589 @functools.wraps(strfunc) | |
590 def wrapped(pathobjA, pathobjB, *args): | |
591 return strfunc(str(pathobjA), str(pathobjB), *args) | |
592 return staticmethod(wrapped) | |
593 | |
594 stat = _wrap_strfunc(os.stat) | |
595 | |
596 lstat = _wrap_strfunc(os.lstat) | |
597 | |
598 open = _wrap_strfunc(os.open) | |
599 | |
600 listdir = _wrap_strfunc(os.listdir) | |
601 | |
602 scandir = _wrap_strfunc(os_scandir) | |
603 | |
604 chmod = _wrap_strfunc(os.chmod) | |
605 | |
606 if hasattr(os, "lchmod"): | |
607 lchmod = _wrap_strfunc(os.lchmod) | |
608 else: | |
609 def lchmod(self, pathobj, mode): | |
610 raise NotImplementedError("lchmod() not available on this system") | |
611 | |
612 mkdir = _wrap_strfunc(os.mkdir) | |
613 | |
614 unlink = _wrap_strfunc(os.unlink) | |
615 | |
616 rmdir = _wrap_strfunc(os.rmdir) | |
617 | |
618 rename = _wrap_binary_strfunc(os.rename) | |
619 | |
620 if sys.version_info >= (3, 3): | |
621 replace = _wrap_binary_strfunc(os.replace) | |
622 | |
623 if nt: | |
624 if supports_symlinks: | |
625 symlink = _wrap_binary_strfunc(os.symlink) | |
626 else: | |
627 def symlink(a, b, target_is_directory): | |
628 raise NotImplementedError( | |
629 "symlink() not available on this system") | |
630 else: | |
631 # Under POSIX, os.symlink() takes two args | |
632 @staticmethod | |
633 def symlink(a, b, target_is_directory): | |
634 return os.symlink(str(a), str(b)) | |
635 | |
636 utime = _wrap_strfunc(os.utime) | |
637 | |
638 # Helper for resolve() | |
639 def readlink(self, path): | |
640 return os.readlink(path) | |
641 | |
642 | |
643 _normal_accessor = _NormalAccessor() | |
644 | |
645 | |
646 # | |
647 # Globbing helpers | |
648 # | |
649 | |
650 def _make_selector(pattern_parts): | |
651 pat = pattern_parts[0] | |
652 child_parts = pattern_parts[1:] | |
653 if pat == '**': | |
654 cls = _RecursiveWildcardSelector | |
655 elif '**' in pat: | |
656 raise ValueError( | |
657 "Invalid pattern: '**' can only be an entire path component") | |
658 elif _is_wildcard_pattern(pat): | |
659 cls = _WildcardSelector | |
660 else: | |
661 cls = _PreciseSelector | |
662 return cls(pat, child_parts) | |
663 | |
664 | |
665 if hasattr(functools, "lru_cache"): | |
666 _make_selector = functools.lru_cache()(_make_selector) | |
667 | |
668 | |
669 class _Selector: | |
670 | |
671 """A selector matches a specific glob pattern part against the children | |
672 of a given path.""" | |
673 | |
674 def __init__(self, child_parts): | |
675 self.child_parts = child_parts | |
676 if child_parts: | |
677 self.successor = _make_selector(child_parts) | |
678 self.dironly = True | |
679 else: | |
680 self.successor = _TerminatingSelector() | |
681 self.dironly = False | |
682 | |
683 def select_from(self, parent_path): | |
684 """Iterate over all child paths of `parent_path` matched by this | |
685 selector. This can contain parent_path itself.""" | |
686 path_cls = type(parent_path) | |
687 is_dir = path_cls.is_dir | |
688 exists = path_cls.exists | |
689 scandir = parent_path._accessor.scandir | |
690 if not is_dir(parent_path): | |
691 return iter([]) | |
692 return self._select_from(parent_path, is_dir, exists, scandir) | |
693 | |
694 | |
695 class _TerminatingSelector: | |
696 | |
697 def _select_from(self, parent_path, is_dir, exists, scandir): | |
698 yield parent_path | |
699 | |
700 | |
701 class _PreciseSelector(_Selector): | |
702 | |
703 def __init__(self, name, child_parts): | |
704 self.name = name | |
705 _Selector.__init__(self, child_parts) | |
706 | |
707 def _select_from(self, parent_path, is_dir, exists, scandir): | |
708 def try_iter(): | |
709 path = parent_path._make_child_relpath(self.name) | |
710 if (is_dir if self.dironly else exists)(path): | |
711 for p in self.successor._select_from( | |
712 path, is_dir, exists, scandir): | |
713 yield p | |
714 | |
715 def except_iter(exc): | |
716 return | |
717 yield | |
718 | |
719 for x in _try_except_permissionerror_iter(try_iter, except_iter): | |
720 yield x | |
721 | |
722 | |
723 class _WildcardSelector(_Selector): | |
724 | |
725 def __init__(self, pat, child_parts): | |
726 self.pat = re.compile(fnmatch.translate(pat)) | |
727 _Selector.__init__(self, child_parts) | |
728 | |
729 def _select_from(self, parent_path, is_dir, exists, scandir): | |
730 def try_iter(): | |
731 cf = parent_path._flavour.casefold | |
732 entries = list(scandir(parent_path)) | |
733 for entry in entries: | |
734 if not self.dironly or entry.is_dir(): | |
735 name = entry.name | |
736 casefolded = cf(name) | |
737 if self.pat.match(casefolded): | |
738 path = parent_path._make_child_relpath(name) | |
739 for p in self.successor._select_from( | |
740 path, is_dir, exists, scandir): | |
741 yield p | |
742 | |
743 def except_iter(exc): | |
744 return | |
745 yield | |
746 | |
747 for x in _try_except_permissionerror_iter(try_iter, except_iter): | |
748 yield x | |
749 | |
750 | |
751 class _RecursiveWildcardSelector(_Selector): | |
752 | |
753 def __init__(self, pat, child_parts): | |
754 _Selector.__init__(self, child_parts) | |
755 | |
756 def _iterate_directories(self, parent_path, is_dir, scandir): | |
757 yield parent_path | |
758 | |
759 def try_iter(): | |
760 entries = list(scandir(parent_path)) | |
761 for entry in entries: | |
762 entry_is_dir = False | |
763 try: | |
764 entry_is_dir = entry.is_dir() | |
765 except OSError as e: | |
766 if not _ignore_error(e): | |
767 raise | |
768 if entry_is_dir and not entry.is_symlink(): | |
769 path = parent_path._make_child_relpath(entry.name) | |
770 for p in self._iterate_directories(path, is_dir, scandir): | |
771 yield p | |
772 | |
773 def except_iter(exc): | |
774 return | |
775 yield | |
776 | |
777 for x in _try_except_permissionerror_iter(try_iter, except_iter): | |
778 yield x | |
779 | |
780 def _select_from(self, parent_path, is_dir, exists, scandir): | |
781 def try_iter(): | |
782 yielded = set() | |
783 try: | |
784 successor_select = self.successor._select_from | |
785 for starting_point in self._iterate_directories( | |
786 parent_path, is_dir, scandir): | |
787 for p in successor_select( | |
788 starting_point, is_dir, exists, scandir): | |
789 if p not in yielded: | |
790 yield p | |
791 yielded.add(p) | |
792 finally: | |
793 yielded.clear() | |
794 | |
795 def except_iter(exc): | |
796 return | |
797 yield | |
798 | |
799 for x in _try_except_permissionerror_iter(try_iter, except_iter): | |
800 yield x | |
801 | |
802 | |
803 # | |
804 # Public API | |
805 # | |
806 | |
807 class _PathParents(Sequence): | |
808 | |
809 """This object provides sequence-like access to the logical ancestors | |
810 of a path. Don't try to construct it yourself.""" | |
811 __slots__ = ('_pathcls', '_drv', '_root', '_parts') | |
812 | |
813 def __init__(self, path): | |
814 # We don't store the instance to avoid reference cycles | |
815 self._pathcls = type(path) | |
816 self._drv = path._drv | |
817 self._root = path._root | |
818 self._parts = path._parts | |
819 | |
820 def __len__(self): | |
821 if self._drv or self._root: | |
822 return len(self._parts) - 1 | |
823 else: | |
824 return len(self._parts) | |
825 | |
826 def __getitem__(self, idx): | |
827 if idx < 0 or idx >= len(self): | |
828 raise IndexError(idx) | |
829 return self._pathcls._from_parsed_parts(self._drv, self._root, | |
830 self._parts[:-idx - 1]) | |
831 | |
832 def __repr__(self): | |
833 return "<{0}.parents>".format(self._pathcls.__name__) | |
834 | |
835 | |
836 class PurePath(object): | |
837 | |
838 """PurePath represents a filesystem path and offers operations which | |
839 don't imply any actual filesystem I/O. Depending on your system, | |
840 instantiating a PurePath will return either a PurePosixPath or a | |
841 PureWindowsPath object. You can also instantiate either of these classes | |
842 directly, regardless of your system. | |
843 """ | |
844 __slots__ = ( | |
845 '_drv', '_root', '_parts', | |
846 '_str', '_hash', '_pparts', '_cached_cparts', | |
847 ) | |
848 | |
849 def __new__(cls, *args): | |
850 """Construct a PurePath from one or several strings and or existing | |
851 PurePath objects. The strings and path objects are combined so as | |
852 to yield a canonicalized path, which is incorporated into the | |
853 new PurePath object. | |
854 """ | |
855 if cls is PurePath: | |
856 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath | |
857 return cls._from_parts(args) | |
858 | |
859 def __reduce__(self): | |
860 # Using the parts tuple helps share interned path parts | |
861 # when pickling related paths. | |
862 return (self.__class__, tuple(self._parts)) | |
863 | |
864 @classmethod | |
865 def _parse_args(cls, args): | |
866 # This is useful when you don't want to create an instance, just | |
867 # canonicalize some constructor arguments. | |
868 parts = [] | |
869 for a in args: | |
870 if isinstance(a, PurePath): | |
871 parts += a._parts | |
872 else: | |
873 if sys.version_info >= (3, 6): | |
874 a = os.fspath(a) | |
875 else: | |
876 # duck typing for older Python versions | |
877 if hasattr(a, "__fspath__"): | |
878 a = a.__fspath__() | |
879 if isinstance(a, str): | |
880 # Force-cast str subclasses to str (issue #21127) | |
881 parts.append(str(a)) | |
882 # also handle unicode for PY2 (six.text_type = unicode) | |
883 elif six.PY2 and isinstance(a, six.text_type): | |
884 # cast to str using filesystem encoding | |
885 # note: in rare circumstances, on Python < 3.2, | |
886 # getfilesystemencoding can return None, in that | |
887 # case fall back to ascii | |
888 parts.append(a.encode( | |
889 sys.getfilesystemencoding() or "ascii")) | |
890 else: | |
891 raise TypeError( | |
892 "argument should be a str object or an os.PathLike " | |
893 "object returning str, not %r" | |
894 % type(a)) | |
895 return cls._flavour.parse_parts(parts) | |
896 | |
897 @classmethod | |
898 def _from_parts(cls, args, init=True): | |
899 # We need to call _parse_args on the instance, so as to get the | |
900 # right flavour. | |
901 self = object.__new__(cls) | |
902 drv, root, parts = self._parse_args(args) | |
903 self._drv = drv | |
904 self._root = root | |
905 self._parts = parts | |
906 if init: | |
907 self._init() | |
908 return self | |
909 | |
910 @classmethod | |
911 def _from_parsed_parts(cls, drv, root, parts, init=True): | |
912 self = object.__new__(cls) | |
913 self._drv = drv | |
914 self._root = root | |
915 self._parts = parts | |
916 if init: | |
917 self._init() | |
918 return self | |
919 | |
920 @classmethod | |
921 def _format_parsed_parts(cls, drv, root, parts): | |
922 if drv or root: | |
923 return drv + root + cls._flavour.join(parts[1:]) | |
924 else: | |
925 return cls._flavour.join(parts) | |
926 | |
927 def _init(self): | |
928 # Overridden in concrete Path | |
929 pass | |
930 | |
931 def _make_child(self, args): | |
932 drv, root, parts = self._parse_args(args) | |
933 drv, root, parts = self._flavour.join_parsed_parts( | |
934 self._drv, self._root, self._parts, drv, root, parts) | |
935 return self._from_parsed_parts(drv, root, parts) | |
936 | |
937 def __str__(self): | |
938 """Return the string representation of the path, suitable for | |
939 passing to system calls.""" | |
940 try: | |
941 return self._str | |
942 except AttributeError: | |
943 self._str = self._format_parsed_parts(self._drv, self._root, | |
944 self._parts) or '.' | |
945 return self._str | |
946 | |
947 def __fspath__(self): | |
948 return str(self) | |
949 | |
950 def as_posix(self): | |
951 """Return the string representation of the path with forward (/) | |
952 slashes.""" | |
953 f = self._flavour | |
954 return str(self).replace(f.sep, '/') | |
955 | |
956 def __bytes__(self): | |
957 """Return the bytes representation of the path. This is only | |
958 recommended to use under Unix.""" | |
959 if sys.version_info < (3, 2): | |
960 raise NotImplementedError("needs Python 3.2 or later") | |
961 return os.fsencode(str(self)) | |
962 | |
963 def __repr__(self): | |
964 return "{0}({1!r})".format(self.__class__.__name__, self.as_posix()) | |
965 | |
966 def as_uri(self): | |
967 """Return the path as a 'file' URI.""" | |
968 if not self.is_absolute(): | |
969 raise ValueError("relative path can't be expressed as a file URI") | |
970 return self._flavour.make_uri(self) | |
971 | |
972 @property | |
973 def _cparts(self): | |
974 # Cached casefolded parts, for hashing and comparison | |
975 try: | |
976 return self._cached_cparts | |
977 except AttributeError: | |
978 self._cached_cparts = self._flavour.casefold_parts(self._parts) | |
979 return self._cached_cparts | |
980 | |
981 def __eq__(self, other): | |
982 if not isinstance(other, PurePath): | |
983 return NotImplemented | |
984 return ( | |
985 self._cparts == other._cparts | |
986 and self._flavour is other._flavour) | |
987 | |
988 def __ne__(self, other): | |
989 return not self == other | |
990 | |
991 def __hash__(self): | |
992 try: | |
993 return self._hash | |
994 except AttributeError: | |
995 self._hash = hash(tuple(self._cparts)) | |
996 return self._hash | |
997 | |
998 def __lt__(self, other): | |
999 if (not isinstance(other, PurePath) | |
1000 or self._flavour is not other._flavour): | |
1001 return NotImplemented | |
1002 return self._cparts < other._cparts | |
1003 | |
1004 def __le__(self, other): | |
1005 if (not isinstance(other, PurePath) | |
1006 or self._flavour is not other._flavour): | |
1007 return NotImplemented | |
1008 return self._cparts <= other._cparts | |
1009 | |
1010 def __gt__(self, other): | |
1011 if (not isinstance(other, PurePath) | |
1012 or self._flavour is not other._flavour): | |
1013 return NotImplemented | |
1014 return self._cparts > other._cparts | |
1015 | |
1016 def __ge__(self, other): | |
1017 if (not isinstance(other, PurePath) | |
1018 or self._flavour is not other._flavour): | |
1019 return NotImplemented | |
1020 return self._cparts >= other._cparts | |
1021 | |
1022 drive = property(attrgetter('_drv'), | |
1023 doc="""The drive prefix (letter or UNC path), if any.""") | |
1024 | |
1025 root = property(attrgetter('_root'), | |
1026 doc="""The root of the path, if any.""") | |
1027 | |
1028 @property | |
1029 def anchor(self): | |
1030 """The concatenation of the drive and root, or ''.""" | |
1031 anchor = self._drv + self._root | |
1032 return anchor | |
1033 | |
1034 @property | |
1035 def name(self): | |
1036 """The final path component, if any.""" | |
1037 parts = self._parts | |
1038 if len(parts) == (1 if (self._drv or self._root) else 0): | |
1039 return '' | |
1040 return parts[-1] | |
1041 | |
1042 @property | |
1043 def suffix(self): | |
1044 """The final component's last suffix, if any.""" | |
1045 name = self.name | |
1046 i = name.rfind('.') | |
1047 if 0 < i < len(name) - 1: | |
1048 return name[i:] | |
1049 else: | |
1050 return '' | |
1051 | |
1052 @property | |
1053 def suffixes(self): | |
1054 """A list of the final component's suffixes, if any.""" | |
1055 name = self.name | |
1056 if name.endswith('.'): | |
1057 return [] | |
1058 name = name.lstrip('.') | |
1059 return ['.' + suffix for suffix in name.split('.')[1:]] | |
1060 | |
1061 @property | |
1062 def stem(self): | |
1063 """The final path component, minus its last suffix.""" | |
1064 name = self.name | |
1065 i = name.rfind('.') | |
1066 if 0 < i < len(name) - 1: | |
1067 return name[:i] | |
1068 else: | |
1069 return name | |
1070 | |
1071 def with_name(self, name): | |
1072 """Return a new path with the file name changed.""" | |
1073 if not self.name: | |
1074 raise ValueError("%r has an empty name" % (self,)) | |
1075 drv, root, parts = self._flavour.parse_parts((name,)) | |
1076 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep] | |
1077 or drv or root or len(parts) != 1): | |
1078 raise ValueError("Invalid name %r" % (name)) | |
1079 return self._from_parsed_parts(self._drv, self._root, | |
1080 self._parts[:-1] + [name]) | |
1081 | |
1082 def with_suffix(self, suffix): | |
1083 """Return a new path with the file suffix changed. If the path | |
1084 has no suffix, add given suffix. If the given suffix is an empty | |
1085 string, remove the suffix from the path. | |
1086 """ | |
1087 # XXX if suffix is None, should the current suffix be removed? | |
1088 f = self._flavour | |
1089 if f.sep in suffix or f.altsep and f.altsep in suffix: | |
1090 raise ValueError("Invalid suffix %r" % (suffix)) | |
1091 if suffix and not suffix.startswith('.') or suffix == '.': | |
1092 raise ValueError("Invalid suffix %r" % (suffix)) | |
1093 name = self.name | |
1094 if not name: | |
1095 raise ValueError("%r has an empty name" % (self,)) | |
1096 old_suffix = self.suffix | |
1097 if not old_suffix: | |
1098 name = name + suffix | |
1099 else: | |
1100 name = name[:-len(old_suffix)] + suffix | |
1101 return self._from_parsed_parts(self._drv, self._root, | |
1102 self._parts[:-1] + [name]) | |
1103 | |
1104 def relative_to(self, *other): | |
1105 """Return the relative path to another path identified by the passed | |
1106 arguments. If the operation is not possible (because this is not | |
1107 a subpath of the other path), raise ValueError. | |
1108 """ | |
1109 # For the purpose of this method, drive and root are considered | |
1110 # separate parts, i.e.: | |
1111 # Path('c:/').relative_to('c:') gives Path('/') | |
1112 # Path('c:/').relative_to('/') raise ValueError | |
1113 if not other: | |
1114 raise TypeError("need at least one argument") | |
1115 parts = self._parts | |
1116 drv = self._drv | |
1117 root = self._root | |
1118 if root: | |
1119 abs_parts = [drv, root] + parts[1:] | |
1120 else: | |
1121 abs_parts = parts | |
1122 to_drv, to_root, to_parts = self._parse_args(other) | |
1123 if to_root: | |
1124 to_abs_parts = [to_drv, to_root] + to_parts[1:] | |
1125 else: | |
1126 to_abs_parts = to_parts | |
1127 n = len(to_abs_parts) | |
1128 cf = self._flavour.casefold_parts | |
1129 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts): | |
1130 formatted = self._format_parsed_parts(to_drv, to_root, to_parts) | |
1131 raise ValueError("{0!r} does not start with {1!r}" | |
1132 .format(str(self), str(formatted))) | |
1133 return self._from_parsed_parts('', root if n == 1 else '', | |
1134 abs_parts[n:]) | |
1135 | |
1136 @property | |
1137 def parts(self): | |
1138 """An object providing sequence-like access to the | |
1139 components in the filesystem path.""" | |
1140 # We cache the tuple to avoid building a new one each time .parts | |
1141 # is accessed. XXX is this necessary? | |
1142 try: | |
1143 return self._pparts | |
1144 except AttributeError: | |
1145 self._pparts = tuple(self._parts) | |
1146 return self._pparts | |
1147 | |
1148 def joinpath(self, *args): | |
1149 """Combine this path with one or several arguments, and return a | |
1150 new path representing either a subpath (if all arguments are relative | |
1151 paths) or a totally different path (if one of the arguments is | |
1152 anchored). | |
1153 """ | |
1154 return self._make_child(args) | |
1155 | |
1156 def __truediv__(self, key): | |
1157 return self._make_child((key,)) | |
1158 | |
1159 def __rtruediv__(self, key): | |
1160 return self._from_parts([key] + self._parts) | |
1161 | |
1162 if six.PY2: | |
1163 __div__ = __truediv__ | |
1164 __rdiv__ = __rtruediv__ | |
1165 | |
1166 @property | |
1167 def parent(self): | |
1168 """The logical parent of the path.""" | |
1169 drv = self._drv | |
1170 root = self._root | |
1171 parts = self._parts | |
1172 if len(parts) == 1 and (drv or root): | |
1173 return self | |
1174 return self._from_parsed_parts(drv, root, parts[:-1]) | |
1175 | |
1176 @property | |
1177 def parents(self): | |
1178 """A sequence of this path's logical parents.""" | |
1179 return _PathParents(self) | |
1180 | |
1181 def is_absolute(self): | |
1182 """True if the path is absolute (has both a root and, if applicable, | |
1183 a drive).""" | |
1184 if not self._root: | |
1185 return False | |
1186 return not self._flavour.has_drv or bool(self._drv) | |
1187 | |
1188 def is_reserved(self): | |
1189 """Return True if the path contains one of the special names reserved | |
1190 by the system, if any.""" | |
1191 return self._flavour.is_reserved(self._parts) | |
1192 | |
1193 def match(self, path_pattern): | |
1194 """ | |
1195 Return True if this path matches the given pattern. | |
1196 """ | |
1197 cf = self._flavour.casefold | |
1198 path_pattern = cf(path_pattern) | |
1199 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,)) | |
1200 if not pat_parts: | |
1201 raise ValueError("empty pattern") | |
1202 if drv and drv != cf(self._drv): | |
1203 return False | |
1204 if root and root != cf(self._root): | |
1205 return False | |
1206 parts = self._cparts | |
1207 if drv or root: | |
1208 if len(pat_parts) != len(parts): | |
1209 return False | |
1210 pat_parts = pat_parts[1:] | |
1211 elif len(pat_parts) > len(parts): | |
1212 return False | |
1213 for part, pat in zip(reversed(parts), reversed(pat_parts)): | |
1214 if not fnmatch.fnmatchcase(part, pat): | |
1215 return False | |
1216 return True | |
1217 | |
1218 | |
1219 # Can't subclass os.PathLike from PurePath and keep the constructor | |
1220 # optimizations in PurePath._parse_args(). | |
1221 if sys.version_info >= (3, 6): | |
1222 os.PathLike.register(PurePath) | |
1223 | |
1224 | |
1225 class PurePosixPath(PurePath): | |
1226 _flavour = _posix_flavour | |
1227 __slots__ = () | |
1228 | |
1229 | |
1230 class PureWindowsPath(PurePath): | |
1231 """PurePath subclass for Windows systems. | |
1232 | |
1233 On a Windows system, instantiating a PurePath should return this object. | |
1234 However, you can also instantiate it directly on any system. | |
1235 """ | |
1236 _flavour = _windows_flavour | |
1237 __slots__ = () | |
1238 | |
1239 | |
1240 # Filesystem-accessing classes | |
1241 | |
1242 | |
1243 class Path(PurePath): | |
1244 """PurePath subclass that can make system calls. | |
1245 | |
1246 Path represents a filesystem path but unlike PurePath, also offers | |
1247 methods to do system calls on path objects. Depending on your system, | |
1248 instantiating a Path will return either a PosixPath or a WindowsPath | |
1249 object. You can also instantiate a PosixPath or WindowsPath directly, | |
1250 but cannot instantiate a WindowsPath on a POSIX system or vice versa. | |
1251 """ | |
1252 __slots__ = ( | |
1253 '_accessor', | |
1254 '_closed', | |
1255 ) | |
1256 | |
1257 def __new__(cls, *args, **kwargs): | |
1258 if cls is Path: | |
1259 cls = WindowsPath if os.name == 'nt' else PosixPath | |
1260 self = cls._from_parts(args, init=False) | |
1261 if not self._flavour.is_supported: | |
1262 raise NotImplementedError("cannot instantiate %r on your system" | |
1263 % (cls.__name__,)) | |
1264 self._init() | |
1265 return self | |
1266 | |
1267 def _init(self, | |
1268 # Private non-constructor arguments | |
1269 template=None, | |
1270 ): | |
1271 self._closed = False | |
1272 if template is not None: | |
1273 self._accessor = template._accessor | |
1274 else: | |
1275 self._accessor = _normal_accessor | |
1276 | |
1277 def _make_child_relpath(self, part): | |
1278 # This is an optimization used for dir walking. `part` must be | |
1279 # a single part relative to this path. | |
1280 parts = self._parts + [part] | |
1281 return self._from_parsed_parts(self._drv, self._root, parts) | |
1282 | |
1283 def __enter__(self): | |
1284 if self._closed: | |
1285 self._raise_closed() | |
1286 return self | |
1287 | |
1288 def __exit__(self, t, v, tb): | |
1289 self._closed = True | |
1290 | |
1291 def _raise_closed(self): | |
1292 raise ValueError("I/O operation on closed path") | |
1293 | |
1294 def _opener(self, name, flags, mode=0o666): | |
1295 # A stub for the opener argument to built-in open() | |
1296 return self._accessor.open(self, flags, mode) | |
1297 | |
1298 def _raw_open(self, flags, mode=0o777): | |
1299 """ | |
1300 Open the file pointed by this path and return a file descriptor, | |
1301 as os.open() does. | |
1302 """ | |
1303 if self._closed: | |
1304 self._raise_closed() | |
1305 return self._accessor.open(self, flags, mode) | |
1306 | |
1307 # Public API | |
1308 | |
1309 @classmethod | |
1310 def cwd(cls): | |
1311 """Return a new path pointing to the current working directory | |
1312 (as returned by os.getcwd()). | |
1313 """ | |
1314 return cls(os.getcwd()) | |
1315 | |
1316 @classmethod | |
1317 def home(cls): | |
1318 """Return a new path pointing to the user's home directory (as | |
1319 returned by os.path.expanduser('~')). | |
1320 """ | |
1321 return cls(cls()._flavour.gethomedir(None)) | |
1322 | |
1323 def samefile(self, other_path): | |
1324 """Return whether other_path is the same or not as this file | |
1325 (as returned by os.path.samefile()). | |
1326 """ | |
1327 if hasattr(os.path, "samestat"): | |
1328 st = self.stat() | |
1329 try: | |
1330 other_st = other_path.stat() | |
1331 except AttributeError: | |
1332 other_st = os.stat(other_path) | |
1333 return os.path.samestat(st, other_st) | |
1334 else: | |
1335 filename1 = six.text_type(self) | |
1336 filename2 = six.text_type(other_path) | |
1337 st1 = _win32_get_unique_path_id(filename1) | |
1338 st2 = _win32_get_unique_path_id(filename2) | |
1339 return st1 == st2 | |
1340 | |
1341 def iterdir(self): | |
1342 """Iterate over the files in this directory. Does not yield any | |
1343 result for the special paths '.' and '..'. | |
1344 """ | |
1345 if self._closed: | |
1346 self._raise_closed() | |
1347 for name in self._accessor.listdir(self): | |
1348 if name in ('.', '..'): | |
1349 # Yielding a path object for these makes little sense | |
1350 continue | |
1351 yield self._make_child_relpath(name) | |
1352 if self._closed: | |
1353 self._raise_closed() | |
1354 | |
1355 def glob(self, pattern): | |
1356 """Iterate over this subtree and yield all existing files (of any | |
1357 kind, including directories) matching the given relative pattern. | |
1358 """ | |
1359 if not pattern: | |
1360 raise ValueError("Unacceptable pattern: {0!r}".format(pattern)) | |
1361 pattern = self._flavour.casefold(pattern) | |
1362 drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) | |
1363 if drv or root: | |
1364 raise NotImplementedError("Non-relative patterns are unsupported") | |
1365 selector = _make_selector(tuple(pattern_parts)) | |
1366 for p in selector.select_from(self): | |
1367 yield p | |
1368 | |
1369 def rglob(self, pattern): | |
1370 """Recursively yield all existing files (of any kind, including | |
1371 directories) matching the given relative pattern, anywhere in | |
1372 this subtree. | |
1373 """ | |
1374 pattern = self._flavour.casefold(pattern) | |
1375 drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) | |
1376 if drv or root: | |
1377 raise NotImplementedError("Non-relative patterns are unsupported") | |
1378 selector = _make_selector(("**",) + tuple(pattern_parts)) | |
1379 for p in selector.select_from(self): | |
1380 yield p | |
1381 | |
1382 def absolute(self): | |
1383 """Return an absolute version of this path. This function works | |
1384 even if the path doesn't point to anything. | |
1385 | |
1386 No normalization is done, i.e. all '.' and '..' will be kept along. | |
1387 Use resolve() to get the canonical path to a file. | |
1388 """ | |
1389 # XXX untested yet! | |
1390 if self._closed: | |
1391 self._raise_closed() | |
1392 if self.is_absolute(): | |
1393 return self | |
1394 # FIXME this must defer to the specific flavour (and, under Windows, | |
1395 # use nt._getfullpathname()) | |
1396 obj = self._from_parts([os.getcwd()] + self._parts, init=False) | |
1397 obj._init(template=self) | |
1398 return obj | |
1399 | |
1400 def resolve(self, strict=False): | |
1401 """ | |
1402 Make the path absolute, resolving all symlinks on the way and also | |
1403 normalizing it (for example turning slashes into backslashes under | |
1404 Windows). | |
1405 """ | |
1406 if self._closed: | |
1407 self._raise_closed() | |
1408 s = self._flavour.resolve(self, strict=strict) | |
1409 if s is None: | |
1410 # No symlink resolution => for consistency, raise an error if | |
1411 # the path is forbidden | |
1412 # but not raise error if file does not exist (see issue #54). | |
1413 | |
1414 def _try_func(): | |
1415 self.stat() | |
1416 | |
1417 def _exc_func(exc): | |
1418 pass | |
1419 | |
1420 _try_except_filenotfounderror(_try_func, _exc_func) | |
1421 s = str(self.absolute()) | |
1422 else: | |
1423 # ensure s is a string (normpath requires this on older python) | |
1424 s = str(s) | |
1425 # Now we have no symlinks in the path, it's safe to normalize it. | |
1426 normed = self._flavour.pathmod.normpath(s) | |
1427 obj = self._from_parts((normed,), init=False) | |
1428 obj._init(template=self) | |
1429 return obj | |
1430 | |
1431 def stat(self): | |
1432 """ | |
1433 Return the result of the stat() system call on this path, like | |
1434 os.stat() does. | |
1435 """ | |
1436 return self._accessor.stat(self) | |
1437 | |
1438 def owner(self): | |
1439 """ | |
1440 Return the login name of the file owner. | |
1441 """ | |
1442 import pwd | |
1443 return pwd.getpwuid(self.stat().st_uid).pw_name | |
1444 | |
1445 def group(self): | |
1446 """ | |
1447 Return the group name of the file gid. | |
1448 """ | |
1449 import grp | |
1450 return grp.getgrgid(self.stat().st_gid).gr_name | |
1451 | |
1452 def open(self, mode='r', buffering=-1, encoding=None, | |
1453 errors=None, newline=None): | |
1454 """ | |
1455 Open the file pointed by this path and return a file object, as | |
1456 the built-in open() function does. | |
1457 """ | |
1458 if self._closed: | |
1459 self._raise_closed() | |
1460 if sys.version_info >= (3, 3): | |
1461 return io.open( | |
1462 str(self), mode, buffering, encoding, errors, newline, | |
1463 opener=self._opener) | |
1464 else: | |
1465 return io.open(str(self), mode, buffering, | |
1466 encoding, errors, newline) | |
1467 | |
1468 def read_bytes(self): | |
1469 """ | |
1470 Open the file in bytes mode, read it, and close the file. | |
1471 """ | |
1472 with self.open(mode='rb') as f: | |
1473 return f.read() | |
1474 | |
1475 def read_text(self, encoding=None, errors=None): | |
1476 """ | |
1477 Open the file in text mode, read it, and close the file. | |
1478 """ | |
1479 with self.open(mode='r', encoding=encoding, errors=errors) as f: | |
1480 return f.read() | |
1481 | |
1482 def write_bytes(self, data): | |
1483 """ | |
1484 Open the file in bytes mode, write to it, and close the file. | |
1485 """ | |
1486 if not isinstance(data, six.binary_type): | |
1487 raise TypeError( | |
1488 'data must be %s, not %s' % | |
1489 (six.binary_type.__name__, data.__class__.__name__)) | |
1490 with self.open(mode='wb') as f: | |
1491 return f.write(data) | |
1492 | |
1493 def write_text(self, data, encoding=None, errors=None): | |
1494 """ | |
1495 Open the file in text mode, write to it, and close the file. | |
1496 """ | |
1497 if not isinstance(data, six.text_type): | |
1498 raise TypeError( | |
1499 'data must be %s, not %s' % | |
1500 (six.text_type.__name__, data.__class__.__name__)) | |
1501 with self.open(mode='w', encoding=encoding, errors=errors) as f: | |
1502 return f.write(data) | |
1503 | |
1504 def touch(self, mode=0o666, exist_ok=True): | |
1505 """ | |
1506 Create this file with the given access mode, if it doesn't exist. | |
1507 """ | |
1508 if self._closed: | |
1509 self._raise_closed() | |
1510 if exist_ok: | |
1511 # First try to bump modification time | |
1512 # Implementation note: GNU touch uses the UTIME_NOW option of | |
1513 # the utimensat() / futimens() functions. | |
1514 try: | |
1515 self._accessor.utime(self, None) | |
1516 except OSError: | |
1517 # Avoid exception chaining | |
1518 pass | |
1519 else: | |
1520 return | |
1521 flags = os.O_CREAT | os.O_WRONLY | |
1522 if not exist_ok: | |
1523 flags |= os.O_EXCL | |
1524 fd = self._raw_open(flags, mode) | |
1525 os.close(fd) | |
1526 | |
1527 def mkdir(self, mode=0o777, parents=False, exist_ok=False): | |
1528 """ | |
1529 Create a new directory at this given path. | |
1530 """ | |
1531 if self._closed: | |
1532 self._raise_closed() | |
1533 | |
1534 def _try_func(): | |
1535 self._accessor.mkdir(self, mode) | |
1536 | |
1537 def _exc_func(exc): | |
1538 if not parents or self.parent == self: | |
1539 raise exc | |
1540 self.parent.mkdir(parents=True, exist_ok=True) | |
1541 self.mkdir(mode, parents=False, exist_ok=exist_ok) | |
1542 | |
1543 try: | |
1544 _try_except_filenotfounderror(_try_func, _exc_func) | |
1545 except OSError: | |
1546 # Cannot rely on checking for EEXIST, since the operating system | |
1547 # could give priority to other errors like EACCES or EROFS | |
1548 if not exist_ok or not self.is_dir(): | |
1549 raise | |
1550 | |
1551 def chmod(self, mode): | |
1552 """ | |
1553 Change the permissions of the path, like os.chmod(). | |
1554 """ | |
1555 if self._closed: | |
1556 self._raise_closed() | |
1557 self._accessor.chmod(self, mode) | |
1558 | |
1559 def lchmod(self, mode): | |
1560 """ | |
1561 Like chmod(), except if the path points to a symlink, the symlink's | |
1562 permissions are changed, rather than its target's. | |
1563 """ | |
1564 if self._closed: | |
1565 self._raise_closed() | |
1566 self._accessor.lchmod(self, mode) | |
1567 | |
1568 def unlink(self): | |
1569 """ | |
1570 Remove this file or link. | |
1571 If the path is a directory, use rmdir() instead. | |
1572 """ | |
1573 if self._closed: | |
1574 self._raise_closed() | |
1575 self._accessor.unlink(self) | |
1576 | |
1577 def rmdir(self): | |
1578 """ | |
1579 Remove this directory. The directory must be empty. | |
1580 """ | |
1581 if self._closed: | |
1582 self._raise_closed() | |
1583 self._accessor.rmdir(self) | |
1584 | |
1585 def lstat(self): | |
1586 """ | |
1587 Like stat(), except if the path points to a symlink, the symlink's | |
1588 status information is returned, rather than its target's. | |
1589 """ | |
1590 if self._closed: | |
1591 self._raise_closed() | |
1592 return self._accessor.lstat(self) | |
1593 | |
1594 def rename(self, target): | |
1595 """ | |
1596 Rename this path to the given path. | |
1597 """ | |
1598 if self._closed: | |
1599 self._raise_closed() | |
1600 self._accessor.rename(self, target) | |
1601 | |
1602 def replace(self, target): | |
1603 """ | |
1604 Rename this path to the given path, clobbering the existing | |
1605 destination if it exists. | |
1606 """ | |
1607 if sys.version_info < (3, 3): | |
1608 raise NotImplementedError("replace() is only available " | |
1609 "with Python 3.3 and later") | |
1610 if self._closed: | |
1611 self._raise_closed() | |
1612 self._accessor.replace(self, target) | |
1613 | |
1614 def symlink_to(self, target, target_is_directory=False): | |
1615 """ | |
1616 Make this path a symlink pointing to the given path. | |
1617 Note the order of arguments (self, target) is the reverse of | |
1618 os.symlink's. | |
1619 """ | |
1620 if self._closed: | |
1621 self._raise_closed() | |
1622 self._accessor.symlink(target, self, target_is_directory) | |
1623 | |
1624 # Convenience functions for querying the stat results | |
1625 | |
1626 def exists(self): | |
1627 """ | |
1628 Whether this path exists. | |
1629 """ | |
1630 try: | |
1631 self.stat() | |
1632 except OSError as e: | |
1633 if not _ignore_error(e): | |
1634 raise | |
1635 return False | |
1636 except ValueError: | |
1637 # Non-encodable path | |
1638 return False | |
1639 return True | |
1640 | |
1641 def is_dir(self): | |
1642 """ | |
1643 Whether this path is a directory. | |
1644 """ | |
1645 try: | |
1646 return S_ISDIR(self.stat().st_mode) | |
1647 except OSError as e: | |
1648 if not _ignore_error(e): | |
1649 raise | |
1650 # Path doesn't exist or is a broken symlink | |
1651 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | |
1652 return False | |
1653 except ValueError: | |
1654 # Non-encodable path | |
1655 return False | |
1656 | |
1657 def is_file(self): | |
1658 """ | |
1659 Whether this path is a regular file (also True for symlinks pointing | |
1660 to regular files). | |
1661 """ | |
1662 try: | |
1663 return S_ISREG(self.stat().st_mode) | |
1664 except OSError as e: | |
1665 if not _ignore_error(e): | |
1666 raise | |
1667 # Path doesn't exist or is a broken symlink | |
1668 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | |
1669 return False | |
1670 except ValueError: | |
1671 # Non-encodable path | |
1672 return False | |
1673 | |
1674 def is_mount(self): | |
1675 """ | |
1676 Check if this path is a POSIX mount point | |
1677 """ | |
1678 # Need to exist and be a dir | |
1679 if not self.exists() or not self.is_dir(): | |
1680 return False | |
1681 | |
1682 parent = Path(self.parent) | |
1683 try: | |
1684 parent_dev = parent.stat().st_dev | |
1685 except OSError: | |
1686 return False | |
1687 | |
1688 dev = self.stat().st_dev | |
1689 if dev != parent_dev: | |
1690 return True | |
1691 ino = self.stat().st_ino | |
1692 parent_ino = parent.stat().st_ino | |
1693 return ino == parent_ino | |
1694 | |
1695 def is_symlink(self): | |
1696 """ | |
1697 Whether this path is a symbolic link. | |
1698 """ | |
1699 try: | |
1700 return S_ISLNK(self.lstat().st_mode) | |
1701 except OSError as e: | |
1702 if not _ignore_error(e): | |
1703 raise | |
1704 # Path doesn't exist | |
1705 return False | |
1706 except ValueError: | |
1707 # Non-encodable path | |
1708 return False | |
1709 | |
1710 def is_block_device(self): | |
1711 """ | |
1712 Whether this path is a block device. | |
1713 """ | |
1714 try: | |
1715 return S_ISBLK(self.stat().st_mode) | |
1716 except OSError as e: | |
1717 if not _ignore_error(e): | |
1718 raise | |
1719 # Path doesn't exist or is a broken symlink | |
1720 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | |
1721 return False | |
1722 except ValueError: | |
1723 # Non-encodable path | |
1724 return False | |
1725 | |
1726 def is_char_device(self): | |
1727 """ | |
1728 Whether this path is a character device. | |
1729 """ | |
1730 try: | |
1731 return S_ISCHR(self.stat().st_mode) | |
1732 except OSError as e: | |
1733 if not _ignore_error(e): | |
1734 raise | |
1735 # Path doesn't exist or is a broken symlink | |
1736 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | |
1737 return False | |
1738 except ValueError: | |
1739 # Non-encodable path | |
1740 return False | |
1741 | |
1742 def is_fifo(self): | |
1743 """ | |
1744 Whether this path is a FIFO. | |
1745 """ | |
1746 try: | |
1747 return S_ISFIFO(self.stat().st_mode) | |
1748 except OSError as e: | |
1749 if not _ignore_error(e): | |
1750 raise | |
1751 # Path doesn't exist or is a broken symlink | |
1752 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | |
1753 return False | |
1754 except ValueError: | |
1755 # Non-encodable path | |
1756 return False | |
1757 | |
1758 def is_socket(self): | |
1759 """ | |
1760 Whether this path is a socket. | |
1761 """ | |
1762 try: | |
1763 return S_ISSOCK(self.stat().st_mode) | |
1764 except OSError as e: | |
1765 if not _ignore_error(e): | |
1766 raise | |
1767 # Path doesn't exist or is a broken symlink | |
1768 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | |
1769 return False | |
1770 except ValueError: | |
1771 # Non-encodable path | |
1772 return False | |
1773 | |
1774 def expanduser(self): | |
1775 """ Return a new path with expanded ~ and ~user constructs | |
1776 (as returned by os.path.expanduser) | |
1777 """ | |
1778 if (not (self._drv or self._root) | |
1779 and self._parts and self._parts[0][:1] == '~'): | |
1780 homedir = self._flavour.gethomedir(self._parts[0][1:]) | |
1781 return self._from_parts([homedir] + self._parts[1:]) | |
1782 | |
1783 return self | |
1784 | |
1785 | |
1786 class PosixPath(Path, PurePosixPath): | |
1787 """Path subclass for non-Windows systems. | |
1788 | |
1789 On a POSIX system, instantiating a Path should return this object. | |
1790 """ | |
1791 __slots__ = () | |
1792 | |
1793 | |
1794 class WindowsPath(Path, PureWindowsPath): | |
1795 """Path subclass for Windows systems. | |
1796 | |
1797 On a Windows system, instantiating a Path should return this object. | |
1798 """ | |
1799 __slots__ = () | |
1800 | |
1801 def owner(self): | |
1802 raise NotImplementedError("Path.owner() is unsupported on this system") | |
1803 | |
1804 def group(self): | |
1805 raise NotImplementedError("Path.group() is unsupported on this system") | |
1806 | |
1807 def is_mount(self): | |
1808 raise NotImplementedError( | |
1809 "Path.is_mount() is unsupported on this system") |