Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/anyio/_core/_fileio.py: 43%

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

399 statements  

1from __future__ import annotations 

2 

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) 

27 

28from .. import to_thread 

29from ..abc import AsyncResource 

30from ._synchronization import CapacityLimiter 

31from ._tasks import CancelScope 

32 

33if sys.version_info >= (3, 11): 

34 from typing import Self 

35else: 

36 from typing_extensions import Self 

37 

38if sys.version_info >= (3, 14): 

39 from pathlib.types import PathInfo 

40 

41if TYPE_CHECKING: 

42 from types import ModuleType 

43 

44 from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer 

45else: 

46 ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object 

47 

48 

49T = TypeVar("T", bound="Path") 

50 

51 

52class AsyncFile(AsyncResource, Generic[AnyStr]): 

53 """ 

54 An asynchronous file object. 

55 

56 This class wraps a standard file object and provides async friendly versions of the 

57 following blocking methods (where available on the original file object): 

58 

59 * read 

60 * read1 

61 * readline 

62 * readlines 

63 * readinto 

64 * readinto1 

65 * write 

66 * writelines 

67 * truncate 

68 * seek 

69 * tell 

70 * flush 

71 

72 All other methods are directly passed through. 

73 

74 This class supports the asynchronous context manager protocol which closes the 

75 underlying file at the end of the context block. 

76 

77 This class also supports asynchronous iteration:: 

78 

79 async with await open_file(...) as f: 

80 async for line in f: 

81 print(line) 

82 """ 

83 

84 def __init__( 

85 self, fp: IO[AnyStr], *, limiter: CapacityLimiter | None = None 

86 ) -> None: 

87 if limiter is not None and not isinstance(limiter, CapacityLimiter): 

88 raise TypeError( 

89 f"limiter must be a CapacityLimiter or None, not " 

90 f"{limiter.__class__.__name__}" 

91 ) 

92 

93 self._fp: Any = fp 

94 self._limiter = limiter 

95 

96 def __getattr__(self, name: str) -> object: 

97 return getattr(self._fp, name) 

98 

99 @property 

100 def limiter(self) -> CapacityLimiter | None: 

101 """The capacity limiter used by this file object, if not the global limiter.""" 

102 return self._limiter 

103 

104 @property 

105 def wrapped(self) -> IO[AnyStr]: 

106 """The wrapped file object.""" 

107 return self._fp 

108 

109 async def __aiter__(self) -> AsyncIterator[AnyStr]: 

110 while True: 

111 line = await self.readline() 

112 if line: 

113 yield line 

114 else: 

115 break 

116 

117 async def aclose(self) -> None: 

118 with CancelScope(shield=True): 

119 await to_thread.run_sync(self._fp.close, limiter=self._limiter) 

120 

121 async def read(self, size: int = -1) -> AnyStr: 

122 return await to_thread.run_sync(self._fp.read, size, limiter=self._limiter) 

123 

124 async def read1(self: AsyncFile[bytes], size: int = -1) -> bytes: 

125 return await to_thread.run_sync(self._fp.read1, size, limiter=self._limiter) 

126 

127 async def readline(self) -> AnyStr: 

128 return await to_thread.run_sync(self._fp.readline, limiter=self._limiter) 

129 

130 async def readlines(self) -> list[AnyStr]: 

131 return await to_thread.run_sync(self._fp.readlines, limiter=self._limiter) 

132 

133 async def readinto(self: AsyncFile[bytes], b: WriteableBuffer) -> int: 

134 return await to_thread.run_sync(self._fp.readinto, b, limiter=self._limiter) 

135 

136 async def readinto1(self: AsyncFile[bytes], b: WriteableBuffer) -> int: 

137 return await to_thread.run_sync(self._fp.readinto1, b, limiter=self._limiter) 

138 

139 @overload 

140 async def write(self: AsyncFile[bytes], b: ReadableBuffer) -> int: ... 

141 

142 @overload 

143 async def write(self: AsyncFile[str], b: str) -> int: ... 

144 

145 async def write(self, b: ReadableBuffer | str) -> int: 

146 return await to_thread.run_sync(self._fp.write, b, limiter=self._limiter) 

147 

148 @overload 

149 async def writelines( 

150 self: AsyncFile[bytes], lines: Iterable[ReadableBuffer] 

151 ) -> None: ... 

152 

153 @overload 

154 async def writelines(self: AsyncFile[str], lines: Iterable[str]) -> None: ... 

155 

156 async def writelines(self, lines: Iterable[ReadableBuffer] | Iterable[str]) -> None: 

157 return await to_thread.run_sync( 

158 self._fp.writelines, lines, limiter=self._limiter 

159 ) 

160 

161 async def truncate(self, size: int | None = None) -> int: 

