Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/anyio/_core/_fileio.py: 44%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from __future__ import annotations
3import os
4import pathlib
5import sys
6from collections.abc import (
7 AsyncIterator,
8 Callable,
9 Iterable,
10 Iterator,
11 Sequence,
12)
13from dataclasses import dataclass
14from functools import partial
15from os import PathLike
16from typing import (
17 IO,
18 TYPE_CHECKING,
19 Any,
20 AnyStr,
21 ClassVar,
22 Final,
23 Generic,
24 TypeVar,
25 overload,
26)
28from .. import to_thread
29from ..abc import AsyncResource
30from ._synchronization import CapacityLimiter
32if sys.version_info >= (3, 11):
33 from typing import Self
34else:
35 from typing_extensions import Self
37if sys.version_info >= (3, 14):
38 from pathlib.types import PathInfo
40if TYPE_CHECKING:
41 from types import ModuleType
43 from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer
44else:
45 ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object
48T = TypeVar("T", bound="Path")
51class AsyncFile(AsyncResource, Generic[AnyStr]):
52 """
53 An asynchronous file object.
55 This class wraps a standard file object and provides async friendly versions of the
56 following blocking methods (where available on the original file object):
58 * read
59 * read1
60 * readline
61 * readlines
62 * readinto
63 * readinto1
64 * write
65 * writelines
66 * truncate
67 * seek
68 * tell
69 * flush
71 All other methods are directly passed through.
73 This class supports the asynchronous context manager protocol which closes the
74 underlying file at the end of the context block.
76 This class also supports asynchronous iteration::
78 async with await open_file(...) as f:
79 async for line in f:
80 print(line)
81 """
83 def __init__(
84 self, fp: IO[AnyStr], *, limiter: CapacityLimiter | None = None
85 ) -> None:
86 if limiter is not None and not isinstance(limiter, CapacityLimiter):
87 raise TypeError(
88 f"limiter must be a CapacityLimiter or None, not "
89 f"{limiter.__class__.__name__}"
90 )
92 self._fp: Any = fp
93 self._limiter = limiter
95 def __getattr__(self, name: str) -> object:
96 return getattr(self._fp, name)
98 @property
99 def limiter(self) -> CapacityLimiter | None:
100 """The capacity limiter used by this file object, if not the global limiter."""
101 return self._limiter
103 @property
104 def wrapped(self) -> IO[AnyStr]:
105 """The wrapped file object."""
106 return self._fp
108 async def __aiter__(self) -> AsyncIterator[AnyStr]:
109 while True:
110 line = await self.readline()
111 if line:
112 yield line
113 else:
114 break
116 async def aclose(self) -> None:
117 return await to_thread.run_sync(self._fp.close, limiter=self._limiter)
119 async def read(self, size: int = -1) -> AnyStr:
120 return await to_thread.run_sync(self._fp.read, size, limiter=self._limiter)
122 async def read1(self: AsyncFile[bytes], size: int = -1) -> bytes:
123 return await to_thread.run_sync(self._fp.read1, size, limiter=self._limiter)
125 async def readline(self) -> AnyStr:
126 return await to_thread.run_sync(self._fp.readline, limiter=self._limiter)
128 async def readlines(self) -> list[AnyStr]:
129 return await to_thread.run_sync(self._fp.readlines, limiter=self._limiter)
131 async def readinto(self: AsyncFile[bytes], b: WriteableBuffer) -> int:
132 return await to_thread.run_sync(self._fp.readinto, b, limiter=self._limiter)
134 async def readinto1(self: AsyncFile[bytes], b: WriteableBuffer) -> int:
135 return await to_thread.run_sync(self._fp.readinto1, b, limiter=self._limiter)
137 @overload
138 async def write(self: AsyncFile[bytes], b: ReadableBuffer) -> int: ...
140 @overload
141 async def write(self: AsyncFile[str], b: str) -> int: ...
143 async def write(self, b: ReadableBuffer | str) -> int:
144 return await to_thread.run_sync(self._fp.write, b, limiter=self._limiter)
146 @overload
147 async def writelines(
148 self: AsyncFile[bytes], lines: Iterable[ReadableBuffer]
149 ) -> None: ...
151 @overload
152 async def writelines(self: AsyncFile[str], lines: Iterable[str]) -> None: ...
154 async def writelines(self, lines: Iterable[ReadableBuffer] | Iterable[str]) -> None:
155 return await to_thread.run_sync(
156 self._fp.writelines, lines, limiter=self._limiter
157 )
159 async def truncate(self, size: int | None = None) -> int:
160 return await to_thread.run_sync(self._fp.truncate, size, limiter=self._limiter)
162 async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int:
163 return await to_thread.run_sync(
164 self._fp.seek, offset, whence, limiter=self._limiter
165 )
167 async def tell(self) -> int:
168 return await to_thread.run_sync(self._fp.tell, limiter=self._limiter)
170 async def flush(self) -> None:
171 return await to_thread.run_sync(self._fp.flush, limiter=self._limiter)
174@overload
175async def open_file(
176 file: str | PathLike[str] | int,
177 mode: OpenBinaryMode,
178 buffering: int = ...,
179 encoding: str | None = ...,
180 errors: str | None = ...,
181 newline: str | None = ...,
182 closefd: bool = ...,
183 opener: Callable[[str, int], int] | None = ...,
184 *,
185 limiter: CapacityLimiter | None = ...,
186) -> AsyncFile[bytes]: ...
189@overload
190async def open_file(
191 file: str | PathLike[str] | int,
192 mode: OpenTextMode = ...,
193 buffering: int = ...,
194 encoding: str | None = ...,
195 errors: str | None = ...,
196 newline: str | None = ...,
197 closefd: bool = ...,
198 opener: Callable[[str, int], int] | None = ...,
199 *,
200 limiter: CapacityLimiter | None = ...,
201) -> AsyncFile[str]: ...
204async def open_file(
205 file: str | PathLike[str] | int,
206 mode: str = "r",
207 buffering: int = -1,
208 encoding: str | None = None,
209 errors: str | None = None,
210 newline: str | None = None,
211 closefd: bool = True,
212 opener: Callable[[str, int], int] | None = None,
213 *,
214 limiter: CapacityLimiter | None = None,
215) -> AsyncFile[Any]:
216 """
217 Open a file asynchronously.
219 Except for ``limiter``, the arguments are exactly the same as for the builtin :func:`open`.
221 :param limiter: an optional capacity limiter to use with the file
222 instead of the default one
223 :return: an asynchronous file object
225 .. versionchanged:: 4.14.0
226 Added the ``limiter`` keyword argument.
228 """
229 fp = await to_thread.run_sync(
230 open,
231 file,
232 mode,
233 buffering,
234 encoding,
235 errors,
236 newline,
237 closefd,
238 opener,
239 limiter=limiter,
240 )
241 return AsyncFile(fp, limiter=limiter)
244def wrap_file(
245 file: IO[AnyStr], *, limiter: CapacityLimiter | None = None
246) -> AsyncFile[AnyStr]:
247 """
248 Wrap an existing file as an asynchronous file.
250 :param file: an existing file-like object
251 :param limiter: an optional capacity limiter to use with the file
252 instead of the default one
253 :return: an asynchronous file object
255 .. versionchanged:: 4.14.0
256 Added the ``limiter`` keyword argument.
258 """
259 return AsyncFile(file, limiter=limiter)
262@dataclass(eq=False)
263class _PathIterator(AsyncIterator[T]):
264 iterator: Iterator[PathLike[str]]
265 limiter: CapacityLimiter | None
266 # This was added to ensure that iterating over a subclass of Path yields instances
267 # of that subclass rather than the base Path class.
268 path_cls: type[T]
270 async def __anext__(self) -> T:
271 nextval = await to_thread.run_sync(
272 next, self.iterator, None, abandon_on_cancel=True, limiter=self.limiter
273 )
274 if nextval is None:
275 raise StopAsyncIteration from None
277 return self.path_cls(nextval, limiter=self.limiter)
280class Path:
281 """
282 An asynchronous version of :class:`pathlib.Path`.
284 This class cannot be substituted for :class:`pathlib.Path` or
285 :class:`pathlib.PurePath`, but it is compatible with the :class:`os.PathLike`
286 interface.
288 It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for
289 the deprecated :meth:`~pathlib.Path.link_to` method.
291 Some methods may be unavailable or have limited functionality, based on the Python
292 version:
294 * :meth:`~pathlib.Path.copy` (available on Python 3.14 or later)
295 * :meth:`~pathlib.Path.copy_into` (available on Python 3.14 or later)
296 * :meth:`~pathlib.Path.from_uri` (available on Python 3.13 or later)
297 * :meth:`~pathlib.PurePath.full_match` (available on Python 3.13 or later)
298 * :attr:`~pathlib.Path.info` (available on Python 3.14 or later)
299 * :meth:`~pathlib.Path.is_junction` (available on Python 3.12 or later)
300 * :meth:`~pathlib.PurePath.match` (the ``case_sensitive`` parameter is only
301 available on Python 3.13 or later)
302 * :meth:`~pathlib.Path.move` (available on Python 3.14 or later)
303 * :meth:`~pathlib.Path.move_into` (available on Python 3.14 or later)
304 * :meth:`~pathlib.PurePath.relative_to` (the ``walk_up`` parameter is only available
305 on Python 3.12 or later)
306 * :meth:`~pathlib.Path.walk` (available on Python 3.12 or later)
308 Any methods that do disk I/O need to be awaited on. These methods are:
310 * :meth:`~pathlib.Path.absolute`
311 * :meth:`~pathlib.Path.chmod`
312 * :meth:`~pathlib.Path.cwd`
313 * :meth:`~pathlib.Path.exists`
314 * :meth:`~pathlib.Path.expanduser`
315 * :meth:`~pathlib.Path.group`
316 * :meth:`~pathlib.Path.hardlink_to`
317 * :meth:`~pathlib.Path.home`
318 * :meth:`~pathlib.Path.is_block_device`
319 * :meth:`~pathlib.Path.is_char_device`
320 * :meth:`~pathlib.Path.is_dir`
321 * :meth:`~pathlib.Path.is_fifo`
322 * :meth:`~pathlib.Path.is_file`
323 * :meth:`~pathlib.Path.is_junction`
324 * :meth:`~pathlib.Path.is_mount`
325 * :meth:`~pathlib.Path.is_socket`
326 * :meth:`~pathlib.Path.is_symlink`
327 * :meth:`~pathlib.Path.lchmod`
328 * :meth:`~pathlib.Path.lstat`
329 * :meth:`~pathlib.Path.mkdir`
330 * :meth:`~pathlib.Path.open`
331 * :meth:`~pathlib.Path.owner`
332 * :meth:`~pathlib.Path.read_bytes`
333 * :meth:`~pathlib.Path.read_text`
334 * :meth:`~pathlib.Path.readlink`
335 * :meth:`~pathlib.Path.rename`
336 * :meth:`~pathlib.Path.replace`
337 * :meth:`~pathlib.Path.resolve`
338 * :meth:`~pathlib.Path.rmdir`
339 * :meth:`~pathlib.Path.samefile`
340 * :meth:`~pathlib.Path.stat`
341 * :meth:`~pathlib.Path.symlink_to`
342 * :meth:`~pathlib.Path.touch`
343 * :meth:`~pathlib.Path.unlink`
344 * :meth:`~pathlib.Path.walk`
345 * :meth:`~pathlib.Path.write_bytes`
346 * :meth:`~pathlib.Path.write_text`
348 Additionally, the following methods return an async iterator yielding
349 :class:`~.Path` objects:
351 * :meth:`~pathlib.Path.glob`
352 * :meth:`~pathlib.Path.iterdir`
353 * :meth:`~pathlib.Path.rglob`
355 .. versionchanged:: 4.14.0
356 Added the ``limiter`` keyword argument.
357 """
359 __slots__ = "_path", "_limiter", "__weakref__"
361 __weakref__: Any
363 def __init__(
364 self, *args: str | PathLike[str], limiter: CapacityLimiter | None = None
365 ) -> None:
366 if limiter is not None and not isinstance(limiter, CapacityLimiter):
367 raise TypeError(
368 f"limiter must be a CapacityLimiter or None, not "
369 f"{limiter.__class__.__name__}"
370 )
372 self._path: Final[pathlib.Path] = pathlib.Path(*args)
373 self._limiter = limiter
375 def __fspath__(self) -> str:
376 return self._path.__fspath__()
378 if sys.version_info >= (3, 15):
380 def __vfspath__(self) -> str:
381 return self._path.__vfspath__()
383 def __str__(self) -> str:
384 return self._path.__str__()
386 def __repr__(self) -> str:
387 return f"{self.__class__.__name__}({self.as_posix()!r})"
389 def __bytes__(self) -> bytes:
390 return self._path.__bytes__()
392 def __hash__(self) -> int:
393 return self._path.__hash__()
395 def __eq__(self, other: object) -> bool:
396 target = other._path if isinstance(other, Path) else other
397 return self._path.__eq__(target)
399 def __lt__(self, other: pathlib.PurePath | Path) -> bool:
400 target = other._path if isinstance(other, Path) else other
401 return self._path.__lt__(target)
403 def __le__(self, other: pathlib.PurePath | Path) -> bool:
404 target = other._path if isinstance(other, Path) else other
405 return self._path.__le__(target)
407 def __gt__(self, other: pathlib.PurePath | Path) -> bool:
408 target = other._path if isinstance(other, Path) else other
409 return self._path.__gt__(target)
411 def __ge__(self, other: pathlib.PurePath | Path) -> bool:
412 target = other._path if isinstance(other, Path) else other
413 return self._path.__ge__(target)
415 def __truediv__(self, other: str | PathLike[str]) -> Self:
416 return type(self)(self._path / other, limiter=self._limiter)
418 def __rtruediv__(self, other: str | PathLike[str]) -> Self:
419 return type(self)(other, limiter=self._limiter) / self
421 @property
422 def limiter(self) -> CapacityLimiter | None:
423 """The capacity limiter used by this path, if not the global limiter."""
424 return self._limiter
426 @property
427 def parts(self) -> tuple[str, ...]:
428 return self._path.parts
430 @property
431 def drive(self) -> str:
432 return self._path.drive
434 @property
435 def root(self) -> str:
436 return self._path.root
438 @property
439 def anchor(self) -> str:
440 return self._path.anchor
442 @property
443 def parents(self) -> Sequence[Self]:
444 return tuple(type(self)(p, limiter=self._limiter) for p in self._path.parents)
446 @property
447 def parent(self) -> Self:
448 return type(self)(self._path.parent, limiter=self._limiter)
450 @property
451 def name(self) -> str:
452 return self._path.name
454 @property
455 def suffix(self) -> str:
456 return self._path.suffix
458 @property
459 def suffixes(self) -> list[str]:
460 return self._path.suffixes
462 @property
463 def stem(self) -> str:
464 return self._path.stem
466 async def absolute(self) -> Self:
467 path = await to_thread.run_sync(self._path.absolute, limiter=self._limiter)
468 return type(self)(path, limiter=self._limiter)
470 def as_posix(self) -> str:
471 return self._path.as_posix()
473 def as_uri(self) -> str:
474 return self._path.as_uri()
476 if sys.version_info >= (3, 13):
477 parser: ClassVar[ModuleType] = pathlib.Path.parser
479 @classmethod
480 def from_uri(cls, uri: str, *, limiter: CapacityLimiter | None = None) -> Self:
481 return cls(pathlib.Path.from_uri(uri), limiter=limiter)
483 def full_match(
484 self, path_pattern: str, *, case_sensitive: bool | None = None
485 ) -> bool:
486 return self._path.full_match(path_pattern, case_sensitive=case_sensitive)
488 def match(
489 self, path_pattern: str, *, case_sensitive: bool | None = None
490 ) -> bool:
491 return self._path.match(path_pattern, case_sensitive=case_sensitive)
492 else:
494 def match(self, path_pattern: str) -> bool:
495 return self._path.match(path_pattern)
497 if sys.version_info >= (3, 14):
499 @property
500 def info(self) -> PathInfo:
501 return self._path.info
503 async def copy(
504 self,
505 target: str | os.PathLike[str],
506 *,
507 follow_symlinks: bool = True,
508 preserve_metadata: bool = False,
509 ) -> Self:
510 func = partial(
511 self._path.copy,
512 follow_symlinks=follow_symlinks,
513 preserve_metadata=preserve_metadata,
514 )
515 return type(self)(
516 await to_thread.run_sync(
517 func, pathlib.Path(target), limiter=self._limiter
518 ),
519 limiter=self._limiter,
520 )
522 async def copy_into(
523 self,
524 target_dir: str | os.PathLike[str],
525 *,
526 follow_symlinks: bool = True,
527 preserve_metadata: bool = False,
528 ) -> Self:
529 func = partial(
530 self._path.copy_into,
531 follow_symlinks=follow_symlinks,
532 preserve_metadata=preserve_metadata,
533 )
534 return type(self)(
535 await to_thread.run_sync(
536 func, pathlib.Path(target_dir), limiter=self._limiter
537 ),
538 limiter=self._limiter,
539 )
541 async def move(self, target: str | os.PathLike[str]) -> Self:
542 # Upstream does not handle anyio.Path properly as a PathLike
543 target = pathlib.Path(target)
544 return type(self)(
545 await to_thread.run_sync(
546 self._path.move, target, limiter=self._limiter
547 ),
548 limiter=self._limiter,
549 )
551 async def move_into(
552 self,
553 target_dir: str | os.PathLike[str],
554 ) -> Self:
555 return type(self)(
556 await to_thread.run_sync(
557 self._path.move_into, target_dir, limiter=self._limiter
558 ),
559 limiter=self._limiter,
560 )
562 def is_relative_to(self, other: str | PathLike[str]) -> bool:
563 try:
564 self.relative_to(other)
565 return True
566 except ValueError:
567 return False
569 async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None:
570 func = partial(os.chmod, follow_symlinks=follow_symlinks)
571 return await to_thread.run_sync(func, self._path, mode, limiter=self._limiter)
573 @classmethod
574 async def cwd(cls, *, limiter: CapacityLimiter | None = None) -> Self:
575 path = await to_thread.run_sync(pathlib.Path.cwd, limiter=limiter)
576 return cls(path, limiter=limiter)
578 async def exists(self) -> bool:
579 return await to_thread.run_sync(
580 self._path.exists, abandon_on_cancel=True, limiter=self._limiter
581 )
583 async def expanduser(self) -> Self:
584 return type(self)(
585 await to_thread.run_sync(
586 self._path.expanduser, abandon_on_cancel=True, limiter=self._limiter
587 ),
588 limiter=self._limiter,
589 )
591 if sys.version_info < (3, 12):
592 # Python 3.11 and earlier
593 def glob(self, pattern: str) -> AsyncIterator[Self]:
594 gen = self._path.glob(pattern)
595 return _PathIterator(gen, self._limiter, type(self))
596 elif (3, 12) <= sys.version_info < (3, 13):
597 # changed in Python 3.12:
598 # - The case_sensitive parameter was added.
599 def glob(
600 self,
601 pattern: str,
602 *,
603 case_sensitive: bool | None = None,
604 ) -> AsyncIterator[Self]:
605 gen = self._path.glob(pattern, case_sensitive=case_sensitive)
606 return _PathIterator(gen, self._limiter, type(self))
607 elif sys.version_info >= (3, 13):
608 # Changed in Python 3.13:
609 # - The recurse_symlinks parameter was added.
610 # - The pattern parameter accepts a path-like object.
611 def glob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block
612 self,
613 pattern: str | PathLike[str],
614 *,
615 case_sensitive: bool | None = None,
616 recurse_symlinks: bool = False,
617 ) -> AsyncIterator[Self]:
618 gen = self._path.glob(
619 pattern, # type: ignore[arg-type]
620 case_sensitive=case_sensitive,
621 recurse_symlinks=recurse_symlinks,
622 )
623 return _PathIterator(gen, self._limiter, type(self))
625 async def group(self) -> str:
626 return await to_thread.run_sync(
627 self._path.group, abandon_on_cancel=True, limiter=self._limiter
628 )
630 async def hardlink_to(
631 self, target: str | bytes | PathLike[str] | PathLike[bytes]
632 ) -> None:
633 if isinstance(target, Path):
634 target = target._path
636 await to_thread.run_sync(os.link, target, self, limiter=self._limiter)
638 @classmethod
639 async def home(cls, *, limiter: CapacityLimiter | None = None) -> Self:
640 home_path = await to_thread.run_sync(pathlib.Path.home, limiter=limiter)
641 return cls(home_path, limiter=limiter)
643 def is_absolute(self) -> bool:
644 return self._path.is_absolute()
646 async def is_block_device(self) -> bool:
647 return await to_thread.run_sync(
648 self._path.is_block_device, abandon_on_cancel=True, limiter=self._limiter
649 )
651 async def is_char_device(self) -> bool:
652 return await to_thread.run_sync(
653 self._path.is_char_device, abandon_on_cancel=True, limiter=self._limiter
654 )
656 async def is_dir(self) -> bool:
657 return await to_thread.run_sync(
658 self._path.is_dir, abandon_on_cancel=True, limiter=self._limiter
659 )
661 async def is_fifo(self) -> bool:
662 return await to_thread.run_sync(
663 self._path.is_fifo, abandon_on_cancel=True, limiter=self._limiter
664 )
666 async def is_file(self) -> bool:
667 return await to_thread.run_sync(
668 self._path.is_file, abandon_on_cancel=True, limiter=self._limiter
669 )
671 if sys.version_info >= (3, 12):
673 async def is_junction(self) -> bool:
674 return await to_thread.run_sync(
675 self._path.is_junction, limiter=self._limiter
676 )
678 async def is_mount(self) -> bool:
679 return await to_thread.run_sync(
680 os.path.ismount, self._path, abandon_on_cancel=True, limiter=self._limiter
681 )
683 if sys.version_info < (3, 15):
685 def is_reserved(self) -> bool:
686 return self._path.is_reserved()
688 async def is_socket(self) -> bool:
689 return await to_thread.run_sync(
690 self._path.is_socket, abandon_on_cancel=True, limiter=self._limiter
691 )
693 async def is_symlink(self) -> bool:
694 return await to_thread.run_sync(
695 self._path.is_symlink, abandon_on_cancel=True, limiter=self._limiter
696 )
698 async def iterdir(self) -> AsyncIterator[Self]:
699 gen = (
700 self._path.iterdir()
701 if sys.version_info < (3, 13)
702 else await to_thread.run_sync(
703 self._path.iterdir, abandon_on_cancel=True, limiter=self._limiter
704 )
705 )
706 async for path in _PathIterator(gen, self._limiter, type(self)):
707 yield path
709 def joinpath(self, *args: str | PathLike[str]) -> Self:
710 return type(self)(self._path.joinpath(*args), limiter=self._limiter)
712 async def lchmod(self, mode: int) -> None:
713 await to_thread.run_sync(self._path.lchmod, mode, limiter=self._limiter)
715 async def lstat(self) -> os.stat_result:
716 return await to_thread.run_sync(
717 self._path.lstat, abandon_on_cancel=True, limiter=self._limiter
718 )
720 async def mkdir(
721 self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False
722 ) -> None:
723 await to_thread.run_sync(
724 self._path.mkdir, mode, parents, exist_ok, limiter=self._limiter
725 )
727 @overload
728 async def open(
729 self,
730 mode: OpenBinaryMode,
731 buffering: int = ...,
732 encoding: str | None = ...,
733 errors: str | None = ...,
734 newline: str | None = ...,
735 ) -> AsyncFile[bytes]: ...
737 @overload
738 async def open(
739 self,
740 mode: OpenTextMode = ...,
741 buffering: int = ...,
742 encoding: str | None = ...,
743 errors: str | None = ...,
744 newline: str | None = ...,
745 ) -> AsyncFile[str]: ...
747 async def open(
748 self,
749 mode: str = "r",
750 buffering: int = -1,
751 encoding: str | None = None,
752 errors: str | None = None,
753 newline: str | None = None,
754 ) -> AsyncFile[Any]:
755 fp = await to_thread.run_sync(
756 self._path.open,
757 mode,
758 buffering,
759 encoding,
760 errors,
761 newline,
762 limiter=self._limiter,
763 )
764 return AsyncFile(fp, limiter=self._limiter)
766 async def owner(self) -> str:
767 return await to_thread.run_sync(
768 self._path.owner, abandon_on_cancel=True, limiter=self._limiter
769 )
771 async def read_bytes(self) -> bytes:
772 return await to_thread.run_sync(self._path.read_bytes, limiter=self._limiter)
774 async def read_text(
775 self, encoding: str | None = None, errors: str | None = None
776 ) -> str:
777 return await to_thread.run_sync(
778 self._path.read_text, encoding, errors, limiter=self._limiter
779 )
781 if sys.version_info >= (3, 12):
783 def relative_to(
784 self, *other: str | PathLike[str], walk_up: bool = False
785 ) -> Self:
786 # relative_to() should work with any PathLike but it doesn't
787 others = [pathlib.Path(other) for other in other]
788 return type(self)(
789 self._path.relative_to(*others, walk_up=walk_up), limiter=self._limiter
790 )
792 else:
794 def relative_to(self, *other: str | PathLike[str]) -> Self:
795 return type(self)(self._path.relative_to(*other), limiter=self._limiter)
797 async def readlink(self) -> Self:
798 target = await to_thread.run_sync(
799 os.readlink, self._path, limiter=self._limiter
800 )
801 return type(self)(target, limiter=self._limiter)
803 async def rename(self, target: str | pathlib.PurePath | Path) -> Self:
804 if isinstance(target, Path):
805 target = target._path
807 await to_thread.run_sync(self._path.rename, target, limiter=self._limiter)
808 return type(self)(target, limiter=self._limiter)
810 async def replace(self, target: str | pathlib.PurePath | Path) -> Self:
811 if isinstance(target, Path):
812 target = target._path
814 await to_thread.run_sync(self._path.replace, target, limiter=self._limiter)
815 return type(self)(target, limiter=self._limiter)
817 async def resolve(self, strict: bool = False) -> Self:
818 func = partial(self._path.resolve, strict=strict)
819 return type(self)(
820 await to_thread.run_sync(
821 func, abandon_on_cancel=True, limiter=self._limiter
822 ),
823 limiter=self._limiter,
824 )
826 if sys.version_info < (3, 12):
827 # Pre Python 3.12
828 def rglob(self, pattern: str) -> AsyncIterator[Self]:
829 gen = self._path.rglob(pattern)
830 return _PathIterator(gen, self._limiter, type(self))
831 elif (3, 12) <= sys.version_info < (3, 13):
832 # Changed in Python 3.12:
833 # - The case_sensitive parameter was added.
834 def rglob(
835 self, pattern: str, *, case_sensitive: bool | None = None
836 ) -> AsyncIterator[Self]:
837 gen = self._path.rglob(pattern, case_sensitive=case_sensitive)
838 return _PathIterator(gen, self._limiter, type(self))
839 elif sys.version_info >= (3, 13):
840 # Changed in Python 3.13:
841 # - The recurse_symlinks parameter was added.
842 # - The pattern parameter accepts a path-like object.
843 def rglob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block
844 self,
845 pattern: str | PathLike[str],
846 *,
847 case_sensitive: bool | None = None,
848 recurse_symlinks: bool = False,
849 ) -> AsyncIterator[Self]:
850 gen = self._path.rglob(
851 pattern, # type: ignore[arg-type]
852 case_sensitive=case_sensitive,
853 recurse_symlinks=recurse_symlinks,
854 )
855 return _PathIterator(gen, self._limiter, type(self))
857 async def rmdir(self) -> None:
858 await to_thread.run_sync(self._path.rmdir, limiter=self._limiter)
860 async def samefile(self, other_path: str | PathLike[str]) -> bool:
861 if isinstance(other_path, Path):
862 other_path = other_path._path
864 return await to_thread.run_sync(
865 self._path.samefile,
866 other_path,
867 abandon_on_cancel=True,
868 limiter=self._limiter,
869 )
871 async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result:
872 func = partial(os.stat, follow_symlinks=follow_symlinks)
873 return await to_thread.run_sync(
874 func, self._path, abandon_on_cancel=True, limiter=self._limiter
875 )
877 async def symlink_to(
878 self,
879 target: str | bytes | PathLike[str] | PathLike[bytes],
880 target_is_directory: bool = False,
881 ) -> None:
882 if isinstance(target, Path):
883 target = target._path
885 await to_thread.run_sync(
886 self._path.symlink_to, target, target_is_directory, limiter=self._limiter
887 )
889 async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None:
890 await to_thread.run_sync(
891 self._path.touch, mode, exist_ok, limiter=self._limiter
892 )
894 async def unlink(self, missing_ok: bool = False) -> None:
895 try:
896 await to_thread.run_sync(self._path.unlink, limiter=self._limiter)
897 except FileNotFoundError:
898 if not missing_ok:
899 raise
901 if sys.version_info >= (3, 12):
903 async def walk(
904 self,
905 top_down: bool = True,
906 on_error: Callable[[OSError], object] | None = None,
907 follow_symlinks: bool = False,
908 ) -> AsyncIterator[tuple[Self, list[str], list[str]]]:
909 def get_next_value() -> tuple[pathlib.Path, list[str], list[str]] | None:
910 try:
911 return next(gen)
912 except StopIteration:
913 return None
915 gen = self._path.walk(top_down, on_error, follow_symlinks)
916 while True:
917 value = await to_thread.run_sync(get_next_value, limiter=self._limiter)
918 if value is None:
919 return
921 root, dirs, paths = value
922 yield type(self)(root, limiter=self._limiter), dirs, paths
924 def with_name(self, name: str) -> Self:
925 return type(self)(self._path.with_name(name), limiter=self._limiter)
927 def with_stem(self, stem: str) -> Self:
928 return type(self)(
929 self._path.with_name(stem + self._path.suffix), limiter=self._limiter
930 )
932 def with_suffix(self, suffix: str) -> Self:
933 return type(self)(self._path.with_suffix(suffix), limiter=self._limiter)
935 def with_segments(self, *pathsegments: str | PathLike[str]) -> Self:
936 return type(self)(*pathsegments, limiter=self._limiter)
938 async def write_bytes(self, data: ReadableBuffer) -> int:
939 return await to_thread.run_sync(
940 self._path.write_bytes, data, limiter=self._limiter
941 )
943 async def write_text(
944 self,
945 data: str,
946 encoding: str | None = None,
947 errors: str | None = None,
948 newline: str | None = None,
949 ) -> int:
950 return await to_thread.run_sync(
951 self._path.write_text,
952 data,
953 encoding,
954 errors,
955 newline,
956 limiter=self._limiter,
957 )
960PathLike.register(Path)