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

371 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 

31 

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

33 from typing import Self 

34else: 

35 from typing_extensions import Self 

36 

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

38 from pathlib.types import PathInfo 

39 

40if TYPE_CHECKING: 

41 from types import ModuleType 

42 

43 from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer 

44else: 

45 ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object 

46 

47 

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

49 

50 

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

52 """ 

53 An asynchronous file object. 

54 

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): 

57 

58 * read 

59 * read1 

60 * readline 

61 * readlines 

62 * readinto 

63 * readinto1 

64 * write 

65 * writelines 

66 * truncate 

67 * seek 

68 * tell 

69 * flush 

70 

71 All other methods are directly passed through. 

72 

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

74 underlying file at the end of the context block. 

75 

76 This class also supports asynchronous iteration:: 

77 

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

79 async for line in f: 

80 print(line) 

81 """ 

82 

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 ) 

91 

92 self._fp: Any = fp 

93 self._limiter = limiter 

94 

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

96 return getattr(self._fp, name) 

97 

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 

102 

103 @property 

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

105 """The wrapped file object.""" 

106 return self._fp 

107 

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 

115 

116 async def aclose(self) -> None: 

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

118 

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

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

121 

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) 

124 

125 async def readline(self) -> AnyStr: 

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

127 

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

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

130 

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

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

133 

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

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

136 

137 @overload 

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

139 

140 @overload 

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

142 

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

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

145 

146 @overload 

147 async def writelines( 

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

149 ) -> None: ... 

150 

151 @overload 

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

153 

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 ) 

158 

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

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

161 

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 ) 

166 

167 async def tell(self) -> int: 

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

169 

170 async def flush(self) -> None: 

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

172 

173 

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]: ... 

187 

188 

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]: ... 

202 

203 

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. 

218 

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

220 

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

222 instead of the default one 

223 :return: an asynchronous file object 

224 

225 .. versionchanged:: 4.14.0 

226 Added the ``limiter`` keyword argument. 

227 

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) 

242 

243 

244def wrap_file( 

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

246) -> AsyncFile[AnyStr]: 

247 """ 

248 Wrap an existing file as an asynchronous file. 

249 

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 

254 

255 .. versionchanged:: 4.14.0 

256 Added the ``limiter`` keyword argument. 

257 

258 """ 

259 return AsyncFile(file, limiter=limiter) 

260 

261 

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] 

269 

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 

276 

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

278 

279 

280class Path: 

281 """ 

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

283 

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. 

287 

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

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

290 

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

292 version: 

293 

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) 

307 

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

309 

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` 

347 

348 Additionally, the following methods return an async iterator yielding 

349 :class:`~.Path` objects: 

350 

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

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

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

354 

355 .. versionchanged:: 4.14.0 

356 Added the ``limiter`` keyword argument. 

357 """ 

358 

359 __slots__ = "_path", "_limiter", "__weakref__" 

360 

361 __weakref__: Any 

362 

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 ) 

371 

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

373 self._limiter = limiter 

374 

375 def __fspath__(self) -> str: 

376 return self._path.__fspath__() 

377 

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

379 

380 def __vfspath__(self) -> str: 

381 return self._path.__vfspath__() 

382 

383 def __str__(self) -> str: 

384 return self._path.__str__() 

385 

386 def __repr__(self) -> str: 

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

388 

389 def __bytes__(self) -> bytes: 

390 return self._path.__bytes__() 

391 

392 def __hash__(self) -> int: 

393 return self._path.__hash__() 

394 

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

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

397 return self._path.__eq__(target) 

398 

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) 

402 

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) 

406 

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) 

410 

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) 

414 

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

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

417 

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

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

420 

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 

425 

426 @property 

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

428 return self._path.parts 

429 

430 @property 

431 def drive(self) -> str: 

432 return self._path.drive 

433 

434 @property 

435 def root(self) -> str: 

436 return self._path.root 

437 

438 @property 

439 def anchor(self) -> str: 

440 return self._path.anchor 

441 

442 @property 

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

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

445 

446 @property 

447 def parent(self) -> Self: 

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

449 

450 @property 

451 def name(self) -> str: 

452 return self._path.name 

453 

454 @property 

455 def suffix(self) -> str: 

456 return self._path.suffix 

457 

458 @property 

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

460 return self._path.suffixes 

461 

462 @property 

463 def stem(self) -> str: 

464 return self._path.stem 

465 

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) 

469 

470 def as_posix(self) -> str: 

471 return self._path.as_posix() 

472 

473 def as_uri(self) -> str: 

474 return self._path.as_uri() 

475 

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

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

478 

479 @classmethod 

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

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

482 

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) 

487 

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: 

493 

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

495 return self._path.match(path_pattern) 

496 

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

498 

499 @property 

500 def info(self) -> PathInfo: 

501 return self._path.info 

502 

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 ) 

521 

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 ) 

540 

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 ) 

550 

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 ) 

561 

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 

568 

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) 

572 

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) 

577 

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 ) 

582 

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 ) 

590 

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)) 

624 

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 ) 

629 

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 

635 

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

637 

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) 

642 

643 def is_absolute(self) -> bool: 

644 return self._path.is_absolute() 

645 

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 ) 

650 

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 ) 

655 

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 ) 

660 

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 ) 

665 

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 ) 

670 

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

672 

673 async def is_junction(self) -> bool: 

674 return await to_thread.run_sync( 

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

676 ) 

677 

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 ) 

682 

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

684 

685 def is_reserved(self) -> bool: 

686 return self._path.is_reserved() 

687 

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 ) 

692 

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 ) 

697 

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 

708 

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

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

711 

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

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

714 

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 ) 

719 

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 ) 

726 

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]: ... 

736 

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]: ... 

746 

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) 

765 

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 ) 

770 

771 async def read_bytes(self) -> bytes: 

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

773 

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 ) 

780 

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

782 

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 ) 

791 

792 else: 

793 

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

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

796 

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) 

802 

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

804 if isinstance(target, Path): 

805 target = target._path 

806 

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

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

809 

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

811 if isinstance(target, Path): 

812 target = target._path 

813 

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

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

816 

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 ) 

825 

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)) 

856 

857 async def rmdir(self) -> None: 

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

859 

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

861 if isinstance(other_path, Path): 

862 other_path = other_path._path 

863 

864 return await to_thread.run_sync( 

865 self._path.samefile, 

866 other_path, 

867 abandon_on_cancel=True, 

868 limiter=self._limiter, 

869 ) 

870 

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 ) 

876 

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 

884 

885 await to_thread.run_sync( 

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

887 ) 

888 

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 ) 

893 

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 

900 

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

902 

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 

914 

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 

920 

921 root, dirs, paths = value 

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

923 

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

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

926 

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 ) 

931 

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

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

934 

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

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

937 

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 ) 

942 

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 ) 

958 

959 

960PathLike.register(Path)