162 return await to_thread.run_sync(self._fp.truncate, size, limiter=self._limiter) 

163 

164 async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int: 

165 return await to_thread.run_sync( 

166 self._fp.seek, offset, whence, limiter=self._limiter 

167 ) 

168 

169 async def tell(self) -> int: 

170 return await to_thread.run_sync(self._fp.tell, limiter=self._limiter) 

171 

172 async def flush(self) -> None: 

173 return await to_thread.run_sync(self._fp.flush, limiter=self._limiter) 

174 

175 

176@overload 

177async def open_file( 

178 file: str | PathLike[str] | int, 

179 mode: OpenBinaryMode, 

180 buffering: int = ..., 

181 encoding: str | None = ..., 

182 errors: str | None = ..., 

183 newline: str | None = ..., 

184 closefd: bool = ..., 

185 opener: Callable[[str, int], int] | None = ..., 

186 *, 

187 limiter: CapacityLimiter | None = ..., 

188) -> AsyncFile[bytes]: ... 

189 

190 

191@overload 

192async def open_file( 

193 file: str | PathLike[str] | int, 

194 mode: OpenTextMode = ..., 

195 buffering: int = ..., 

196 encoding: str | None = ..., 

197 errors: str | None = ..., 

198 newline: str | None = ..., 

199 closefd: bool = ..., 

200 opener: Callable[[str, int], int] | None = ..., 

201 *, 

202 limiter: CapacityLimiter | None = ..., 

203) -> AsyncFile[str]: ... 

204 

205 

206async def open_file( 

207 file: str | PathLike[str] | int, 

208 mode: str = "r", 

209 buffering: int = -1, 

210 encoding: str | None = None, 

211 errors: str | None = None, 

212 newline: str | None = None, 

213 closefd: bool = True, 

214 opener: Callable[[str, int], int] | None = None, 

215 *, 

216 limiter: CapacityLimiter | None = None, 

217) -> AsyncFile[Any]: 

218 """ 

219 Open a file asynchronously. 

220 

221 Except for ``limiter``, the arguments are exactly the same as for the builtin :func:`open`. 

222 

223 :param limiter: an optional capacity limiter to use with the file 

224 instead of the default one 

225 :return: an asynchronous file object 

226 

227 .. versionchanged:: 4.14.0 

228 Added the ``limiter`` keyword argument. 

229 

230 """ 

231 fp = await to_thread.run_sync( 

232 open, 

233 file, 

234 mode, 

235 buffering, 

236 encoding, 

237 errors, 

238 newline, 

239 closefd, 

240 opener, 

241 limiter=limiter, 

242 ) 

243 return AsyncFile(fp, limiter=limiter) 

244 

245 

246def wrap_file( 

247 file: IO[AnyStr], *, limiter: CapacityLimiter | None = None 

248) -> AsyncFile[AnyStr]: 

249 """ 

250 Wrap an existing file as an asynchronous file. 

251 

252 :param file: an existing file-like object 

253 :param limiter: an optional capacity limiter to use with the file 

254 instead of the default one 

255 :return: an asynchronous file object 

256 

257 .. versionchanged:: 4.14.0 

258 Added the ``limiter`` keyword argument. 

259 

260 """ 

261 return AsyncFile(file, limiter=limiter) 

262 

263 

264@dataclass(eq=False) 

265class _PathIterator(AsyncIterator[T]): 

266 iterator: Iterator[PathLike[str]] 

267 limiter: CapacityLimiter | None 

268 # This was added to ensure that iterating over a subclass of Path yields instances 

269 # of that subclass rather than the base Path class. 

270 path_cls: type[T] 

271 

272 async def __anext__(self) -> T: 

273 nextval = await to_thread.run_sync( 

274 next, self.iterator, None, abandon_on_cancel=True, limiter=self.limiter 

275 ) 

276 if nextval is None: 

277 raise StopAsyncIteration from None 

278 

279 return self.path_cls(nextval, limiter=self.limiter) 

280 

281 

282class Path: 

