(root)/
Python-3.12.0/
Lib/
pathlib.py
       1  """Object-oriented filesystem paths.
       2  
       3  This module provides classes to represent abstract paths and concrete
       4  paths with operations that have semantics appropriate for different
       5  operating systems.
       6  """
       7  
       8  import fnmatch
       9  import functools
      10  import io
      11  import ntpath
      12  import os
      13  import posixpath
      14  import re
      15  import sys
      16  import warnings
      17  from _collections_abc import Sequence
      18  from errno import ENOENT, ENOTDIR, EBADF, ELOOP
      19  from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
      20  from urllib.parse import quote_from_bytes as urlquote_from_bytes
      21  
      22  
      23  __all__ = [
      24      "PurePath", "PurePosixPath", "PureWindowsPath",
      25      "Path", "PosixPath", "WindowsPath",
      26      ]
      27  
      28  #
      29  # Internals
      30  #
      31  
      32  # Reference for Windows paths can be found at
      33  # https://learn.microsoft.com/en-gb/windows/win32/fileio/naming-a-file .
      34  _WIN_RESERVED_NAMES = frozenset(
      35      {'CON', 'PRN', 'AUX', 'NUL', 'CONIN$', 'CONOUT$'} |
      36      {f'COM{c}' for c in '123456789\xb9\xb2\xb3'} |
      37      {f'LPT{c}' for c in '123456789\xb9\xb2\xb3'}
      38  )
      39  
      40  _WINERROR_NOT_READY = 21  # drive exists but is not accessible
      41  _WINERROR_INVALID_NAME = 123  # fix for bpo-35306
      42  _WINERROR_CANT_RESOLVE_FILENAME = 1921  # broken symlink pointing to itself
      43  
      44  # EBADF - guard against macOS `stat` throwing EBADF
      45  _IGNORED_ERRNOS = (ENOENT, ENOTDIR, EBADF, ELOOP)
      46  
      47  _IGNORED_WINERRORS = (
      48      _WINERROR_NOT_READY,
      49      _WINERROR_INVALID_NAME,
      50      _WINERROR_CANT_RESOLVE_FILENAME)
      51  
      52  def _ignore_error(exception):
      53      return (getattr(exception, 'errno', None) in _IGNORED_ERRNOS or
      54              getattr(exception, 'winerror', None) in _IGNORED_WINERRORS)
      55  
      56  
      57  @functools.cache
      58  def _is_case_sensitive(flavour):
      59      return flavour.normcase('Aa') == 'Aa'
      60  
      61  #
      62  # Globbing helpers
      63  #
      64  
      65  
      66  # fnmatch.translate() returns a regular expression that includes a prefix and
      67  # a suffix, which enable matching newlines and ensure the end of the string is
      68  # matched, respectively. These features are undesirable for our implementation
      69  # of PurePatch.match(), which represents path separators as newlines and joins
      70  # pattern segments together. As a workaround, we define a slice object that
      71  # can remove the prefix and suffix from any translate() result. See the
      72  # _compile_pattern_lines() function for more details.
      73  _FNMATCH_PREFIX, _FNMATCH_SUFFIX = fnmatch.translate('_').split('_')
      74  _FNMATCH_SLICE = slice(len(_FNMATCH_PREFIX), -len(_FNMATCH_SUFFIX))
      75  _SWAP_SEP_AND_NEWLINE = {
      76      '/': str.maketrans({'/': '\n', '\n': '/'}),
      77      '\\': str.maketrans({'\\': '\n', '\n': '\\'}),
      78  }
      79  
      80  
      81  @functools.lru_cache()
      82  def _make_selector(pattern_parts, flavour, case_sensitive):
      83      pat = pattern_parts[0]
      84      if not pat:
      85          return _TerminatingSelector()
      86      if pat == '**':
      87          child_parts_idx = 1
      88          while child_parts_idx < len(pattern_parts) and pattern_parts[child_parts_idx] == '**':
      89              child_parts_idx += 1
      90          child_parts = pattern_parts[child_parts_idx:]
      91          if '**' in child_parts:
      92              cls = _DoubleRecursiveWildcardSelector
      93          else:
      94              cls = _RecursiveWildcardSelector
      95      else:
      96          child_parts = pattern_parts[1:]
      97          if pat == '..':
      98              cls = _ParentSelector
      99          elif '**' in pat:
     100              raise ValueError("Invalid pattern: '**' can only be an entire path component")
     101          else:
     102              cls = _WildcardSelector
     103      return cls(pat, child_parts, flavour, case_sensitive)
     104  
     105  
     106  @functools.lru_cache(maxsize=256)
     107  def _compile_pattern(pat, case_sensitive):
     108      flags = re.NOFLAG if case_sensitive else re.IGNORECASE
     109      return re.compile(fnmatch.translate(pat), flags).match
     110  
     111  
     112  @functools.lru_cache()
     113  def _compile_pattern_lines(pattern_lines, case_sensitive):
     114      """Compile the given pattern lines to an `re.Pattern` object.
     115  
     116      The *pattern_lines* argument is a glob-style pattern (e.g. '*/*.py') with
     117      its path separators and newlines swapped (e.g. '*\n*.py`). By using
     118      newlines to separate path components, and not setting `re.DOTALL`, we
     119      ensure that the `*` wildcard cannot match path separators.
     120  
     121      The returned `re.Pattern` object may have its `match()` method called to
     122      match a complete pattern, or `search()` to match from the right. The
     123      argument supplied to these methods must also have its path separators and
     124      newlines swapped.
     125      """
     126  
     127      # Match the start of the path, or just after a path separator
     128      parts = ['^']
     129      for part in pattern_lines.splitlines(keepends=True):
     130          if part == '*\n':
     131              part = r'.+\n'
     132          elif part == '*':
     133              part = r'.+'
     134          else:
     135              # Any other component: pass to fnmatch.translate(). We slice off
     136              # the common prefix and suffix added by translate() to ensure that
     137              # re.DOTALL is not set, and the end of the string not matched,
     138              # respectively. With DOTALL not set, '*' wildcards will not match
     139              # path separators, because the '.' characters in the pattern will
     140              # not match newlines.
     141              part = fnmatch.translate(part)[_FNMATCH_SLICE]
     142          parts.append(part)
     143      # Match the end of the path, always.
     144      parts.append(r'\Z')
     145      flags = re.MULTILINE
     146      if not case_sensitive:
     147          flags |= re.IGNORECASE
     148      return re.compile(''.join(parts), flags=flags)
     149  
     150  
     151  class ESC[4;38;5;81m_Selector:
     152      """A selector matches a specific glob pattern part against the children
     153      of a given path."""
     154  
     155      def __init__(self, child_parts, flavour, case_sensitive):
     156          self.child_parts = child_parts
     157          if child_parts:
     158              self.successor = _make_selector(child_parts, flavour, case_sensitive)
     159              self.dironly = True
     160          else:
     161              self.successor = _TerminatingSelector()
     162              self.dironly = False
     163  
     164      def select_from(self, parent_path):
     165          """Iterate over all child paths of `parent_path` matched by this
     166          selector.  This can contain parent_path itself."""
     167          path_cls = type(parent_path)
     168          scandir = path_cls._scandir
     169          if not parent_path.is_dir():
     170              return iter([])
     171          return self._select_from(parent_path, scandir)
     172  
     173  
     174  class ESC[4;38;5;81m_TerminatingSelector:
     175  
     176      def _select_from(self, parent_path, scandir):
     177          yield parent_path
     178  
     179  
     180  class ESC[4;38;5;81m_ParentSelector(ESC[4;38;5;149m_Selector):
     181  
     182      def __init__(self, name, child_parts, flavour, case_sensitive):
     183          _Selector.__init__(self, child_parts, flavour, case_sensitive)
     184  
     185      def _select_from(self,  parent_path, scandir):
     186          path = parent_path._make_child_relpath('..')
     187          for p in self.successor._select_from(path, scandir):
     188              yield p
     189  
     190  
     191  class ESC[4;38;5;81m_WildcardSelector(ESC[4;38;5;149m_Selector):
     192  
     193      def __init__(self, pat, child_parts, flavour, case_sensitive):
     194          _Selector.__init__(self, child_parts, flavour, case_sensitive)
     195          if case_sensitive is None:
     196              # TODO: evaluate case-sensitivity of each directory in _select_from()
     197              case_sensitive = _is_case_sensitive(flavour)
     198          self.match = _compile_pattern(pat, case_sensitive)
     199  
     200      def _select_from(self, parent_path, scandir):
     201          try:
     202              # We must close the scandir() object before proceeding to
     203              # avoid exhausting file descriptors when globbing deep trees.
     204              with scandir(parent_path) as scandir_it:
     205                  entries = list(scandir_it)
     206          except OSError:
     207              pass
     208          else:
     209              for entry in entries:
     210                  if self.dironly:
     211                      try:
     212                          if not entry.is_dir():
     213                              continue
     214                      except OSError:
     215                          continue
     216                  name = entry.name
     217                  if self.match(name):
     218                      path = parent_path._make_child_relpath(name)
     219                      for p in self.successor._select_from(path, scandir):
     220                          yield p
     221  
     222  
     223  class ESC[4;38;5;81m_RecursiveWildcardSelector(ESC[4;38;5;149m_Selector):
     224  
     225      def __init__(self, pat, child_parts, flavour, case_sensitive):
     226          _Selector.__init__(self, child_parts, flavour, case_sensitive)
     227  
     228      def _iterate_directories(self, parent_path):
     229          yield parent_path
     230          for dirpath, dirnames, _ in parent_path.walk():
     231              for dirname in dirnames:
     232                  yield dirpath._make_child_relpath(dirname)
     233  
     234      def _select_from(self, parent_path, scandir):
     235          successor_select = self.successor._select_from
     236          for starting_point in self._iterate_directories(parent_path):
     237              for p in successor_select(starting_point, scandir):
     238                  yield p
     239  
     240  
     241  class ESC[4;38;5;81m_DoubleRecursiveWildcardSelector(ESC[4;38;5;149m_RecursiveWildcardSelector):
     242      """
     243      Like _RecursiveWildcardSelector, but also de-duplicates results from
     244      successive selectors. This is necessary if the pattern contains
     245      multiple non-adjacent '**' segments.
     246      """
     247  
     248      def _select_from(self, parent_path, scandir):
     249          yielded = set()
     250          try:
     251              for p in super()._select_from(parent_path, scandir):
     252                  if p not in yielded:
     253                      yield p
     254                      yielded.add(p)
     255          finally:
     256              yielded.clear()
     257  
     258  
     259  #
     260  # Public API
     261  #
     262  
     263  class ESC[4;38;5;81m_PathParents(ESC[4;38;5;149mSequence):
     264      """This object provides sequence-like access to the logical ancestors
     265      of a path.  Don't try to construct it yourself."""
     266      __slots__ = ('_path', '_drv', '_root', '_tail')
     267  
     268      def __init__(self, path):
     269          self._path = path
     270          self._drv = path.drive
     271          self._root = path.root
     272          self._tail = path._tail
     273  
     274      def __len__(self):
     275          return len(self._tail)
     276  
     277      def __getitem__(self, idx):
     278          if isinstance(idx, slice):
     279              return tuple(self[i] for i in range(*idx.indices(len(self))))
     280  
     281          if idx >= len(self) or idx < -len(self):
     282              raise IndexError(idx)
     283          if idx < 0:
     284              idx += len(self)
     285          return self._path._from_parsed_parts(self._drv, self._root,
     286                                               self._tail[:-idx - 1])
     287  
     288      def __repr__(self):
     289          return "<{}.parents>".format(type(self._path).__name__)
     290  
     291  
     292  class ESC[4;38;5;81mPurePath(ESC[4;38;5;149mobject):
     293      """Base class for manipulating paths without I/O.
     294  
     295      PurePath represents a filesystem path and offers operations which
     296      don't imply any actual filesystem I/O.  Depending on your system,
     297      instantiating a PurePath will return either a PurePosixPath or a
     298      PureWindowsPath object.  You can also instantiate either of these classes
     299      directly, regardless of your system.
     300      """
     301  
     302      __slots__ = (
     303          # The `_raw_paths` slot stores unnormalized string paths. This is set
     304          # in the `__init__()` method.
     305          '_raw_paths',
     306  
     307          # The `_drv`, `_root` and `_tail_cached` slots store parsed and
     308          # normalized parts of the path. They are set when any of the `drive`,
     309          # `root` or `_tail` properties are accessed for the first time. The
     310          # three-part division corresponds to the result of
     311          # `os.path.splitroot()`, except that the tail is further split on path
     312          # separators (i.e. it is a list of strings), and that the root and
     313          # tail are normalized.
     314          '_drv', '_root', '_tail_cached',
     315  
     316          # The `_str` slot stores the string representation of the path,
     317          # computed from the drive, root and tail when `__str__()` is called
     318          # for the first time. It's used to implement `_str_normcase`
     319          '_str',
     320  
     321          # The `_str_normcase_cached` slot stores the string path with
     322          # normalized case. It is set when the `_str_normcase` property is
     323          # accessed for the first time. It's used to implement `__eq__()`
     324          # `__hash__()`, and `_parts_normcase`
     325          '_str_normcase_cached',
     326  
     327          # The `_parts_normcase_cached` slot stores the case-normalized
     328          # string path after splitting on path separators. It's set when the
     329          # `_parts_normcase` property is accessed for the first time. It's used
     330          # to implement comparison methods like `__lt__()`.
     331          '_parts_normcase_cached',
     332  
     333          # The `_lines_cached` slot stores the string path with path separators
     334          # and newlines swapped. This is used to implement `match()`.
     335          '_lines_cached',
     336  
     337          # The `_hash` slot stores the hash of the case-normalized string
     338          # path. It's set when `__hash__()` is called for the first time.
     339          '_hash',
     340      )
     341      _flavour = os.path
     342  
     343      def __new__(cls, *args, **kwargs):
     344          """Construct a PurePath from one or several strings and or existing
     345          PurePath objects.  The strings and path objects are combined so as
     346          to yield a canonicalized path, which is incorporated into the
     347          new PurePath object.
     348          """
     349          if cls is PurePath:
     350              cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
     351          return object.__new__(cls)
     352  
     353      def __reduce__(self):
     354          # Using the parts tuple helps share interned path parts
     355          # when pickling related paths.
     356          return (self.__class__, self.parts)
     357  
     358      def __init__(self, *args):
     359          paths = []
     360          for arg in args:
     361              if isinstance(arg, PurePath):
     362                  if arg._flavour is ntpath and self._flavour is posixpath:
     363                      # GH-103631: Convert separators for backwards compatibility.
     364                      paths.extend(path.replace('\\', '/') for path in arg._raw_paths)
     365                  else:
     366                      paths.extend(arg._raw_paths)
     367              else:
     368                  try:
     369                      path = os.fspath(arg)
     370                  except TypeError:
     371                      path = arg
     372                  if not isinstance(path, str):
     373                      raise TypeError(
     374                          "argument should be a str or an os.PathLike "
     375                          "object where __fspath__ returns a str, "
     376                          f"not {type(path).__name__!r}")
     377                  paths.append(path)
     378          self._raw_paths = paths
     379  
     380      def with_segments(self, *pathsegments):
     381          """Construct a new path object from any number of path-like objects.
     382          Subclasses may override this method to customize how new path objects
     383          are created from methods like `iterdir()`.
     384          """
     385          return type(self)(*pathsegments)
     386  
     387      @classmethod
     388      def _parse_path(cls, path):
     389          if not path:
     390              return '', '', []
     391          sep = cls._flavour.sep
     392          altsep = cls._flavour.altsep
     393          if altsep:
     394              path = path.replace(altsep, sep)
     395          drv, root, rel = cls._flavour.splitroot(path)
     396          if not root and drv.startswith(sep) and not drv.endswith(sep):
     397              drv_parts = drv.split(sep)
     398              if len(drv_parts) == 4 and drv_parts[2] not in '?.':
     399                  # e.g. //server/share
     400                  root = sep
     401              elif len(drv_parts) == 6:
     402                  # e.g. //?/unc/server/share
     403                  root = sep
     404          parsed = [sys.intern(str(x)) for x in rel.split(sep) if x and x != '.']
     405          return drv, root, parsed
     406  
     407      def _load_parts(self):
     408          paths = self._raw_paths
     409          if len(paths) == 0:
     410              path = ''
     411          elif len(paths) == 1:
     412              path = paths[0]
     413          else:
     414              path = self._flavour.join(*paths)
     415          drv, root, tail = self._parse_path(path)
     416          self._drv = drv
     417          self._root = root
     418          self._tail_cached = tail
     419  
     420      def _from_parsed_parts(self, drv, root, tail):
     421          path_str = self._format_parsed_parts(drv, root, tail)
     422          path = self.with_segments(path_str)
     423          path._str = path_str or '.'
     424          path._drv = drv
     425          path._root = root
     426          path._tail_cached = tail
     427          return path
     428  
     429      @classmethod
     430      def _format_parsed_parts(cls, drv, root, tail):
     431          if drv or root:
     432              return drv + root + cls._flavour.sep.join(tail)
     433          elif tail and cls._flavour.splitdrive(tail[0])[0]:
     434              tail = ['.'] + tail
     435          return cls._flavour.sep.join(tail)
     436  
     437      def __str__(self):
     438          """Return the string representation of the path, suitable for
     439          passing to system calls."""
     440          try:
     441              return self._str
     442          except AttributeError:
     443              self._str = self._format_parsed_parts(self.drive, self.root,
     444                                                    self._tail) or '.'
     445              return self._str
     446  
     447      def __fspath__(self):
     448          return str(self)
     449  
     450      def as_posix(self):
     451          """Return the string representation of the path with forward (/)
     452          slashes."""
     453          f = self._flavour
     454          return str(self).replace(f.sep, '/')
     455  
     456      def __bytes__(self):
     457          """Return the bytes representation of the path.  This is only
     458          recommended to use under Unix."""
     459          return os.fsencode(self)
     460  
     461      def __repr__(self):
     462          return "{}({!r})".format(self.__class__.__name__, self.as_posix())
     463  
     464      def as_uri(self):
     465          """Return the path as a 'file' URI."""
     466          if not self.is_absolute():
     467              raise ValueError("relative path can't be expressed as a file URI")
     468  
     469          drive = self.drive
     470          if len(drive) == 2 and drive[1] == ':':
     471              # It's a path on a local drive => 'file:///c:/a/b'
     472              prefix = 'file:///' + drive
     473              path = self.as_posix()[2:]
     474          elif drive:
     475              # It's a path on a network drive => 'file://host/share/a/b'
     476              prefix = 'file:'
     477              path = self.as_posix()
     478          else:
     479              # It's a posix path => 'file:///etc/hosts'
     480              prefix = 'file://'
     481              path = str(self)
     482          return prefix + urlquote_from_bytes(os.fsencode(path))
     483  
     484      @property
     485      def _str_normcase(self):
     486          # String with normalized case, for hashing and equality checks
     487          try:
     488              return self._str_normcase_cached
     489          except AttributeError:
     490              if _is_case_sensitive(self._flavour):
     491                  self._str_normcase_cached = str(self)
     492              else:
     493                  self._str_normcase_cached = str(self).lower()
     494              return self._str_normcase_cached
     495  
     496      @property
     497      def _parts_normcase(self):
     498          # Cached parts with normalized case, for comparisons.
     499          try:
     500              return self._parts_normcase_cached
     501          except AttributeError:
     502              self._parts_normcase_cached = self._str_normcase.split(self._flavour.sep)
     503              return self._parts_normcase_cached
     504  
     505      @property
     506      def _lines(self):
     507          # Path with separators and newlines swapped, for pattern matching.
     508          try:
     509              return self._lines_cached
     510          except AttributeError:
     511              path_str = str(self)
     512              if path_str == '.':
     513                  self._lines_cached = ''
     514              else:
     515                  trans = _SWAP_SEP_AND_NEWLINE[self._flavour.sep]
     516                  self._lines_cached = path_str.translate(trans)
     517              return self._lines_cached
     518  
     519      def __eq__(self, other):
     520          if not isinstance(other, PurePath):
     521              return NotImplemented
     522          return self._str_normcase == other._str_normcase and self._flavour is other._flavour
     523  
     524      def __hash__(self):
     525          try:
     526              return self._hash
     527          except AttributeError:
     528              self._hash = hash(self._str_normcase)
     529              return self._hash
     530  
     531      def __lt__(self, other):
     532          if not isinstance(other, PurePath) or self._flavour is not other._flavour:
     533              return NotImplemented
     534          return self._parts_normcase < other._parts_normcase
     535  
     536      def __le__(self, other):
     537          if not isinstance(other, PurePath) or self._flavour is not other._flavour:
     538              return NotImplemented
     539          return self._parts_normcase <= other._parts_normcase
     540  
     541      def __gt__(self, other):
     542          if not isinstance(other, PurePath) or self._flavour is not other._flavour:
     543              return NotImplemented
     544          return self._parts_normcase > other._parts_normcase
     545  
     546      def __ge__(self, other):
     547          if not isinstance(other, PurePath) or self._flavour is not other._flavour:
     548              return NotImplemented
     549          return self._parts_normcase >= other._parts_normcase
     550  
     551      @property
     552      def drive(self):
     553          """The drive prefix (letter or UNC path), if any."""
     554          try:
     555              return self._drv
     556          except AttributeError:
     557              self._load_parts()
     558              return self._drv
     559  
     560      @property
     561      def root(self):
     562          """The root of the path, if any."""
     563          try:
     564              return self._root
     565          except AttributeError:
     566              self._load_parts()
     567              return self._root
     568  
     569      @property
     570      def _tail(self):
     571          try:
     572              return self._tail_cached
     573          except AttributeError:
     574              self._load_parts()
     575              return self._tail_cached
     576  
     577      @property
     578      def anchor(self):
     579          """The concatenation of the drive and root, or ''."""
     580          anchor = self.drive + self.root
     581          return anchor
     582  
     583      @property
     584      def name(self):
     585          """The final path component, if any."""
     586          tail = self._tail
     587          if not tail:
     588              return ''
     589          return tail[-1]
     590  
     591      @property
     592      def suffix(self):
     593          """
     594          The final component's last suffix, if any.
     595  
     596          This includes the leading period. For example: '.txt'
     597          """
     598          name = self.name
     599          i = name.rfind('.')
     600          if 0 < i < len(name) - 1:
     601              return name[i:]
     602          else:
     603              return ''
     604  
     605      @property
     606      def suffixes(self):
     607          """
     608          A list of the final component's suffixes, if any.
     609  
     610          These include the leading periods. For example: ['.tar', '.gz']
     611          """
     612          name = self.name
     613          if name.endswith('.'):
     614              return []
     615          name = name.lstrip('.')
     616          return ['.' + suffix for suffix in name.split('.')[1:]]
     617  
     618      @property
     619      def stem(self):
     620          """The final path component, minus its last suffix."""
     621          name = self.name
     622          i = name.rfind('.')
     623          if 0 < i < len(name) - 1:
     624              return name[:i]
     625          else:
     626              return name
     627  
     628      def with_name(self, name):
     629          """Return a new path with the file name changed."""
     630          if not self.name:
     631              raise ValueError("%r has an empty name" % (self,))
     632          f = self._flavour
     633          drv, root, tail = f.splitroot(name)
     634          if drv or root or not tail or f.sep in tail or (f.altsep and f.altsep in tail):
     635              raise ValueError("Invalid name %r" % (name))
     636          return self._from_parsed_parts(self.drive, self.root,
     637                                         self._tail[:-1] + [name])
     638  
     639      def with_stem(self, stem):
     640          """Return a new path with the stem changed."""
     641          return self.with_name(stem + self.suffix)
     642  
     643      def with_suffix(self, suffix):
     644          """Return a new path with the file suffix changed.  If the path
     645          has no suffix, add given suffix.  If the given suffix is an empty
     646          string, remove the suffix from the path.
     647          """
     648          f = self._flavour
     649          if f.sep in suffix or f.altsep and f.altsep in suffix:
     650              raise ValueError("Invalid suffix %r" % (suffix,))
     651          if suffix and not suffix.startswith('.') or suffix == '.':
     652              raise ValueError("Invalid suffix %r" % (suffix))
     653          name = self.name
     654          if not name:
     655              raise ValueError("%r has an empty name" % (self,))
     656          old_suffix = self.suffix
     657          if not old_suffix:
     658              name = name + suffix
     659          else:
     660              name = name[:-len(old_suffix)] + suffix
     661          return self._from_parsed_parts(self.drive, self.root,
     662                                         self._tail[:-1] + [name])
     663  
     664      def relative_to(self, other, /, *_deprecated, walk_up=False):
     665          """Return the relative path to another path identified by the passed
     666          arguments.  If the operation is not possible (because this is not
     667          related to the other path), raise ValueError.
     668  
     669          The *walk_up* parameter controls whether `..` may be used to resolve
     670          the path.
     671          """
     672          if _deprecated:
     673              msg = ("support for supplying more than one positional argument "
     674                     "to pathlib.PurePath.relative_to() is deprecated and "
     675                     "scheduled for removal in Python {remove}")
     676              warnings._deprecated("pathlib.PurePath.relative_to(*args)", msg,
     677                                   remove=(3, 14))
     678          other = self.with_segments(other, *_deprecated)
     679          for step, path in enumerate([other] + list(other.parents)):
     680              if self.is_relative_to(path):
     681                  break
     682              elif not walk_up:
     683                  raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}")
     684              elif path.name == '..':
     685                  raise ValueError(f"'..' segment in {str(other)!r} cannot be walked")
     686          else:
     687              raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
     688          parts = ['..'] * step + self._tail[len(path._tail):]
     689          return self.with_segments(*parts)
     690  
     691      def is_relative_to(self, other, /, *_deprecated):
     692          """Return True if the path is relative to another path or False.
     693          """
     694          if _deprecated:
     695              msg = ("support for supplying more than one argument to "
     696                     "pathlib.PurePath.is_relative_to() is deprecated and "
     697                     "scheduled for removal in Python {remove}")
     698              warnings._deprecated("pathlib.PurePath.is_relative_to(*args)",
     699                                   msg, remove=(3, 14))
     700          other = self.with_segments(other, *_deprecated)
     701          return other == self or other in self.parents
     702  
     703      @property
     704      def parts(self):
     705          """An object providing sequence-like access to the
     706          components in the filesystem path."""
     707          if self.drive or self.root:
     708              return (self.drive + self.root,) + tuple(self._tail)
     709          else:
     710              return tuple(self._tail)
     711  
     712      def joinpath(self, *pathsegments):
     713          """Combine this path with one or several arguments, and return a
     714          new path representing either a subpath (if all arguments are relative
     715          paths) or a totally different path (if one of the arguments is
     716          anchored).
     717          """
     718          return self.with_segments(self, *pathsegments)
     719  
     720      def __truediv__(self, key):
     721          try:
     722              return self.joinpath(key)
     723          except TypeError:
     724              return NotImplemented
     725  
     726      def __rtruediv__(self, key):
     727          try:
     728              return self.with_segments(key, self)
     729          except TypeError:
     730              return NotImplemented
     731  
     732      @property
     733      def parent(self):
     734          """The logical parent of the path."""
     735          drv = self.drive
     736          root = self.root
     737          tail = self._tail
     738          if not tail:
     739              return self
     740          return self._from_parsed_parts(drv, root, tail[:-1])
     741  
     742      @property
     743      def parents(self):
     744          """A sequence of this path's logical parents."""
     745          # The value of this property should not be cached on the path object,
     746          # as doing so would introduce a reference cycle.
     747          return _PathParents(self)
     748  
     749      def is_absolute(self):
     750          """True if the path is absolute (has both a root and, if applicable,
     751          a drive)."""
     752          if self._flavour is ntpath:
     753              # ntpath.isabs() is defective - see GH-44626.
     754              return bool(self.drive and self.root)
     755          elif self._flavour is posixpath:
     756              # Optimization: work with raw paths on POSIX.
     757              for path in self._raw_paths:
     758                  if path.startswith('/'):
     759                      return True
     760              return False
     761          else:
     762              return self._flavour.isabs(str(self))
     763  
     764      def is_reserved(self):
     765          """Return True if the path contains one of the special names reserved
     766          by the system, if any."""
     767          if self._flavour is posixpath or not self._tail:
     768              return False
     769  
     770          # NOTE: the rules for reserved names seem somewhat complicated
     771          # (e.g. r"..\NUL" is reserved but not r"foo\NUL" if "foo" does not
     772          # exist). We err on the side of caution and return True for paths
     773          # which are not considered reserved by Windows.
     774          if self.drive.startswith('\\\\'):
     775              # UNC paths are never reserved.
     776              return False
     777          name = self._tail[-1].partition('.')[0].partition(':')[0].rstrip(' ')
     778          return name.upper() in _WIN_RESERVED_NAMES
     779  
     780      def match(self, path_pattern, *, case_sensitive=None):
     781          """
     782          Return True if this path matches the given pattern.
     783          """
     784          if not isinstance(path_pattern, PurePath):
     785              path_pattern = self.with_segments(path_pattern)
     786          if case_sensitive is None:
     787              case_sensitive = _is_case_sensitive(self._flavour)
     788          pattern = _compile_pattern_lines(path_pattern._lines, case_sensitive)
     789          if path_pattern.drive or path_pattern.root:
     790              return pattern.match(self._lines) is not None
     791          elif path_pattern._tail:
     792              return pattern.search(self._lines) is not None
     793          else:
     794              raise ValueError("empty pattern")
     795  
     796  
     797  # Can't subclass os.PathLike from PurePath and keep the constructor
     798  # optimizations in PurePath.__slots__.
     799  os.PathLike.register(PurePath)
     800  
     801  
     802  class ESC[4;38;5;81mPurePosixPath(ESC[4;38;5;149mPurePath):
     803      """PurePath subclass for non-Windows systems.
     804  
     805      On a POSIX system, instantiating a PurePath should return this object.
     806      However, you can also instantiate it directly on any system.
     807      """
     808      _flavour = posixpath
     809      __slots__ = ()
     810  
     811  
     812  class ESC[4;38;5;81mPureWindowsPath(ESC[4;38;5;149mPurePath):
     813      """PurePath subclass for Windows systems.
     814  
     815      On a Windows system, instantiating a PurePath should return this object.
     816      However, you can also instantiate it directly on any system.
     817      """
     818      _flavour = ntpath
     819      __slots__ = ()
     820  
     821  
     822  # Filesystem-accessing classes
     823  
     824  
     825  class ESC[4;38;5;81mPath(ESC[4;38;5;149mPurePath):
     826      """PurePath subclass that can make system calls.
     827  
     828      Path represents a filesystem path but unlike PurePath, also offers
     829      methods to do system calls on path objects. Depending on your system,
     830      instantiating a Path will return either a PosixPath or a WindowsPath
     831      object. You can also instantiate a PosixPath or WindowsPath directly,
     832      but cannot instantiate a WindowsPath on a POSIX system or vice versa.
     833      """
     834      __slots__ = ()
     835  
     836      def stat(self, *, follow_symlinks=True):
     837          """
     838          Return the result of the stat() system call on this path, like
     839          os.stat() does.
     840          """
     841          return os.stat(self, follow_symlinks=follow_symlinks)
     842  
     843      def lstat(self):
     844          """
     845          Like stat(), except if the path points to a symlink, the symlink's
     846          status information is returned, rather than its target's.
     847          """
     848          return self.stat(follow_symlinks=False)
     849  
     850  
     851      # Convenience functions for querying the stat results
     852  
     853      def exists(self, *, follow_symlinks=True):
     854          """
     855          Whether this path exists.
     856  
     857          This method normally follows symlinks; to check whether a symlink exists,
     858          add the argument follow_symlinks=False.
     859          """
     860          try:
     861              self.stat(follow_symlinks=follow_symlinks)
     862          except OSError as e:
     863              if not _ignore_error(e):
     864                  raise
     865              return False
     866          except ValueError:
     867              # Non-encodable path
     868              return False
     869          return True
     870  
     871      def is_dir(self):
     872          """
     873          Whether this path is a directory.
     874          """
     875          try:
     876              return S_ISDIR(self.stat().st_mode)
     877          except OSError as e:
     878              if not _ignore_error(e):
     879                  raise
     880              # Path doesn't exist or is a broken symlink
     881              # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
     882              return False
     883          except ValueError:
     884              # Non-encodable path
     885              return False
     886  
     887      def is_file(self):
     888          """
     889          Whether this path is a regular file (also True for symlinks pointing
     890          to regular files).
     891          """
     892          try:
     893              return S_ISREG(self.stat().st_mode)
     894          except OSError as e:
     895              if not _ignore_error(e):
     896                  raise
     897              # Path doesn't exist or is a broken symlink
     898              # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
     899              return False
     900          except ValueError:
     901              # Non-encodable path
     902              return False
     903  
     904      def is_mount(self):
     905          """
     906          Check if this path is a mount point
     907          """
     908          return self._flavour.ismount(self)
     909  
     910      def is_symlink(self):
     911          """
     912          Whether this path is a symbolic link.
     913          """
     914          try:
     915              return S_ISLNK(self.lstat().st_mode)
     916          except OSError as e:
     917              if not _ignore_error(e):
     918                  raise
     919              # Path doesn't exist
     920              return False
     921          except ValueError:
     922              # Non-encodable path
     923              return False
     924  
     925      def is_junction(self):
     926          """
     927          Whether this path is a junction.
     928          """
     929          return self._flavour.isjunction(self)
     930  
     931      def is_block_device(self):
     932          """
     933          Whether this path is a block device.
     934          """
     935          try:
     936              return S_ISBLK(self.stat().st_mode)
     937          except OSError as e:
     938              if not _ignore_error(e):
     939                  raise
     940              # Path doesn't exist or is a broken symlink
     941              # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
     942              return False
     943          except ValueError:
     944              # Non-encodable path
     945              return False
     946  
     947      def is_char_device(self):
     948          """
     949          Whether this path is a character device.
     950          """
     951          try:
     952              return S_ISCHR(self.stat().st_mode)
     953          except OSError as e:
     954              if not _ignore_error(e):
     955                  raise
     956              # Path doesn't exist or is a broken symlink
     957              # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
     958              return False
     959          except ValueError:
     960              # Non-encodable path
     961              return False
     962  
     963      def is_fifo(self):
     964          """
     965          Whether this path is a FIFO.
     966          """
     967          try:
     968              return S_ISFIFO(self.stat().st_mode)
     969          except OSError as e:
     970              if not _ignore_error(e):
     971                  raise
     972              # Path doesn't exist or is a broken symlink
     973              # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
     974              return False
     975          except ValueError:
     976              # Non-encodable path
     977              return False
     978  
     979      def is_socket(self):
     980          """
     981          Whether this path is a socket.
     982          """
     983          try:
     984              return S_ISSOCK(self.stat().st_mode)
     985          except OSError as e:
     986              if not _ignore_error(e):
     987                  raise
     988              # Path doesn't exist or is a broken symlink
     989              # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
     990              return False
     991          except ValueError:
     992              # Non-encodable path
     993              return False
     994  
     995      def samefile(self, other_path):
     996          """Return whether other_path is the same or not as this file
     997          (as returned by os.path.samefile()).
     998          """
     999          st = self.stat()
    1000          try:
    1001              other_st = other_path.stat()
    1002          except AttributeError:
    1003              other_st = self.with_segments(other_path).stat()
    1004          return self._flavour.samestat(st, other_st)
    1005  
    1006      def open(self, mode='r', buffering=-1, encoding=None,
    1007               errors=None, newline=None):
    1008          """
    1009          Open the file pointed by this path and return a file object, as
    1010          the built-in open() function does.
    1011          """
    1012          if "b" not in mode:
    1013              encoding = io.text_encoding(encoding)
    1014          return io.open(self, mode, buffering, encoding, errors, newline)
    1015  
    1016      def read_bytes(self):
    1017          """
    1018          Open the file in bytes mode, read it, and close the file.
    1019          """
    1020          with self.open(mode='rb') as f:
    1021              return f.read()
    1022  
    1023      def read_text(self, encoding=None, errors=None):
    1024          """
    1025          Open the file in text mode, read it, and close the file.
    1026          """
    1027          encoding = io.text_encoding(encoding)
    1028          with self.open(mode='r', encoding=encoding, errors=errors) as f:
    1029              return f.read()
    1030  
    1031      def write_bytes(self, data):
    1032          """
    1033          Open the file in bytes mode, write to it, and close the file.
    1034          """
    1035          # type-check for the buffer interface before truncating the file
    1036          view = memoryview(data)
    1037          with self.open(mode='wb') as f:
    1038              return f.write(view)
    1039  
    1040      def write_text(self, data, encoding=None, errors=None, newline=None):
    1041          """
    1042          Open the file in text mode, write to it, and close the file.
    1043          """
    1044          if not isinstance(data, str):
    1045              raise TypeError('data must be str, not %s' %
    1046                              data.__class__.__name__)
    1047          encoding = io.text_encoding(encoding)
    1048          with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
    1049              return f.write(data)
    1050  
    1051      def iterdir(self):
    1052          """Yield path objects of the directory contents.
    1053  
    1054          The children are yielded in arbitrary order, and the
    1055          special entries '.' and '..' are not included.
    1056          """
    1057          for name in os.listdir(self):
    1058              yield self._make_child_relpath(name)
    1059  
    1060      def _scandir(self):
    1061          # bpo-24132: a future version of pathlib will support subclassing of
    1062          # pathlib.Path to customize how the filesystem is accessed. This
    1063          # includes scandir(), which is used to implement glob().
    1064          return os.scandir(self)
    1065  
    1066      def _make_child_relpath(self, name):
    1067          path_str = str(self)
    1068          tail = self._tail
    1069          if tail:
    1070              path_str = f'{path_str}{self._flavour.sep}{name}'
    1071          elif path_str != '.':
    1072              path_str = f'{path_str}{name}'
    1073          else:
    1074              path_str = name
    1075          path = self.with_segments(path_str)
    1076          path._str = path_str
    1077          path._drv = self.drive
    1078          path._root = self.root
    1079          path._tail_cached = tail + [name]
    1080          return path
    1081  
    1082      def glob(self, pattern, *, case_sensitive=None):
    1083          """Iterate over this subtree and yield all existing files (of any
    1084          kind, including directories) matching the given relative pattern.
    1085          """
    1086          sys.audit("pathlib.Path.glob", self, pattern)
    1087          if not pattern:
    1088              raise ValueError("Unacceptable pattern: {!r}".format(pattern))
    1089          drv, root, pattern_parts = self._parse_path(pattern)
    1090          if drv or root:
    1091              raise NotImplementedError("Non-relative patterns are unsupported")
    1092          if pattern[-1] in (self._flavour.sep, self._flavour.altsep):
    1093              pattern_parts.append('')
    1094          selector = _make_selector(tuple(pattern_parts), self._flavour, case_sensitive)
    1095          for p in selector.select_from(self):
    1096              yield p
    1097  
    1098      def rglob(self, pattern, *, case_sensitive=None):
    1099          """Recursively yield all existing files (of any kind, including
    1100          directories) matching the given relative pattern, anywhere in
    1101          this subtree.
    1102          """
    1103          sys.audit("pathlib.Path.rglob", self, pattern)
    1104          drv, root, pattern_parts = self._parse_path(pattern)
    1105          if drv or root:
    1106              raise NotImplementedError("Non-relative patterns are unsupported")
    1107          if pattern and pattern[-1] in (self._flavour.sep, self._flavour.altsep):
    1108              pattern_parts.append('')
    1109          selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour, case_sensitive)
    1110          for p in selector.select_from(self):
    1111              yield p
    1112  
    1113      def walk(self, top_down=True, on_error=None, follow_symlinks=False):
    1114          """Walk the directory tree from this directory, similar to os.walk()."""
    1115          sys.audit("pathlib.Path.walk", self, on_error, follow_symlinks)
    1116          paths = [self]
    1117  
    1118          while paths:
    1119              path = paths.pop()
    1120              if isinstance(path, tuple):
    1121                  yield path
    1122                  continue
    1123  
    1124              # We may not have read permission for self, in which case we can't
    1125              # get a list of the files the directory contains. os.walk()
    1126              # always suppressed the exception in that instance, rather than
    1127              # blow up for a minor reason when (say) a thousand readable
    1128              # directories are still left to visit. That logic is copied here.
    1129              try:
    1130                  scandir_it = path._scandir()
    1131              except OSError as error:
    1132                  if on_error is not None:
    1133                      on_error(error)
    1134                  continue
    1135  
    1136              with scandir_it:
    1137                  dirnames = []
    1138                  filenames = []
    1139                  for entry in scandir_it:
    1140                      try:
    1141                          is_dir = entry.is_dir(follow_symlinks=follow_symlinks)
    1142                      except OSError:
    1143                          # Carried over from os.path.isdir().
    1144                          is_dir = False
    1145  
    1146                      if is_dir:
    1147                          dirnames.append(entry.name)
    1148                      else:
    1149                          filenames.append(entry.name)
    1150  
    1151              if top_down:
    1152                  yield path, dirnames, filenames
    1153              else:
    1154                  paths.append((path, dirnames, filenames))
    1155  
    1156              paths += [path._make_child_relpath(d) for d in reversed(dirnames)]
    1157  
    1158      def __init__(self, *args, **kwargs):
    1159          if kwargs:
    1160              msg = ("support for supplying keyword arguments to pathlib.PurePath "
    1161                     "is deprecated and scheduled for removal in Python {remove}")
    1162              warnings._deprecated("pathlib.PurePath(**kwargs)", msg, remove=(3, 14))
    1163          super().__init__(*args)
    1164  
    1165      def __new__(cls, *args, **kwargs):
    1166          if cls is Path:
    1167              cls = WindowsPath if os.name == 'nt' else PosixPath
    1168          return object.__new__(cls)
    1169  
    1170      def __enter__(self):
    1171          # In previous versions of pathlib, __exit__() marked this path as
    1172          # closed; subsequent attempts to perform I/O would raise an IOError.
    1173          # This functionality was never documented, and had the effect of
    1174          # making Path objects mutable, contrary to PEP 428.
    1175          # In Python 3.9 __exit__() was made a no-op.
    1176          # In Python 3.11 __enter__() began emitting DeprecationWarning.
    1177          # In Python 3.13 __enter__() and __exit__() should be removed.
    1178          warnings.warn("pathlib.Path.__enter__() is deprecated and scheduled "
    1179                        "for removal in Python 3.13; Path objects as a context "
    1180                        "manager is a no-op",
    1181                        DeprecationWarning, stacklevel=2)
    1182          return self
    1183  
    1184      def __exit__(self, t, v, tb):
    1185          pass
    1186  
    1187      # Public API
    1188  
    1189      @classmethod
    1190      def cwd(cls):
    1191          """Return a new path pointing to the current working directory."""
    1192          # We call 'absolute()' rather than using 'os.getcwd()' directly to
    1193          # enable users to replace the implementation of 'absolute()' in a
    1194          # subclass and benefit from the new behaviour here. This works because
    1195          # os.path.abspath('.') == os.getcwd().
    1196          return cls().absolute()
    1197  
    1198      @classmethod
    1199      def home(cls):
    1200          """Return a new path pointing to the user's home directory (as
    1201          returned by os.path.expanduser('~')).
    1202          """
    1203          return cls("~").expanduser()
    1204  
    1205      def absolute(self):
    1206          """Return an absolute version of this path by prepending the current
    1207          working directory. No normalization or symlink resolution is performed.
    1208  
    1209          Use resolve() to get the canonical path to a file.
    1210          """
    1211          if self.is_absolute():
    1212              return self
    1213          elif self.drive:
    1214              # There is a CWD on each drive-letter drive.
    1215              cwd = self._flavour.abspath(self.drive)
    1216          else:
    1217              cwd = os.getcwd()
    1218              # Fast path for "empty" paths, e.g. Path("."), Path("") or Path().
    1219              # We pass only one argument to with_segments() to avoid the cost
    1220              # of joining, and we exploit the fact that getcwd() returns a
    1221              # fully-normalized string by storing it in _str. This is used to
    1222              # implement Path.cwd().
    1223              if not self.root and not self._tail:
    1224                  result = self.with_segments(cwd)
    1225                  result._str = cwd
    1226                  return result
    1227          return self.with_segments(cwd, self)
    1228  
    1229      def resolve(self, strict=False):
    1230          """
    1231          Make the path absolute, resolving all symlinks on the way and also
    1232          normalizing it.
    1233          """
    1234  
    1235          def check_eloop(e):
    1236              winerror = getattr(e, 'winerror', 0)
    1237              if e.errno == ELOOP or winerror == _WINERROR_CANT_RESOLVE_FILENAME:
    1238                  raise RuntimeError("Symlink loop from %r" % e.filename)
    1239  
    1240          try:
    1241              s = self._flavour.realpath(self, strict=strict)
    1242          except OSError as e:
    1243              check_eloop(e)
    1244              raise
    1245          p = self.with_segments(s)
    1246  
    1247          # In non-strict mode, realpath() doesn't raise on symlink loops.
    1248          # Ensure we get an exception by calling stat()
    1249          if not strict:
    1250              try:
    1251                  p.stat()
    1252              except OSError as e:
    1253                  check_eloop(e)
    1254          return p
    1255  
    1256      def owner(self):
    1257          """
    1258          Return the login name of the file owner.
    1259          """
    1260          try:
    1261              import pwd
    1262              return pwd.getpwuid(self.stat().st_uid).pw_name
    1263          except ImportError:
    1264              raise NotImplementedError("Path.owner() is unsupported on this system")
    1265  
    1266      def group(self):
    1267          """
    1268          Return the group name of the file gid.
    1269          """
    1270  
    1271          try:
    1272              import grp
    1273              return grp.getgrgid(self.stat().st_gid).gr_name
    1274          except ImportError:
    1275              raise NotImplementedError("Path.group() is unsupported on this system")
    1276  
    1277      def readlink(self):
    1278          """
    1279          Return the path to which the symbolic link points.
    1280          """
    1281          if not hasattr(os, "readlink"):
    1282              raise NotImplementedError("os.readlink() not available on this system")
    1283          return self.with_segments(os.readlink(self))
    1284  
    1285      def touch(self, mode=0o666, exist_ok=True):
    1286          """
    1287          Create this file with the given access mode, if it doesn't exist.
    1288          """
    1289  
    1290          if exist_ok:
    1291              # First try to bump modification time
    1292              # Implementation note: GNU touch uses the UTIME_NOW option of
    1293              # the utimensat() / futimens() functions.
    1294              try:
    1295                  os.utime(self, None)
    1296              except OSError:
    1297                  # Avoid exception chaining
    1298                  pass
    1299              else:
    1300                  return
    1301          flags = os.O_CREAT | os.O_WRONLY
    1302          if not exist_ok:
    1303              flags |= os.O_EXCL
    1304          fd = os.open(self, flags, mode)
    1305          os.close(fd)
    1306  
    1307      def mkdir(self, mode=0o777, parents=False, exist_ok=False):
    1308          """
    1309          Create a new directory at this given path.
    1310          """
    1311          try:
    1312              os.mkdir(self, mode)
    1313          except FileNotFoundError:
    1314              if not parents or self.parent == self:
    1315                  raise
    1316              self.parent.mkdir(parents=True, exist_ok=True)
    1317              self.mkdir(mode, parents=False, exist_ok=exist_ok)
    1318          except OSError:
    1319              # Cannot rely on checking for EEXIST, since the operating system
    1320              # could give priority to other errors like EACCES or EROFS
    1321              if not exist_ok or not self.is_dir():
    1322                  raise
    1323  
    1324      def chmod(self, mode, *, follow_symlinks=True):
    1325          """
    1326          Change the permissions of the path, like os.chmod().
    1327          """
    1328          os.chmod(self, mode, follow_symlinks=follow_symlinks)
    1329  
    1330      def lchmod(self, mode):
    1331          """
    1332          Like chmod(), except if the path points to a symlink, the symlink's
    1333          permissions are changed, rather than its target's.
    1334          """
    1335          self.chmod(mode, follow_symlinks=False)
    1336  
    1337      def unlink(self, missing_ok=False):
    1338          """
    1339          Remove this file or link.
    1340          If the path is a directory, use rmdir() instead.
    1341          """
    1342          try:
    1343              os.unlink(self)
    1344          except FileNotFoundError:
    1345              if not missing_ok:
    1346                  raise
    1347  
    1348      def rmdir(self):
    1349          """
    1350          Remove this directory.  The directory must be empty.
    1351          """
    1352          os.rmdir(self)
    1353  
    1354      def rename(self, target):
    1355          """
    1356          Rename this path to the target path.
    1357  
    1358          The target path may be absolute or relative. Relative paths are
    1359          interpreted relative to the current working directory, *not* the
    1360          directory of the Path object.
    1361  
    1362          Returns the new Path instance pointing to the target path.
    1363          """
    1364          os.rename(self, target)
    1365          return self.with_segments(target)
    1366  
    1367      def replace(self, target):
    1368          """
    1369          Rename this path to the target path, overwriting if that path exists.
    1370  
    1371          The target path may be absolute or relative. Relative paths are
    1372          interpreted relative to the current working directory, *not* the
    1373          directory of the Path object.
    1374  
    1375          Returns the new Path instance pointing to the target path.
    1376          """
    1377          os.replace(self, target)
    1378          return self.with_segments(target)
    1379  
    1380      def symlink_to(self, target, target_is_directory=False):
    1381          """
    1382          Make this path a symlink pointing to the target path.
    1383          Note the order of arguments (link, target) is the reverse of os.symlink.
    1384          """
    1385          if not hasattr(os, "symlink"):
    1386              raise NotImplementedError("os.symlink() not available on this system")
    1387          os.symlink(target, self, target_is_directory)
    1388  
    1389      def hardlink_to(self, target):
    1390          """
    1391          Make this path a hard link pointing to the same file as *target*.
    1392  
    1393          Note the order of arguments (self, target) is the reverse of os.link's.
    1394          """
    1395          if not hasattr(os, "link"):
    1396              raise NotImplementedError("os.link() not available on this system")
    1397          os.link(target, self)
    1398  
    1399      def expanduser(self):
    1400          """ Return a new path with expanded ~ and ~user constructs
    1401          (as returned by os.path.expanduser)
    1402          """
    1403          if (not (self.drive or self.root) and
    1404              self._tail and self._tail[0][:1] == '~'):
    1405              homedir = self._flavour.expanduser(self._tail[0])
    1406              if homedir[:1] == "~":
    1407                  raise RuntimeError("Could not determine home directory.")
    1408              drv, root, tail = self._parse_path(homedir)
    1409              return self._from_parsed_parts(drv, root, tail + self._tail[1:])
    1410  
    1411          return self
    1412  
    1413  
    1414  class ESC[4;38;5;81mPosixPath(ESC[4;38;5;149mPath, ESC[4;38;5;149mPurePosixPath):
    1415      """Path subclass for non-Windows systems.
    1416  
    1417      On a POSIX system, instantiating a Path should return this object.
    1418      """
    1419      __slots__ = ()
    1420  
    1421      if os.name == 'nt':
    1422          def __new__(cls, *args, **kwargs):
    1423              raise NotImplementedError(
    1424                  f"cannot instantiate {cls.__name__!r} on your system")
    1425  
    1426  class ESC[4;38;5;81mWindowsPath(ESC[4;38;5;149mPath, ESC[4;38;5;149mPureWindowsPath):
    1427      """Path subclass for Windows systems.
    1428  
    1429      On a Windows system, instantiating a Path should return this object.
    1430      """
    1431      __slots__ = ()
    1432  
    1433      if os.name != 'nt':
    1434          def __new__(cls, *args, **kwargs):
    1435              raise NotImplementedError(
    1436                  f"cannot instantiate {cls.__name__!r} on your system")