Format: text/x-python
⬇ Download File (22.8 KB)
   1  from __future__ import annotations
   2  
   3  import os
   4  import pathlib
   5  import sys
   6  from collections.abc import (
   7      AsyncIterator,
   8      Callable,
   9      Iterable,
  10      Iterator,
  11      Sequence,
  12  )
  13  from dataclasses import dataclass
  14  from functools import partial
  15  from os import PathLike
  16  from typing import (
  17      IO,
  18      TYPE_CHECKING,
  19      Any,
  20      AnyStr,
  21      ClassVar,
  22      Final,
  23      Generic,
  24      overload,
  25  )
  26  
  27  from .. import to_thread
  28  from ..abc import AsyncResource
  29  
  30  if TYPE_CHECKING:
  31      from types import ModuleType
  32  
  33      from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer
  34  else:
  35      ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object
  36  
  37  
  38  class AsyncFile(AsyncResource, Generic[AnyStr]):
  39      """
  40      An asynchronous file object.
  41  
  42      This class wraps a standard file object and provides async friendly versions of the
  43      following blocking methods (where available on the original file object):
  44  
  45      * read
  46      * read1
  47      * readline
  48      * readlines
  49      * readinto
  50      * readinto1
  51      * write
  52      * writelines
  53      * truncate
  54      * seek
  55      * tell
  56      * flush
  57  
  58      All other methods are directly passed through.
  59  
  60      This class supports the asynchronous context manager protocol which closes the
  61      underlying file at the end of the context block.
  62  
  63      This class also supports asynchronous iteration::
  64  
  65          async with await open_file(...) as f:
  66              async for line in f:
  67                  print(line)
  68      """
  69  
  70      def __init__(self, fp: IO[AnyStr]) -> None:
  71          self._fp: Any = fp
  72  
  73      def __getattr__(self, name: str) -> object:
  74          return getattr(self._fp, name)
  75  
  76      @property
  77      def wrapped(self) -> IO[AnyStr]:
  78          """The wrapped file object."""
  79          return self._fp
  80  
  81      async def __aiter__(self) -> AsyncIterator[AnyStr]:
  82          while True:
  83              line = await self.readline()
  84              if line:
  85                  yield line
  86              else:
  87                  break
  88  
  89      async def aclose(self) -> None:
  90          return await to_thread.run_sync(self._fp.close)
  91  
  92      async def read(self, size: int = -1) -> AnyStr:
  93          return await to_thread.run_sync(self._fp.read, size)
  94  
  95      async def read1(self: AsyncFile[bytes], size: int = -1) -> bytes:
  96          return await to_thread.run_sync(self._fp.read1, size)
  97  
  98      async def readline(self) -> AnyStr:
  99          return await to_thread.run_sync(self._fp.readline)
 100  
 101      async def readlines(self) -> list[AnyStr]:
 102          return await to_thread.run_sync(self._fp.readlines)
 103  
 104      async def readinto(self: AsyncFile[bytes], b: WriteableBuffer) -> int:
 105          return await to_thread.run_sync(self._fp.readinto, b)
 106  
 107      async def readinto1(self: AsyncFile[bytes], b: WriteableBuffer) -> int:
 108          return await to_thread.run_sync(self._fp.readinto1, b)
 109  
 110      @overload
 111      async def write(self: AsyncFile[bytes], b: ReadableBuffer) -> int: ...
 112  
 113      @overload
 114      async def write(self: AsyncFile[str], b: str) -> int: ...
 115  
 116      async def write(self, b: ReadableBuffer | str) -> int:
 117          return await to_thread.run_sync(self._fp.write, b)
 118  
 119      @overload
 120      async def writelines(
 121          self: AsyncFile[bytes], lines: Iterable[ReadableBuffer]
 122      ) -> None: ...
 123  
 124      @overload
 125      async def writelines(self: AsyncFile[str], lines: Iterable[str]) -> None: ...
 126  
 127      async def writelines(self, lines: Iterable[ReadableBuffer] | Iterable[str]) -> None:
 128          return await to_thread.run_sync(self._fp.writelines, lines)
 129  
 130      async def truncate(self, size: int | None = None) -> int:
 131          return await to_thread.run_sync(self._fp.truncate, size)
 132  
 133      async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int:
 134          return await to_thread.run_sync(self._fp.seek, offset, whence)
 135  
 136      async def tell(self) -> int:
 137          return await to_thread.run_sync(self._fp.tell)
 138  
 139      async def flush(self) -> None:
 140          return await to_thread.run_sync(self._fp.flush)
 141  
 142  
 143  @overload
 144  async def open_file(
 145      file: str | PathLike[str] | int,
 146      mode: OpenBinaryMode,
 147      buffering: int = ...,
 148      encoding: str | None = ...,
 149      errors: str | None = ...,
 150      newline: str | None = ...,
 151      closefd: bool = ...,
 152      opener: Callable[[str, int], int] | None = ...,
 153  ) -> AsyncFile[bytes]: ...
 154  
 155  
 156  @overload
 157  async def open_file(
 158      file: str | PathLike[str] | int,
 159      mode: OpenTextMode = ...,
 160      buffering: int = ...,
 161      encoding: str | None = ...,
 162      errors: str | None = ...,
 163      newline: str | None = ...,
 164      closefd: bool = ...,
 165      opener: Callable[[str, int], int] | None = ...,
 166  ) -> AsyncFile[str]: ...
 167  
 168  
 169  async def open_file(
 170      file: str | PathLike[str] | int,
 171      mode: str = "r",
 172      buffering: int = -1,
 173      encoding: str | None = None,
 174      errors: str | None = None,
 175      newline: str | None = None,
 176      closefd: bool = True,
 177      opener: Callable[[str, int], int] | None = None,
 178  ) -> AsyncFile[Any]:
 179      """
 180      Open a file asynchronously.
 181  
 182      The arguments are exactly the same as for the builtin :func:`open`.
 183  
 184      :return: an asynchronous file object
 185  
 186      """
 187      fp = await to_thread.run_sync(
 188          open, file, mode, buffering, encoding, errors, newline, closefd, opener
 189      )
 190      return AsyncFile(fp)
 191  
 192  
 193  def wrap_file(file: IO[AnyStr]) -> AsyncFile[AnyStr]:
 194      """
 195      Wrap an existing file as an asynchronous file.
 196  
 197      :param file: an existing file-like object
 198      :return: an asynchronous file object
 199  
 200      """
 201      return AsyncFile(file)
 202  
 203  
 204  @dataclass(eq=False)
 205  class _PathIterator(AsyncIterator["Path"]):
 206      iterator: Iterator[PathLike[str]]
 207  
 208      async def __anext__(self) -> Path:
 209          nextval = await to_thread.run_sync(
 210              next, self.iterator, None, abandon_on_cancel=True
 211          )
 212          if nextval is None:
 213              raise StopAsyncIteration from None
 214  
 215          return Path(nextval)
 216  
 217  
 218  class Path:
 219      """
 220      An asynchronous version of :class:`pathlib.Path`.
 221  
 222      This class cannot be substituted for :class:`pathlib.Path` or
 223      :class:`pathlib.PurePath`, but it is compatible with the :class:`os.PathLike`
 224      interface.
 225  
 226      It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for
 227      the deprecated :meth:`~pathlib.Path.link_to` method.
 228  
 229      Some methods may be unavailable or have limited functionality, based on the Python
 230      version:
 231  
 232      * :meth:`~pathlib.Path.copy` (available on Python 3.14 or later)
 233      * :meth:`~pathlib.Path.copy_into` (available on Python 3.14 or later)
 234      * :meth:`~pathlib.Path.from_uri` (available on Python 3.13 or later)
 235      * :meth:`~pathlib.PurePath.full_match` (available on Python 3.13 or later)
 236      * :attr:`~pathlib.Path.info` (available on Python 3.14 or later)
 237      * :meth:`~pathlib.Path.is_junction` (available on Python 3.12 or later)
 238      * :meth:`~pathlib.PurePath.match` (the ``case_sensitive`` parameter is only
 239        available on Python 3.13 or later)
 240      * :meth:`~pathlib.Path.move` (available on Python 3.14 or later)
 241      * :meth:`~pathlib.Path.move_into` (available on Python 3.14 or later)
 242      * :meth:`~pathlib.PurePath.relative_to` (the ``walk_up`` parameter is only available
 243        on Python 3.12 or later)
 244      * :meth:`~pathlib.Path.walk` (available on Python 3.12 or later)
 245  
 246      Any methods that do disk I/O need to be awaited on. These methods are:
 247  
 248      * :meth:`~pathlib.Path.absolute`
 249      * :meth:`~pathlib.Path.chmod`
 250      * :meth:`~pathlib.Path.cwd`
 251      * :meth:`~pathlib.Path.exists`
 252      * :meth:`~pathlib.Path.expanduser`
 253      * :meth:`~pathlib.Path.group`
 254      * :meth:`~pathlib.Path.hardlink_to`
 255      * :meth:`~pathlib.Path.home`
 256      * :meth:`~pathlib.Path.is_block_device`
 257      * :meth:`~pathlib.Path.is_char_device`
 258      * :meth:`~pathlib.Path.is_dir`
 259      * :meth:`~pathlib.Path.is_fifo`
 260      * :meth:`~pathlib.Path.is_file`
 261      * :meth:`~pathlib.Path.is_junction`
 262      * :meth:`~pathlib.Path.is_mount`
 263      * :meth:`~pathlib.Path.is_socket`
 264      * :meth:`~pathlib.Path.is_symlink`
 265      * :meth:`~pathlib.Path.lchmod`
 266      * :meth:`~pathlib.Path.lstat`
 267      * :meth:`~pathlib.Path.mkdir`
 268      * :meth:`~pathlib.Path.open`
 269      * :meth:`~pathlib.Path.owner`
 270      * :meth:`~pathlib.Path.read_bytes`
 271      * :meth:`~pathlib.Path.read_text`
 272      * :meth:`~pathlib.Path.readlink`
 273      * :meth:`~pathlib.Path.rename`
 274      * :meth:`~pathlib.Path.replace`
 275      * :meth:`~pathlib.Path.resolve`
 276      * :meth:`~pathlib.Path.rmdir`
 277      * :meth:`~pathlib.Path.samefile`
 278      * :meth:`~pathlib.Path.stat`
 279      * :meth:`~pathlib.Path.symlink_to`
 280      * :meth:`~pathlib.Path.touch`
 281      * :meth:`~pathlib.Path.unlink`
 282      * :meth:`~pathlib.Path.walk`
 283      * :meth:`~pathlib.Path.write_bytes`
 284      * :meth:`~pathlib.Path.write_text`
 285  
 286      Additionally, the following methods return an async iterator yielding
 287      :class:`~.Path` objects:
 288  
 289      * :meth:`~pathlib.Path.glob`
 290      * :meth:`~pathlib.Path.iterdir`
 291      * :meth:`~pathlib.Path.rglob`
 292      """
 293  
 294      __slots__ = "_path", "__weakref__"
 295  
 296      __weakref__: Any
 297  
 298      def __init__(self, *args: str | PathLike[str]) -> None:
 299          self._path: Final[pathlib.Path] = pathlib.Path(*args)
 300  
 301      def __fspath__(self) -> str:
 302          return self._path.__fspath__()
 303  
 304      def __str__(self) -> str:
 305          return self._path.__str__()
 306  
 307      def __repr__(self) -> str:
 308          return f"{self.__class__.__name__}({self.as_posix()!r})"
 309  
 310      def __bytes__(self) -> bytes:
 311          return self._path.__bytes__()
 312  
 313      def __hash__(self) -> int:
 314          return self._path.__hash__()
 315  
 316      def __eq__(self, other: object) -> bool:
 317          target = other._path if isinstance(other, Path) else other
 318          return self._path.__eq__(target)
 319  
 320      def __lt__(self, other: pathlib.PurePath | Path) -> bool:
 321          target = other._path if isinstance(other, Path) else other
 322          return self._path.__lt__(target)
 323  
 324      def __le__(self, other: pathlib.PurePath | Path) -> bool:
 325          target = other._path if isinstance(other, Path) else other
 326          return self._path.__le__(target)
 327  
 328      def __gt__(self, other: pathlib.PurePath | Path) -> bool:
 329          target = other._path if isinstance(other, Path) else other
 330          return self._path.__gt__(target)
 331  
 332      def __ge__(self, other: pathlib.PurePath | Path) -> bool:
 333          target = other._path if isinstance(other, Path) else other
 334          return self._path.__ge__(target)
 335  
 336      def __truediv__(self, other: str | PathLike[str]) -> Path:
 337          return Path(self._path / other)
 338  
 339      def __rtruediv__(self, other: str | PathLike[str]) -> Path:
 340          return Path(other) / self
 341  
 342      @property
 343      def parts(self) -> tuple[str, ...]:
 344          return self._path.parts
 345  
 346      @property
 347      def drive(self) -> str:
 348          return self._path.drive
 349  
 350      @property
 351      def root(self) -> str:
 352          return self._path.root
 353  
 354      @property
 355      def anchor(self) -> str:
 356          return self._path.anchor
 357  
 358      @property
 359      def parents(self) -> Sequence[Path]:
 360          return tuple(Path(p) for p in self._path.parents)
 361  
 362      @property
 363      def parent(self) -> Path:
 364          return Path(self._path.parent)
 365  
 366      @property
 367      def name(self) -> str:
 368          return self._path.name
 369  
 370      @property
 371      def suffix(self) -> str:
 372          return self._path.suffix
 373  
 374      @property
 375      def suffixes(self) -> list[str]:
 376          return self._path.suffixes
 377  
 378      @property
 379      def stem(self) -> str:
 380          return self._path.stem
 381  
 382      async def absolute(self) -> Path:
 383          path = await to_thread.run_sync(self._path.absolute)
 384          return Path(path)
 385  
 386      def as_posix(self) -> str:
 387          return self._path.as_posix()
 388  
 389      def as_uri(self) -> str:
 390          return self._path.as_uri()
 391  
 392      if sys.version_info >= (3, 13):
 393          parser: ClassVar[ModuleType] = pathlib.Path.parser
 394  
 395          @classmethod
 396          def from_uri(cls, uri: str) -> Path:
 397              return Path(pathlib.Path.from_uri(uri))
 398  
 399          def full_match(
 400              self, path_pattern: str, *, case_sensitive: bool | None = None
 401          ) -> bool:
 402              return self._path.full_match(path_pattern, case_sensitive=case_sensitive)
 403  
 404          def match(
 405              self, path_pattern: str, *, case_sensitive: bool | None = None
 406          ) -> bool:
 407              return self._path.match(path_pattern, case_sensitive=case_sensitive)
 408      else:
 409  
 410          def match(self, path_pattern: str) -> bool:
 411              return self._path.match(path_pattern)
 412  
 413      if sys.version_info >= (3, 14):
 414  
 415          @property
 416          def info(self) -> Any:  # TODO: add return type annotation when Typeshed gets it
 417              return self._path.info
 418  
 419          async def copy(
 420              self,
 421              target: str | os.PathLike[str],
 422              *,
 423              follow_symlinks: bool = True,
 424              dirs_exist_ok: bool = False,
 425              preserve_metadata: bool = False,
 426          ) -> Path:
 427              func = partial(
 428                  self._path.copy,
 429                  follow_symlinks=follow_symlinks,
 430                  dirs_exist_ok=dirs_exist_ok,
 431                  preserve_metadata=preserve_metadata,
 432              )
 433              return Path(await to_thread.run_sync(func, target))
 434  
 435          async def copy_into(
 436              self,
 437              target_dir: str | os.PathLike[str],
 438              *,
 439              follow_symlinks: bool = True,
 440              dirs_exist_ok: bool = False,
 441              preserve_metadata: bool = False,
 442          ) -> Path:
 443              func = partial(
 444                  self._path.copy_into,
 445                  follow_symlinks=follow_symlinks,
 446                  dirs_exist_ok=dirs_exist_ok,
 447                  preserve_metadata=preserve_metadata,
 448              )
 449              return Path(await to_thread.run_sync(func, target_dir))
 450  
 451          async def move(self, target: str | os.PathLike[str]) -> Path:
 452              # Upstream does not handle anyio.Path properly as a PathLike
 453              target = pathlib.Path(target)
 454              return Path(await to_thread.run_sync(self._path.move, target))
 455  
 456          async def move_into(
 457              self,
 458              target_dir: str | os.PathLike[str],
 459          ) -> Path:
 460              return Path(await to_thread.run_sync(self._path.move_into, target_dir))
 461  
 462      def is_relative_to(self, other: str | PathLike[str]) -> bool:
 463          try:
 464              self.relative_to(other)
 465              return True
 466          except ValueError:
 467              return False
 468  
 469      async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None:
 470          func = partial(os.chmod, follow_symlinks=follow_symlinks)
 471          return await to_thread.run_sync(func, self._path, mode)
 472  
 473      @classmethod
 474      async def cwd(cls) -> Path:
 475          path = await to_thread.run_sync(pathlib.Path.cwd)
 476          return cls(path)
 477  
 478      async def exists(self) -> bool:
 479          return await to_thread.run_sync(self._path.exists, abandon_on_cancel=True)
 480  
 481      async def expanduser(self) -> Path:
 482          return Path(
 483              await to_thread.run_sync(self._path.expanduser, abandon_on_cancel=True)
 484          )
 485  
 486      def glob(self, pattern: str) -> AsyncIterator[Path]:
 487          gen = self._path.glob(pattern)
 488          return _PathIterator(gen)
 489  
 490      async def group(self) -> str:
 491          return await to_thread.run_sync(self._path.group, abandon_on_cancel=True)
 492  
 493      async def hardlink_to(
 494          self, target: str | bytes | PathLike[str] | PathLike[bytes]
 495      ) -> None:
 496          if isinstance(target, Path):
 497              target = target._path
 498  
 499          await to_thread.run_sync(os.link, target, self)
 500  
 501      @classmethod
 502      async def home(cls) -> Path:
 503          home_path = await to_thread.run_sync(pathlib.Path.home)
 504          return cls(home_path)
 505  
 506      def is_absolute(self) -> bool:
 507          return self._path.is_absolute()
 508  
 509      async def is_block_device(self) -> bool:
 510          return await to_thread.run_sync(
 511              self._path.is_block_device, abandon_on_cancel=True
 512          )
 513  
 514      async def is_char_device(self) -> bool:
 515          return await to_thread.run_sync(
 516              self._path.is_char_device, abandon_on_cancel=True
 517          )
 518  
 519      async def is_dir(self) -> bool:
 520          return await to_thread.run_sync(self._path.is_dir, abandon_on_cancel=True)
 521  
 522      async def is_fifo(self) -> bool:
 523          return await to_thread.run_sync(self._path.is_fifo, abandon_on_cancel=True)
 524  
 525      async def is_file(self) -> bool:
 526          return await to_thread.run_sync(self._path.is_file, abandon_on_cancel=True)
 527  
 528      if sys.version_info >= (3, 12):
 529  
 530          async def is_junction(self) -> bool:
 531              return await to_thread.run_sync(self._path.is_junction)
 532  
 533      async def is_mount(self) -> bool:
 534          return await to_thread.run_sync(
 535              os.path.ismount, self._path, abandon_on_cancel=True
 536          )
 537  
 538      def is_reserved(self) -> bool:
 539          return self._path.is_reserved()
 540  
 541      async def is_socket(self) -> bool:
 542          return await to_thread.run_sync(self._path.is_socket, abandon_on_cancel=True)
 543  
 544      async def is_symlink(self) -> bool:
 545          return await to_thread.run_sync(self._path.is_symlink, abandon_on_cancel=True)
 546  
 547      async def iterdir(self) -> AsyncIterator[Path]:
 548          gen = (
 549              self._path.iterdir()
 550              if sys.version_info < (3, 13)
 551              else await to_thread.run_sync(self._path.iterdir, abandon_on_cancel=True)
 552          )
 553          async for path in _PathIterator(gen):
 554              yield path
 555  
 556      def joinpath(self, *args: str | PathLike[str]) -> Path:
 557          return Path(self._path.joinpath(*args))
 558  
 559      async def lchmod(self, mode: int) -> None:
 560          await to_thread.run_sync(self._path.lchmod, mode)
 561  
 562      async def lstat(self) -> os.stat_result:
 563          return await to_thread.run_sync(self._path.lstat, abandon_on_cancel=True)
 564  
 565      async def mkdir(
 566          self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False
 567      ) -> None:
 568          await to_thread.run_sync(self._path.mkdir, mode, parents, exist_ok)
 569  
 570      @overload
 571      async def open(
 572          self,
 573          mode: OpenBinaryMode,
 574          buffering: int = ...,
 575          encoding: str | None = ...,
 576          errors: str | None = ...,
 577          newline: str | None = ...,
 578      ) -> AsyncFile[bytes]: ...
 579  
 580      @overload
 581      async def open(
 582          self,
 583          mode: OpenTextMode = ...,
 584          buffering: int = ...,
 585          encoding: str | None = ...,
 586          errors: str | None = ...,
 587          newline: str | None = ...,
 588      ) -> AsyncFile[str]: ...
 589  
 590      async def open(
 591          self,
 592          mode: str = "r",
 593          buffering: int = -1,
 594          encoding: str | None = None,
 595          errors: str | None = None,
 596          newline: str | None = None,
 597      ) -> AsyncFile[Any]:
 598          fp = await to_thread.run_sync(
 599              self._path.open, mode, buffering, encoding, errors, newline
 600          )
 601          return AsyncFile(fp)
 602  
 603      async def owner(self) -> str:
 604          return await to_thread.run_sync(self._path.owner, abandon_on_cancel=True)
 605  
 606      async def read_bytes(self) -> bytes:
 607          return await to_thread.run_sync(self._path.read_bytes)
 608  
 609      async def read_text(
 610          self, encoding: str | None = None, errors: str | None = None
 611      ) -> str:
 612          return await to_thread.run_sync(self._path.read_text, encoding, errors)
 613  
 614      if sys.version_info >= (3, 12):
 615  
 616          def relative_to(
 617              self, *other: str | PathLike[str], walk_up: bool = False
 618          ) -> Path:
 619              return Path(self._path.relative_to(*other, walk_up=walk_up))
 620  
 621      else:
 622  
 623          def relative_to(self, *other: str | PathLike[str]) -> Path:
 624              return Path(self._path.relative_to(*other))
 625  
 626      async def readlink(self) -> Path:
 627          target = await to_thread.run_sync(os.readlink, self._path)
 628          return Path(target)
 629  
 630      async def rename(self, target: str | pathlib.PurePath | Path) -> Path:
 631          if isinstance(target, Path):
 632              target = target._path
 633  
 634          await to_thread.run_sync(self._path.rename, target)
 635          return Path(target)
 636  
 637      async def replace(self, target: str | pathlib.PurePath | Path) -> Path:
 638          if isinstance(target, Path):
 639              target = target._path
 640  
 641          await to_thread.run_sync(self._path.replace, target)
 642          return Path(target)
 643  
 644      async def resolve(self, strict: bool = False) -> Path:
 645          func = partial(self._path.resolve, strict=strict)
 646          return Path(await to_thread.run_sync(func, abandon_on_cancel=True))
 647  
 648      def rglob(self, pattern: str) -> AsyncIterator[Path]:
 649          gen = self._path.rglob(pattern)
 650          return _PathIterator(gen)
 651  
 652      async def rmdir(self) -> None:
 653          await to_thread.run_sync(self._path.rmdir)
 654  
 655      async def samefile(self, other_path: str | PathLike[str]) -> bool:
 656          if isinstance(other_path, Path):
 657              other_path = other_path._path
 658  
 659          return await to_thread.run_sync(
 660              self._path.samefile, other_path, abandon_on_cancel=True
 661          )
 662  
 663      async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result:
 664          func = partial(os.stat, follow_symlinks=follow_symlinks)
 665          return await to_thread.run_sync(func, self._path, abandon_on_cancel=True)
 666  
 667      async def symlink_to(
 668          self,
 669          target: str | bytes | PathLike[str] | PathLike[bytes],
 670          target_is_directory: bool = False,
 671      ) -> None:
 672          if isinstance(target, Path):
 673              target = target._path
 674  
 675          await to_thread.run_sync(self._path.symlink_to, target, target_is_directory)
 676  
 677      async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None:
 678          await to_thread.run_sync(self._path.touch, mode, exist_ok)
 679  
 680      async def unlink(self, missing_ok: bool = False) -> None:
 681          try:
 682              await to_thread.run_sync(self._path.unlink)
 683          except FileNotFoundError:
 684              if not missing_ok:
 685                  raise
 686  
 687      if sys.version_info >= (3, 12):
 688  
 689          async def walk(
 690              self,
 691              top_down: bool = True,
 692              on_error: Callable[[OSError], object] | None = None,
 693              follow_symlinks: bool = False,
 694          ) -> AsyncIterator[tuple[Path, list[str], list[str]]]:
 695              def get_next_value() -> tuple[pathlib.Path, list[str], list[str]] | None:
 696                  try:
 697                      return next(gen)
 698                  except StopIteration:
 699                      return None
 700  
 701              gen = self._path.walk(top_down, on_error, follow_symlinks)
 702              while True:
 703                  value = await to_thread.run_sync(get_next_value)
 704                  if value is None:
 705                      return
 706  
 707                  root, dirs, paths = value
 708                  yield Path(root), dirs, paths
 709  
 710      def with_name(self, name: str) -> Path:
 711          return Path(self._path.with_name(name))
 712  
 713      def with_stem(self, stem: str) -> Path:
 714          return Path(self._path.with_name(stem + self._path.suffix))
 715  
 716      def with_suffix(self, suffix: str) -> Path:
 717          return Path(self._path.with_suffix(suffix))
 718  
 719      def with_segments(self, *pathsegments: str | PathLike[str]) -> Path:
 720          return Path(*pathsegments)
 721  
 722      async def write_bytes(self, data: bytes) -> int:
 723          return await to_thread.run_sync(self._path.write_bytes, data)
 724  
 725      async def write_text(
 726          self,
 727          data: str,
 728          encoding: str | None = None,
 729          errors: str | None = None,
 730          newline: str | None = None,
 731      ) -> int:
 732          # Path.write_text() does not support the "newline" parameter before Python 3.10
 733          def sync_write_text() -> int:
 734              with self._path.open(
 735                  "w", encoding=encoding, errors=errors, newline=newline
 736              ) as fp:
 737                  return fp.write(data)
 738  
 739          return await to_thread.run_sync(sync_write_text)
 740  
 741  
 742  PathLike.register(Path)
 743