283 """ 

284 An asynchronous version of :class:`pathlib.Path`. 

285 

286 This class cannot be substituted for :class:`pathlib.Path` or 

287 :class:`pathlib.PurePath`, but it is compatible with the :class:`os.PathLike` 

288 interface. 

289 

290 It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for 

291 the deprecated :meth:`~pathlib.Path.link_to` method. 

292 

293 Some methods may be unavailable or have limited functionality, based on the Python 

294 version: 

295 

296 * :meth:`~pathlib.Path.copy` (available on Python 3.14 or later) 

297 * :meth:`~pathlib.Path.copy_into` (available on Python 3.14 or later) 

298 * :meth:`~pathlib.Path.from_uri` (available on Python 3.13 or later) 

299 * :meth:`~pathlib.PurePath.full_match` (available on Python 3.13 or later) 

300 * :attr:`~pathlib.Path.info` (available on Python 3.14 or later) 

301 * :meth:`~pathlib.Path.is_junction` (available on Python 3.12 or later) 

302 * :meth:`~pathlib.PurePath.match` (the ``case_sensitive`` parameter is only 

303 available on Python 3.13 or later) 

304 * :meth:`~pathlib.Path.move` (available on Python 3.14 or later) 

305 * :meth:`~pathlib.Path.move_into` (available on Python 3.14 or later) 

306 * :meth:`~pathlib.PurePath.relative_to` (the ``walk_up`` parameter is only available 

307 on Python 3.12 or later) 

308 * :meth:`~pathlib.Path.walk` (available on Python 3.12 or later) 

309 

310 Any methods that do disk I/O need to be awaited on. These methods are: 

311 

312 * :meth:`~pathlib.Path.absolute` 

313 * :meth:`~pathlib.Path.chmod` 

314 * :meth:`~pathlib.Path.cwd` 

315 * :meth:`~pathlib.Path.exists` 

316 * :meth:`~pathlib.Path.expanduser` 

317 * :meth:`~pathlib.Path.group` 

318 * :meth:`~pathlib.Path.hardlink_to` 

319 * :meth:`~pathlib.Path.home` 

320 * :meth:`~pathlib.Path.is_block_device` 

321 * :meth:`~pathlib.Path.is_char_device` 

322 * :meth:`~pathlib.Path.is_dir` 

323 * :meth:`~pathlib.Path.is_fifo` 

324 * :meth:`~pathlib.Path.is_file` 

325 * :meth:`~pathlib.Path.is_junction` 

326 * :meth:`~pathlib.Path.is_mount` 

327 * :meth:`~pathlib.Path.is_socket` 

328 * :meth:`~pathlib.Path.is_symlink` 

329 * :meth:`~pathlib.Path.lchmod` 

330 * :meth:`~pathlib.Path.lstat` 

331 * :meth:`~pathlib.Path.mkdir` 

332 * :meth:`~pathlib.Path.open` 

333 * :meth:`~pathlib.Path.owner` 

334 * :meth:`~pathlib.Path.read_bytes` 

335 * :meth:`~pathlib.Path.read_text` 

336 * :meth:`~pathlib.Path.readlink` 

337 * :meth:`~pathlib.Path.rename` 

338 * :meth:`~pathlib.Path.replace` 

339 * :meth:`~pathlib.Path.resolve` 

340 * :meth:`~pathlib.Path.rmdir` 

341 * :meth:`~pathlib.Path.samefile` 

342 * :meth:`~pathlib.Path.stat` 

343 * :meth:`~pathlib.Path.symlink_to` 

344 * :meth:`~pathlib.Path.touch` 

345 * :meth:`~pathlib.Path.unlink` 

346 * :meth:`~pathlib.Path.walk` 

347 * :meth:`~pathlib.Path.write_bytes` 

348 * :meth:`~pathlib.Path.write_text` 

349 

350 Additionally, the following methods return an async iterator yielding 

351 :class:`~.Path` objects: 

352 

353 * :meth:`~pathlib.Path.glob` 

354 * :meth:`~pathlib.Path.iterdir` 

355 * :meth:`~pathlib.Path.rglob` 

356 

357 .. versionchanged:: 4.14.0 

358 Added the ``limiter`` keyword argument. 

359 """ 

360 

361 __slots__ = "__weakref__", "_limiter", "_path" 

362 

363 __weakref__: Any 

364 

365 def __init__( 

366 self, *args: str | PathLike[str], limiter: CapacityLimiter | None = None 

367 ) -> None: 

368 if limiter is not None and not isinstance(limiter, CapacityLimiter): 

369 raise TypeError( 

370 f"limiter must be a CapacityLimiter or None, not " 

371 f"{limiter.__class__.__name__}" 

372 ) 

373 

374 self._path: Final[pathlib.Path] = pathlib.Path(*args) 

375 self._limiter = limiter 

376 

377 def __fspath__(self) -> str: 

378 return self._path.__fspath__() 

379 

380 if sys.version_info >= (3, 15): 

381 

382 def __vfspath__(self) -> str: 

383 return self._path.__vfspath__() 

384 

385 def __str__(self) -> str: 

386 return self._path.__str__() 

387 

388 def __repr__(self) -> str: 

389 return f"{self.__class__.__name__}({self.as_posix()!r})" 

390 

391 def __bytes__(self) -> bytes: 

392 return self._path.__bytes__() 

393 

394 def __hash__(self) -> int: 

