|
4 | 4 |
|
5 | 5 | from errno import * |
6 | 6 | from io import TextIOWrapper |
7 | | -from stat import S_ISDIR, S_ISREG, S_ISLNK, S_IMODE |
8 | 7 | import os |
9 | 8 | import sys |
10 | 9 | try: |
@@ -307,281 +306,3 @@ def ensure_different_files(source, target): |
307 | 306 | err.filename = vfspath(source) |
308 | 307 | err.filename2 = vfspath(target) |
309 | 308 | raise err |
310 | | - |
311 | | - |
312 | | -def copy_info(info, target, follow_symlinks=True): |
313 | | - """Copy metadata from the given PathInfo to the given local path.""" |
314 | | - copy_times_ns = ( |
315 | | - hasattr(info, '_access_time_ns') and |
316 | | - hasattr(info, '_mod_time_ns') and |
317 | | - (follow_symlinks or os.utime in os.supports_follow_symlinks)) |
318 | | - if copy_times_ns: |
319 | | - t0 = info._access_time_ns(follow_symlinks=follow_symlinks) |
320 | | - t1 = info._mod_time_ns(follow_symlinks=follow_symlinks) |
321 | | - os.utime(target, ns=(t0, t1), follow_symlinks=follow_symlinks) |
322 | | - |
323 | | - # We must copy extended attributes before the file is (potentially) |
324 | | - # chmod()'ed read-only, otherwise setxattr() will error with -EACCES. |
325 | | - copy_xattrs = ( |
326 | | - hasattr(info, '_xattrs') and |
327 | | - hasattr(os, 'setxattr') and |
328 | | - (follow_symlinks or os.setxattr in os.supports_follow_symlinks)) |
329 | | - if copy_xattrs: |
330 | | - xattrs = info._xattrs(follow_symlinks=follow_symlinks) |
331 | | - for attr, value in xattrs: |
332 | | - try: |
333 | | - os.setxattr(target, attr, value, follow_symlinks=follow_symlinks) |
334 | | - except OSError as e: |
335 | | - if e.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES): |
336 | | - raise |
337 | | - |
338 | | - copy_posix_permissions = ( |
339 | | - hasattr(info, '_posix_permissions') and |
340 | | - (follow_symlinks or os.chmod in os.supports_follow_symlinks)) |
341 | | - if copy_posix_permissions: |
342 | | - posix_permissions = info._posix_permissions(follow_symlinks=follow_symlinks) |
343 | | - try: |
344 | | - os.chmod(target, posix_permissions, follow_symlinks=follow_symlinks) |
345 | | - except NotImplementedError: |
346 | | - # if we got a NotImplementedError, it's because |
347 | | - # * follow_symlinks=False, |
348 | | - # * lchown() is unavailable, and |
349 | | - # * either |
350 | | - # * fchownat() is unavailable or |
351 | | - # * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW. |
352 | | - # (it returned ENOSUP.) |
353 | | - # therefore we're out of options--we simply cannot chown the |
354 | | - # symlink. give up, suppress the error. |
355 | | - # (which is what shutil always did in this circumstance.) |
356 | | - pass |
357 | | - |
358 | | - copy_bsd_flags = ( |
359 | | - hasattr(info, '_bsd_flags') and |
360 | | - hasattr(os, 'chflags') and |
361 | | - (follow_symlinks or os.chflags in os.supports_follow_symlinks)) |
362 | | - if copy_bsd_flags: |
363 | | - bsd_flags = info._bsd_flags(follow_symlinks=follow_symlinks) |
364 | | - try: |
365 | | - os.chflags(target, bsd_flags, follow_symlinks=follow_symlinks) |
366 | | - except OSError as why: |
367 | | - if why.errno not in (EOPNOTSUPP, ENOTSUP): |
368 | | - raise |
369 | | - |
370 | | - |
371 | | -class _PathInfoBase: |
372 | | - __slots__ = ('_path', '_stat_result', '_lstat_result') |
373 | | - |
374 | | - def __init__(self, path): |
375 | | - self._path = str(path) |
376 | | - |
377 | | - def __repr__(self): |
378 | | - path_type = "WindowsPath" if os.name == "nt" else "PosixPath" |
379 | | - return f"<{path_type}.info>" |
380 | | - |
381 | | - def _stat(self, *, follow_symlinks=True, ignore_errors=False): |
382 | | - """Return the status as an os.stat_result, or None if stat() fails and |
383 | | - ignore_errors is true.""" |
384 | | - if follow_symlinks: |
385 | | - try: |
386 | | - result = self._stat_result |
387 | | - except AttributeError: |
388 | | - pass |
389 | | - else: |
390 | | - if ignore_errors or result is not None: |
391 | | - return result |
392 | | - try: |
393 | | - self._stat_result = os.stat(self._path) |
394 | | - except (OSError, ValueError): |
395 | | - self._stat_result = None |
396 | | - if not ignore_errors: |
397 | | - raise |
398 | | - return self._stat_result |
399 | | - else: |
400 | | - try: |
401 | | - result = self._lstat_result |
402 | | - except AttributeError: |
403 | | - pass |
404 | | - else: |
405 | | - if ignore_errors or result is not None: |
406 | | - return result |
407 | | - try: |
408 | | - self._lstat_result = os.lstat(self._path) |
409 | | - except (OSError, ValueError): |
410 | | - self._lstat_result = None |
411 | | - if not ignore_errors: |
412 | | - raise |
413 | | - return self._lstat_result |
414 | | - |
415 | | - def _posix_permissions(self, *, follow_symlinks=True): |
416 | | - """Return the POSIX file permissions.""" |
417 | | - return S_IMODE(self._stat(follow_symlinks=follow_symlinks).st_mode) |
418 | | - |
419 | | - def _file_id(self, *, follow_symlinks=True): |
420 | | - """Returns the identifier of the file.""" |
421 | | - st = self._stat(follow_symlinks=follow_symlinks) |
422 | | - return st.st_dev, st.st_ino |
423 | | - |
424 | | - def _access_time_ns(self, *, follow_symlinks=True): |
425 | | - """Return the access time in nanoseconds.""" |
426 | | - return self._stat(follow_symlinks=follow_symlinks).st_atime_ns |
427 | | - |
428 | | - def _mod_time_ns(self, *, follow_symlinks=True): |
429 | | - """Return the modify time in nanoseconds.""" |
430 | | - return self._stat(follow_symlinks=follow_symlinks).st_mtime_ns |
431 | | - |
432 | | - if hasattr(os.stat_result, 'st_flags'): |
433 | | - def _bsd_flags(self, *, follow_symlinks=True): |
434 | | - """Return the flags.""" |
435 | | - return self._stat(follow_symlinks=follow_symlinks).st_flags |
436 | | - |
437 | | - if hasattr(os, 'listxattr'): |
438 | | - def _xattrs(self, *, follow_symlinks=True): |
439 | | - """Return the xattrs as a list of (attr, value) pairs, or an empty |
440 | | - list if extended attributes aren't supported.""" |
441 | | - try: |
442 | | - return [ |
443 | | - (attr, os.getxattr(self._path, attr, follow_symlinks=follow_symlinks)) |
444 | | - for attr in os.listxattr(self._path, follow_symlinks=follow_symlinks)] |
445 | | - except OSError as err: |
446 | | - if err.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES): |
447 | | - raise |
448 | | - return [] |
449 | | - |
450 | | - |
451 | | -class _WindowsPathInfo(_PathInfoBase): |
452 | | - """Implementation of pathlib.types.PathInfo that provides status |
453 | | - information for Windows paths. Don't try to construct it yourself.""" |
454 | | - __slots__ = ('_exists', '_is_dir', '_is_file', '_is_symlink') |
455 | | - |
456 | | - def exists(self, *, follow_symlinks=True): |
457 | | - """Whether this path exists.""" |
458 | | - if not follow_symlinks and self.is_symlink(): |
459 | | - return True |
460 | | - try: |
461 | | - return self._exists |
462 | | - except AttributeError: |
463 | | - if os.path.exists(self._path): |
464 | | - self._exists = True |
465 | | - return True |
466 | | - else: |
467 | | - self._exists = self._is_dir = self._is_file = False |
468 | | - return False |
469 | | - |
470 | | - def is_dir(self, *, follow_symlinks=True): |
471 | | - """Whether this path is a directory.""" |
472 | | - if not follow_symlinks and self.is_symlink(): |
473 | | - return False |
474 | | - try: |
475 | | - return self._is_dir |
476 | | - except AttributeError: |
477 | | - if os.path.isdir(self._path): |
478 | | - self._is_dir = self._exists = True |
479 | | - return True |
480 | | - else: |
481 | | - self._is_dir = False |
482 | | - return False |
483 | | - |
484 | | - def is_file(self, *, follow_symlinks=True): |
485 | | - """Whether this path is a regular file.""" |
486 | | - if not follow_symlinks and self.is_symlink(): |
487 | | - return False |
488 | | - try: |
489 | | - return self._is_file |
490 | | - except AttributeError: |
491 | | - if os.path.isfile(self._path): |
492 | | - self._is_file = self._exists = True |
493 | | - return True |
494 | | - else: |
495 | | - self._is_file = False |
496 | | - return False |
497 | | - |
498 | | - def is_symlink(self): |
499 | | - """Whether this path is a symbolic link.""" |
500 | | - try: |
501 | | - return self._is_symlink |
502 | | - except AttributeError: |
503 | | - self._is_symlink = os.path.islink(self._path) |
504 | | - return self._is_symlink |
505 | | - |
506 | | - |
507 | | -class _PosixPathInfo(_PathInfoBase): |
508 | | - """Implementation of pathlib.types.PathInfo that provides status |
509 | | - information for POSIX paths. Don't try to construct it yourself.""" |
510 | | - __slots__ = () |
511 | | - |
512 | | - def exists(self, *, follow_symlinks=True): |
513 | | - """Whether this path exists.""" |
514 | | - st = self._stat(follow_symlinks=follow_symlinks, ignore_errors=True) |
515 | | - if st is None: |
516 | | - return False |
517 | | - return True |
518 | | - |
519 | | - def is_dir(self, *, follow_symlinks=True): |
520 | | - """Whether this path is a directory.""" |
521 | | - st = self._stat(follow_symlinks=follow_symlinks, ignore_errors=True) |
522 | | - if st is None: |
523 | | - return False |
524 | | - return S_ISDIR(st.st_mode) |
525 | | - |
526 | | - def is_file(self, *, follow_symlinks=True): |
527 | | - """Whether this path is a regular file.""" |
528 | | - st = self._stat(follow_symlinks=follow_symlinks, ignore_errors=True) |
529 | | - if st is None: |
530 | | - return False |
531 | | - return S_ISREG(st.st_mode) |
532 | | - |
533 | | - def is_symlink(self): |
534 | | - """Whether this path is a symbolic link.""" |
535 | | - st = self._stat(follow_symlinks=False, ignore_errors=True) |
536 | | - if st is None: |
537 | | - return False |
538 | | - return S_ISLNK(st.st_mode) |
539 | | - |
540 | | - |
541 | | -PathInfo = _WindowsPathInfo if os.name == 'nt' else _PosixPathInfo |
542 | | - |
543 | | - |
544 | | -class DirEntryInfo(_PathInfoBase): |
545 | | - """Implementation of pathlib.types.PathInfo that provides status |
546 | | - information by querying a wrapped os.DirEntry object. Don't try to |
547 | | - construct it yourself.""" |
548 | | - __slots__ = ('_entry',) |
549 | | - |
550 | | - def __init__(self, entry): |
551 | | - super().__init__(entry.path) |
552 | | - self._entry = entry |
553 | | - |
554 | | - def _stat(self, *, follow_symlinks=True, ignore_errors=False): |
555 | | - try: |
556 | | - return self._entry.stat(follow_symlinks=follow_symlinks) |
557 | | - except OSError: |
558 | | - if not ignore_errors: |
559 | | - raise |
560 | | - return None |
561 | | - |
562 | | - def exists(self, *, follow_symlinks=True): |
563 | | - """Whether this path exists.""" |
564 | | - if not follow_symlinks: |
565 | | - return True |
566 | | - return self._stat(ignore_errors=True) is not None |
567 | | - |
568 | | - def is_dir(self, *, follow_symlinks=True): |
569 | | - """Whether this path is a directory.""" |
570 | | - try: |
571 | | - return self._entry.is_dir(follow_symlinks=follow_symlinks) |
572 | | - except OSError: |
573 | | - return False |
574 | | - |
575 | | - def is_file(self, *, follow_symlinks=True): |
576 | | - """Whether this path is a regular file.""" |
577 | | - try: |
578 | | - return self._entry.is_file(follow_symlinks=follow_symlinks) |
579 | | - except OSError: |
580 | | - return False |
581 | | - |
582 | | - def is_symlink(self): |
583 | | - """Whether this path is a symbolic link.""" |
584 | | - try: |
585 | | - return self._entry.is_symlink() |
586 | | - except OSError: |
587 | | - return False |
0 commit comments