395 return self._path.__hash__() 

396 

397 def __eq__(self, other: object) -> bool: 

398 target = other._path if isinstance(other, Path) else other 

399 return self._path.__eq__(target) 

400 

401 def __lt__(self, other: pathlib.PurePath | Path) -> bool: 

402 target = other._path if isinstance(other, Path) else other 

403 return self._path.__lt__(target) 

404 

405 def __le__(self, other: pathlib.PurePath | Path) -> bool: 

406 target = other._path if isinstance(other, Path) else other 

407 return self._path.__le__(target) 

408 

409 def __gt__(self, other: pathlib.PurePath | Path) -> bool: 

410 target = other._path if isinstance(other, Path) else other 

411 return self._path.__gt__(target) 

412 

413 def __ge__(self, other: pathlib.PurePath | Path) -> bool: 

414 target = other._path if isinstance(other, Path) else other 

415 return self._path.__ge__(target) 

416 

417 def __truediv__(self, other: str | PathLike[str]) -> Self: 

418 return type(self)(self._path / other, limiter=self._limiter) 

419 

420 def __rtruediv__(self, other: str | PathLike[str]) -> Self: 

421 return type(self)(other, limiter=self._limiter) / self 

422 

423 @property 

424 def limiter(self) -> CapacityLimiter | None: 

425 """The capacity limiter used by this path, if not the global limiter.""" 

426 return self._limiter 

427 

428 @property 

429 def parts(self) -> tuple[str, ...]: 

430 return self._path.parts 

431 

432 @property 

433 def drive(self) -> str: 

434 return self._path.drive 

435 

436 @property 

437 def root(self) -> str: 

438 return self._path.root 

439 

440 @property 

441 def anchor(self) -> str: 

442 return self._path.anchor 

443 

444 @property 

445 def parents(self) -> Sequence[Self]: 

446 return tuple(type(self)(p, limiter=self._limiter) for p in self._path.parents) 

447 

448 @property 

449 def parent(self) -> Self: 

450 return type(self)(self._path.parent, limiter=self._limiter) 

451 

452 @property 

453 def name(self) -> str: 

454 return self._path.name 

455 

456 @property 

457 def suffix(self) -> str: 

458 return self._path.suffix 

459 

460 @property 

461 def suffixes(self) -> list[str]: 

462 return self._path.suffixes 

463 

464 @property 

465 def stem(self) -> str: 

466 return self._path.stem 

467 

468 async def absolute(self) -> Self: 

469 path = await to_thread.run_sync(self._path.absolute, limiter=self._limiter) 

470 return type(self)(path, limiter=self._limiter) 

471 

472 def as_posix(self) -> str: 

473 return self._path.as_posix() 

474 

475 def as_uri(self) -> str: 

476 return self._path.as_uri() 

477 

478 if sys.version_info >= (3, 13): 

479 parser: ClassVar[ModuleType] = pathlib.Path.parser 

480 

481 @classmethod 

482 def from_uri(cls, uri: str, *, limiter: CapacityLimiter | None = None) -> Self: 

483 return cls(pathlib.Path.from_uri(uri), limiter=limiter) 

484 

485 def full_match( 

486 self, path_pattern: str, *, case_sensitive: bool | None = None 

487 ) -> bool: 

488 return self._path.full_match(path_pattern, case_sensitive=case_sensitive) 

489 

490 def match( 

491 self, path_pattern: str, *, case_sensitive: bool | None = None 

492 ) -> bool: 

493 return self._path.match(path_pattern, case_sensitive=case_sensitive) 

494 else: 

495 

496 def match(self, path_pattern: str) -> bool: 

497 return self._path.match(path_pattern) 

498 

499 if sys.version_info >= (3, 14): 

500 

501 @property 

502 def info(self) -> PathInfo: 

503 return self._path.info 

504 

505 async def copy( 

506 self, 

507 target: str | os.PathLike[str], 

508 *, 

509 follow_symlinks: bool = True, 

510 preserve_metadata: bool = False, 

511 ) -> Self: 

512 func = partial( 

513 self._path.copy, 

514 follow_symlinks=follow_symlinks, 

515 preserve_metadata=preserve_metadata, 

516 ) 

517 return type(self)( 

518 await to_thread.run_sync( 

519 func, pathlib.Path(target), limiter=self._limiter 

520 ), 

521 limiter=self._limiter, 

522 ) 

523 

524 async def copy_into( 

525 self, 

526 target_dir: str | os.PathLike[str], 

527 *, 

528 follow_symlinks: bool = True, 

529 preserve_metadata: bool = False, 

530 ) -> Self: 

531 func = partial( 

532 self._path.copy_into, 

533 follow_symlinks=follow_symlinks, 

534 preserve_metadata=preserve_metadata, 

535 ) 

536 return type(self)( 

537 await to_thread.run_sync( 

538 func, pathlib.Path(target_dir), limiter=self._limiter 

539 ), 

540 limiter=self._limiter, 

541 ) 

542 

543 async def move(self, target: str | os.PathLike[str]) -> Self: 

544 # Upstream does not handle anyio.Path properly as a PathLike 

545 target = pathlib.Path(target) 

546 return type(self)( 

547 await to_thread.run_sync( 

548 self._path.move, target, limiter=self._limiter 

549 ), 

550 limiter=self._limiter, 

551 ) 

552 

553 async def move_into( 

554 self, 

555 target_dir: str | os.PathLike[str], 

556 ) -> Self: 

557 return type(self)( 

558 await to_thread.run_sync( 

559 self._path.move_into, target_dir, limiter=self._limiter 

560 ), 

561 limiter=self._limiter, 

562 ) 

563 

564 def is_relative_to(self, other: str | PathLike[str]) -> bool: 

565 try: 

566 self.relative_to(other) 

567 return True 

568 except ValueError: 

569 return False 

570 

571 async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None: 

572 func = partial(os.chmod, follow_symlinks=follow_symlinks) 

573 return await to_thread.run_sync(func, self._path, mode, limiter=self._limiter) 

574 

575 @classmethod 

576 async def cwd(cls, *, limiter: CapacityLimiter | None = None) -> Self: 

577 path = await to_thread.run_sync(pathlib.Path.cwd, limiter=limiter) 

578 return cls(path, limiter=limiter) 

579 

580 if sys.version_info >= (3, 12): 

581 

582 async def exists(self, *, follow_symlinks: bool = True) -> bool: 

583 return await to_thread.run_sync( 

584 partial(self._path.exists, follow_symlinks=follow_symlinks), 

585 abandon_on_cancel=True, 

586 limiter=self._limiter, 

587 ) 

588 

589 else: 

590 

591 async def exists(self) -> bool: 

592 return await to_thread.run_sync( 

593 self._path.exists, abandon_on_cancel=True, limiter=self._limiter 

594 ) 

595 

596 async def expanduser(self) -> Self: 

597 return type(self)( 

598 await to_thread.run_sync( 

599 self._path.expanduser, abandon_on_cancel=True, limiter=self._limiter 

600 ), 

601 limiter=self._limiter, 

602 ) 

603 

604 if sys.version_info < (3, 12): 

605 # Python 3.11 and earlier 

606 def glob(self, pattern: str) -> AsyncIterator[Self]: 

607 gen = self._path.glob(pattern) 

608 return _PathIterator(gen, self._limiter, type(self)) 

609 elif (3, 12) <= sys.version_info < (3, 13): 

610 # changed in Python 3.12: 

611 # - The case_sensitive parameter was added. 

612 def glob( 

613 self, 

614 pattern: str, 

615 *, 

616 case_sensitive: bool | None = None, 

617 ) -> AsyncIterator[Self]: 

618 gen = self._path.glob(pattern, case_sensitive=case_sensitive) 

619 return _PathIterator(gen, self._limiter, type(self)) 

620 elif sys.version_info >= (3, 13): 

621 # Changed in Python 3.13: 

622 # - The recurse_symlinks parameter was added. 

623 # - The pattern parameter accepts a path-like object. 

624 def glob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block 

625 self, 

626 pattern: str | PathLike[str], 

627 *, 

628 case_sensitive: bool | None = None, 

629 recurse_symlinks: bool = False, 

630 ) -> AsyncIterator[Self]: 

631 gen = self._path.glob( 

632 pattern, # type: ignore[arg-type] 

633 case_sensitive=case_sensitive, 

634 recurse_symlinks=recurse_symlinks, 

635 ) 

636 return _PathIterator(gen, self._limiter, type(self)) 

637 

638 if sys.version_info >= (3, 13): 

639 

640 async def group(self, *, follow_symlinks: bool = True) -> str: 

641 return await to_thread.run_sync( 

642 partial(self._path.group, follow_symlinks=follow_symlinks), 

643 abandon_on_cancel=True, 

644 limiter=self._limiter, 

645 ) 

646 

647 else: 

648 

649 async def group(self) -> str: 

650 return await to_thread.run_sync( 

651 self._path.group, abandon_on_cancel=True, limiter=self._limiter 

652 ) 

653 

654 async def hardlink_to( 

655 self, target: str | bytes | PathLike[str] | PathLike[bytes] 

656 ) -> None: 

657 if isinstance(target, Path): 

658 target = target._path 

659 

660 await to_thread.run_sync(os.link, target, self, limiter=self._limiter) 

661 

662 @classmethod 

663 async def home(cls, *, limiter: CapacityLimiter | None = None) -> Self: 

664 home_path = await to_thread.run_sync(pathlib.Path.home, limiter=limiter) 

665 return cls(home_path, limiter=limiter) 

666 

667 def is_absolute(self) -> bool: 

668 return self._path.is_absolute() 

669 

670 async def is_block_device(self) -> bool: 

671 return await to_thread.run_sync( 

672 self._path.is_block_device, abandon_on_cancel=True, limiter=self._limiter 

673 ) 

674 

675 async def is_char_device(self) -> bool: 

676 return await to_thread.run_sync( 

677 self._path.is_char_device, abandon_on_cancel=True, limiter=self._limiter 

678 ) 

679 

680 if sys.version_info >= (3, 13): 

681 

682 async def is_dir(self, *, follow_symlinks: bool = True) -> bool: 

683 return await to_thread.run_sync( 

684 partial(self._path.is_dir, follow_symlinks=follow_symlinks), 

685 abandon_on_cancel=True, 

686 limiter=self._limiter, 

687 ) 

688 

689 else: 

690 

691 async def is_dir(self) -> bool: 

692 return await to_thread.run_sync( 

693 self._path.is_dir, abandon_on_cancel=True, limiter=self._limiter 

694 ) 

695 

696 async def is_fifo(self) -> bool: 

697 return await to_thread.run_sync( 

698 self._path.is_fifo, abandon_on_cancel=True, limiter=self._limiter 

699 ) 

700 

701 if sys.version_info >= (3, 13): 

702 

703 async def is_file(self, *, follow_symlinks: bool = True) -> bool: 

704 return await to_thread.run_sync( 

705 partial(self._path.is_file, follow_symlinks=follow_symlinks), 

706 abandon_on_cancel=True, 

707 limiter=self._limiter, 

708 ) 

709 

710 else: 

711 

712 async def is_file(self) -> bool: 

713 return await to_thread.run_sync( 

714 self._path.is_file, abandon_on_cancel=True, limiter=self._limiter 

715 ) 

716 

717 if sys.version_info >= (3, 12): 

718 

719 async def is_junction(self) -> bool: 

720 return await to_thread.run_sync( 

721 self._path.is_junction, limiter=self._limiter 

722 ) 

723 

724 async def is_mount(self) -> bool: 

725 return await to_thread.run_sync( 

726 os.path.ismount, self._path, abandon_on_cancel=True, limiter=self._limiter 

727 ) 

728 

729 if sys.version_info < (3, 15): 

730 

731 def is_reserved(self) -> bool: 

732 return self._path.is_reserved() 

733 

734 async def is_socket(self) -> bool: 

735 return await to_thread.run_sync( 

736 self._path.is_socket, abandon_on_cancel=True, limiter=self._limiter 

737 ) 

738 

739 async def is_symlink(self) -> bool: 

740 return await to_thread.run_sync( 

741 self._path.is_symlink, abandon_on_cancel=True, limiter=self._limiter 

742 ) 

743 

744 async def iterdir(self) -> AsyncIterator[Self]: 

745 gen = ( 

746 self._path.iterdir() 

747 if sys.version_info < (3, 13) 

748 else await to_thread.run_sync( 

749 self._path.iterdir, abandon_on_cancel=True, limiter=self._limiter 

750 ) 

751 ) 

752 async for path in _PathIterator(gen, self._limiter, type(self)): 

753 yield path 

754 

755 def joinpath(self, *args: str | PathLike[str]) -> Self: 

756 return type(self)(self._path.joinpath(*args), limiter=self._limiter) 

757 

758 async def lchmod(self, mode: int) -> None: 

759 await to_thread.run_sync(self._path.lchmod, mode, limiter=self._limiter) 

760 

761 async def lstat(self) -> os.stat_result: 

762 return await to_thread.run_sync( 

763 self._path.lstat, abandon_on_cancel=True, limiter=self._limiter 

764 ) 

765 

766 async def mkdir( 

767 self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False 

768 ) -> None: 

769 await to_thread.run_sync( 

770 self._path.mkdir, mode, parents, exist_ok, limiter=self._limiter 

771 ) 

772 

773 @overload 

774 async def open( 

775 self, 

776 mode: OpenBinaryMode, 

777 buffering: int = ..., 

778 encoding: str | None = ..., 

779 errors: str | None = ..., 

780 newline: str | None = ..., 

781 ) -> AsyncFile[bytes]: ... 

782 

783 @overload 

784 async def open( 

785 self, 

786 mode: OpenTextMode = ..., 

787 buffering: int = ..., 

788 encoding: str | None = ..., 

789 errors: str | None = ..., 

790 newline: str | None = ..., 

791 ) -> AsyncFile[str]: ... 

792 

793 async def open( 

794 self, 

795 mode: str = "r", 

796 buffering: int = -1, 

797 encoding: str | None = None, 

798 errors: str | None = None, 

799 newline: str | None = None, 

800 ) -> AsyncFile[Any]: 

801 fp = await to_thread.run_sync( 

802 self._path.open, 

803 mode, 

804 buffering, 

805 encoding, 

806 errors, 

807 newline, 

808 limiter=self._limiter, 

809 ) 

810 return AsyncFile(fp, limiter=self._limiter) 

811 

812 if sys.version_info >= (3, 13): 

813 

814 async def owner(self, *, follow_symlinks: bool = True) -> str: 

815 return await to_thread.run_sync( 

816 partial(self._path.owner, follow_symlinks=follow_symlinks), 

817 abandon_on_cancel=True, 

818 limiter=self._limiter, 

819 ) 

820 

821 else: 

822 

823 async def owner(self) -> str: 

824 return await to_thread.run_sync( 

825 self._path.owner, abandon_on_cancel=True, limiter=self._limiter 

826 ) 

827 

828 async def read_bytes(self) -> bytes: 

829 return await to_thread.run_sync(self._path.read_bytes, limiter=self._limiter) 

830 

831 if sys.version_info >= (3, 13): 

832 

833 async def read_text( 

834 self, 

835 encoding: str | None = None, 

836 errors: str | None = None, 

837 newline: str | None = None, 

838 ) -> str: 

839 return await to_thread.run_sync( 

840 self._path.read_text, 

841 encoding, 

842 errors, 

843 newline, 

844 limiter=self._limiter, 

845 ) 

846 

847 else: 

848 

849 async def read_text( 

850 self, encoding: str | None = None, errors: str | None = None 

851 ) -> str: 

852 return await to_thread.run_sync( 

853 self._path.read_text, encoding, errors, limiter=self._limiter 

854 ) 

855 

856 if sys.version_info >= (3, 12): 

857 

858 def relative_to( 

859 self, *other: str | PathLike[str], walk_up: bool = False 

860 ) -> Self: 

861 # relative_to() should work with any PathLike but it doesn't 

862 others = [pathlib.Path(other) for other in other] 

863 return type(self)( 

864 self._path.relative_to(*others, walk_up=walk_up), limiter=self._limiter 

865 ) 

866 

867 else: 

868 

869 def relative_to(self, *other: str | PathLike[str]) -> Self: 

870 return type(self)(self._path.relative_to(*other), limiter=self._limiter) 

871 

872 async def readlink(self) -> Self: 

873 target = await to_thread.run_sync( 

874 os.readlink, self._path, limiter=self._limiter 

875 ) 

876 return type(self)(target, limiter=self._limiter) 

877 

878 async def rename(self, target: str | pathlib.PurePath | Path) -> Self: 

879 if isinstance(target, Path): 

880 target = target._path 

881 

882 await to_thread.run_sync(self._path.rename, target, limiter=self._limiter) 

883 return type(self)(target, limiter=self._limiter) 

884 

885 async def replace(self, target: str | pathlib.PurePath | Path) -> Self: 

886 if isinstance(target, Path): 

887 target = target._path 

888 

889 await to_thread.run_sync(self._path.replace, target, limiter=self._limiter) 

890 return type(self)(target, limiter=self._limiter) 

891 

892 async def resolve(self, strict: bool = False) -> Self: 

893 func = partial(self._path.resolve, strict=strict) 

894 return type(self)( 

895 await to_thread.run_sync( 

896 func, abandon_on_cancel=True, limiter=self._limiter 

897 ), 

898 limiter=self._limiter, 

899 ) 

900 

901 if sys.version_info < (3, 12): 

902 # Pre Python 3.12 

903 def rglob(self, pattern: str) -> AsyncIterator[Self]: 

904 gen = self._path.rglob(pattern) 

905 return _PathIterator(gen, self._limiter, type(self)) 

906 elif (3, 12) <= sys.version_info < (3, 13): 

907 # Changed in Python 3.12: 

908 # - The case_sensitive parameter was added. 

909 def rglob( 

910 self, pattern: str, *, case_sensitive: bool | None = None 

911 ) -> AsyncIterator[Self]: 

912 gen = self._path.rglob(pattern, case_sensitive=case_sensitive) 

913 return _PathIterator(gen, self._limiter, type(self)) 

914 elif sys.version_info >= (3, 13): 

915 # Changed in Python 3.13: 

916 # - The recurse_symlinks parameter was added. 

917 # - The pattern parameter accepts a path-like object. 

918 def rglob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block 

919 self, 

920 pattern: str | PathLike[str], 

921 *, 

922 case_sensitive: bool | None = None, 

923 recurse_symlinks: bool = False, 

924 ) -> AsyncIterator[Self]: 

925 gen = self._path.rglob( 

926 pattern, # type: ignore[arg-type] 

927 case_sensitive=case_sensitive, 

928 recurse_symlinks=recurse_symlinks, 

929 ) 

930 return _PathIterator(gen, self._limiter, type(self)) 

931 

932 async def rmdir(self) -> None: 

933 await to_thread.run_sync(self._path.rmdir, limiter=self._limiter) 

934 

935 async def samefile(self, other_path: str | PathLike[str]) -> bool: 

936 if isinstance(other_path, Path): 

937 other_path = other_path._path 

938 

939 return await to_thread.run_sync( 

940 self._path.samefile, 

941 other_path, 

942 abandon_on_cancel=True, 

943 limiter=self._limiter, 

944 ) 

945 

946 async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result: 

947 func = partial(os.stat, follow_symlinks=follow_symlinks) 

948 return await to_thread.run_sync( 

949 func, self._path, abandon_on_cancel=True, limiter=self._limiter 

950 ) 

951 

952 async def symlink_to( 

953 self, 

954 target: str | bytes | PathLike[str] | PathLike[bytes], 

955 target_is_directory: bool = False, 

956 ) -> None: 

957 if isinstance(target, Path): 

958 target = target._path 

959 

960 await to_thread.run_sync( 

961 self._path.symlink_to, target, target_is_directory, limiter=self._limiter 

962 ) 

963 

964 async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: 

965 await to_thread.run_sync( 

966 self._path.touch, mode, exist_ok, limiter=self._limiter 

967 ) 

968 

969 async def unlink(self, missing_ok: bool = False) -> None: 

970 try: 

971 await to_thread.run_sync(self._path.unlink, limiter=self._limiter) 

972 except FileNotFoundError: 

973 if not missing_ok: 

974 raise 

975 

976 if sys.version_info >= (3, 12): 

977 

978 async def walk( 

979 self, 

980 top_down: bool = True, 

981 on_error: Callable[[OSError], object] | None = None, 

982 follow_symlinks: bool = False, 

983 ) -> AsyncIterator[tuple[Self, list[str], list[str]]]: 

984 def get_next_value() -> tuple[pathlib.Path, list[str], list[str]] | None: 

985 try: 

986 return next(gen) 

987 except StopIteration: 

988 return None 

989 

990 gen = self._path.walk(top_down, on_error, follow_symlinks) 

991 while True: 

992 value = await to_thread.run_sync(get_next_value, limiter=self._limiter) 

993 if value is None: 

994 return 

995 

996 root, dirs, paths = value 

997 yield type(self)(root, limiter=self._limiter), dirs, paths 

998 

999 def with_name(self, name: str) -> Self: 

1000 return type(self)(self._path.with_name(name), limiter=self._limiter) 

1001 

1002 if sys.version_info < (3, 13): 

1003 # Backport pathlib's Python>=3.13 behavior for empty stems on paths with non-empty suffixes. 

1004 # See: https://github.com/python/cpython/pull/114612 

1005 def with_stem(self, stem: str) -> Self: 

1006 suffix = self._path.suffix 

1007 if not suffix: 

1008 return self.with_name(stem) 

1009 elif not stem: 

1010 # If the suffix is non-empty, we can't make the stem empty. 

1011 raise ValueError(f"{self!r} has a non-empty suffix") 

1012 else: 

1013 return self.with_name(stem + suffix) 

1014 else: 

1015 

1016 def with_stem(self, stem: str) -> Self: 

1017 return type(self)(self._path.with_stem(stem), limiter=self._limiter) 

1018 

1019 def with_suffix(self, suffix: str) -> Self: 

1020 return type(self)(self._path.with_suffix(suffix), limiter=self._limiter) 

1021 

1022 def with_segments(self, *pathsegments: str | PathLike[str]) -> Self: 

1023 return type(self)(*pathsegments, limiter=self._limiter) 

1024 

1025 async def write_bytes(self, data: ReadableBuffer) -> int: 

1026 return await to_thread.run_sync( 

1027 self._path.write_bytes, data, limiter=self._limiter 

1028 ) 

1029 

1030 async def write_text( 

1031 self, 

1032 data: str, 

1033 encoding: str | None = None, 

1034 errors: str | None = None, 

1035 newline: str | None = None, 

1036 ) -> int: 

1037 return await to_thread.run_sync( 

1038 self._path.write_text, 

1039 data, 

1040 encoding, 

1041 errors, 

1042 newline, 

1043 limiter=self._limiter, 

1044 ) 

1045 

1046 

1047PathLike.register(Path)