Skip to content

Commit ab092b0

Browse files
Merge branch 'master' into from_thread_check_cancelled
2 parents 96e45c5 + b161fec commit ab092b0

14 files changed

+371
-139
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.4.0
3+
rev: v4.5.0
44
hooks:
55
- id: trailing-whitespace
66
- id: end-of-file-fixer
@@ -14,7 +14,7 @@ repos:
1414
hooks:
1515
- id: black
1616
- repo: https://github.com/astral-sh/ruff-pre-commit
17-
rev: v0.0.292
17+
rev: v0.1.0
1818
hooks:
1919
- id: ruff
2020
types: [file]

docs/source/conf.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ def autodoc_process_signature(
102102
def setup(app):
103103
app.add_css_file("hackrtd.css")
104104
app.connect("autodoc-process-signature", autodoc_process_signature)
105+
# After Intersphinx runs, add additional mappings.
106+
app.connect("builder-inited", add_intersphinx, priority=1000)
105107

106108

107109
# -- General configuration ------------------------------------------------
@@ -131,6 +133,18 @@ def setup(app):
131133
"sniffio": ("https://sniffio.readthedocs.io/en/latest/", None),
132134
}
133135

136+
137+
def add_intersphinx(app) -> None:
138+
"""Add some specific intersphinx mappings."""
139+
# This has been removed in Py3.12, so add a link to the 3.11 version with deprecation warnings.
140+
app.builder.env.intersphinx_inventory["py:method"]["pathlib.Path.link_to"] = (
141+
"Python",
142+
"3.11",
143+
"https://docs.python.org/3.11/library/pathlib.html#pathlib.Path.link_to",
144+
"-",
145+
)
146+
147+
134148
autodoc_member_order = "bysource"
135149

136150
# Add any paths that contain templates here, relative to this directory.

pyproject.toml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,6 @@ disallow_untyped_calls = false
8383
# files not yet fully typed
8484
[[tool.mypy.overrides]]
8585
module = [
86-
# internal
87-
"trio/_windows_pipes",
88-
8986
# tests
9087
"trio/testing/_fake_net",
9188
"trio/_core/_tests/test_guest_mode",
@@ -105,7 +102,6 @@ module = [
105102
"trio/_tests/test_highlevel_open_unix_stream",
106103
"trio/_tests/test_highlevel_serve_listeners",
107104
"trio/_tests/test_highlevel_ssl_helpers",
108-
"trio/_tests/test_path",
109105
"trio/_tests/test_scheduler_determinism",
110106
"trio/_tests/test_ssl",
111107
"trio/_tests/test_subprocess",
@@ -116,7 +112,6 @@ module = [
116112
"trio/_tests/test_tracing",
117113
"trio/_tests/test_util",
118114
"trio/_tests/test_wait_for_object",
119-
"trio/_tests/test_windows_pipes",
120115
"trio/_tests/tools/test_gen_exports",
121116
]
122117
check_untyped_defs = false

trio/_file_io.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,8 @@ def tell(self) -> int: ...
195195
class _CanTruncate(Protocol):
196196
def truncate(self, size: int | None = ..., /) -> int: ...
197197

198-
class _CanWrite(Protocol[AnyStr_contra]):
199-
def write(self, data: AnyStr_contra, /) -> int: ...
198+
class _CanWrite(Protocol[T_contra]):
199+
def write(self, data: T_contra, /) -> int: ...
200200

201201
class _CanWriteLines(Protocol[T_contra]):
202202
# The lines parameter varies for bytes/str, so use a typevar to make the async match.
@@ -334,7 +334,7 @@ async def readlines(self: AsyncIOWrapper[_CanReadLines[AnyStr]]) -> list[AnyStr]
334334
async def seek(self: AsyncIOWrapper[_CanSeek], target: int, whence: int = 0, /) -> int: ...
335335
async def tell(self: AsyncIOWrapper[_CanTell]) -> int: ...
336336
async def truncate(self: AsyncIOWrapper[_CanTruncate], size: int | None = None, /) -> int: ...
337-
async def write(self: AsyncIOWrapper[_CanWrite[AnyStr]], data: AnyStr, /) -> int: ...
337+
async def write(self: AsyncIOWrapper[_CanWrite[T]], data: T, /) -> int: ...
338338
async def writelines(self: AsyncIOWrapper[_CanWriteLines[T]], lines: Iterable[T], /) -> None: ...
339339
async def readinto1(self: AsyncIOWrapper[_CanReadInto1], buffer: Buffer, /) -> int: ...
340340
async def peek(self: AsyncIOWrapper[_CanPeek[AnyStr]], size: int = 0, /) -> AnyStr: ...

trio/_path.py

Lines changed: 92 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import pathlib
66
import sys
77
import types
8-
from collections.abc import Awaitable, Callable, Iterable
8+
from collections.abc import Awaitable, Callable, Iterable, Sequence
99
from functools import partial
1010
from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
1111
from typing import (
@@ -245,7 +245,7 @@ def __fspath__(self) -> str:
245245
return os.fspath(self._wrapped)
246246

247247
@overload
248-
def open(
248+
async def open(
249249
self,
250250
mode: OpenTextMode = "r",
251251
buffering: int = -1,
@@ -256,7 +256,7 @@ def open(
256256
...
257257

258258
@overload
259-
def open(
259+
async def open(
260260
self,
261261
mode: OpenBinaryMode,
262262
buffering: Literal[0],
@@ -267,7 +267,7 @@ def open(
267267
...
268268

269269
@overload
270-
def open(
270+
async def open(
271271
self,
272272
mode: OpenBinaryModeUpdating,
273273
buffering: Literal[-1, 1] = -1,
@@ -278,7 +278,7 @@ def open(
278278
...
279279

280280
@overload
281-
def open(
281+
async def open(
282282
self,
283283
mode: OpenBinaryModeWriting,
284284
buffering: Literal[-1, 1] = -1,
@@ -289,7 +289,7 @@ def open(
289289
...
290290

291291
@overload
292-
def open(
292+
async def open(
293293
self,
294294
mode: OpenBinaryModeReading,
295295
buffering: Literal[-1, 1] = -1,
@@ -300,7 +300,7 @@ def open(
300300
...
301301

302302
@overload
303-
def open(
303+
async def open(
304304
self,
305305
mode: OpenBinaryMode,
306306
buffering: int = -1,
@@ -311,7 +311,7 @@ def open(
311311
...
312312

313313
@overload
314-
def open(
314+
async def open(
315315
self,
316316
mode: str,
317317
buffering: int = -1,
@@ -338,11 +338,53 @@ async def open(self, *args: Any, **kwargs: Any) -> _AsyncIOWrapper[IO[Any]]:
338338
def __bytes__(self) -> bytes: ...
339339
def __truediv__(self, other: StrPath) -> Path: ...
340340
def __rtruediv__(self, other: StrPath) -> Path: ...
341+
def __lt__(self, other: Path | pathlib.PurePath) -> bool: ...
342+
def __le__(self, other: Path | pathlib.PurePath) -> bool: ...
343+
def __gt__(self, other: Path | pathlib.PurePath) -> bool: ...
344+
def __ge__(self, other: Path | pathlib.PurePath) -> bool: ...
345+
346+
# The following are ordered the same as in typeshed.
347+
348+
# Properties produced by __getattr__() - all synchronous.
349+
@property
350+
def parts(self) -> tuple[str, ...]: ...
351+
@property
352+
def drive(self) -> str: ...
353+
@property
354+
def root(self) -> str: ...
355+
@property
356+
def anchor(self) -> str: ...
357+
@property
358+
def name(self) -> str: ...
359+
@property
360+
def suffix(self) -> str: ...
361+
@property
362+
def suffixes(self) -> list[str]: ...
363+
@property
364+
def stem(self) -> str: ...
365+
@property
366+
def parents(self) -> Sequence[pathlib.Path]: ... # TODO: Convert these to trio Paths?
367+
@property
368+
def parent(self) -> Path: ...
369+
370+
# PurePath methods - synchronous.
371+
def as_posix(self) -> str: ...
372+
def as_uri(self) -> str: ...
373+
def is_absolute(self) -> bool: ...
374+
def is_reserved(self) -> bool: ...
375+
def match(self, path_pattern: str) -> bool: ...
376+
def relative_to(self, *other: StrPath) -> Path: ...
377+
def with_name(self, name: str) -> Path: ...
378+
def with_suffix(self, suffix: str) -> Path: ...
379+
def joinpath(self, *other: StrPath) -> Path: ...
341380

342-
# wrapped methods handled by __getattr__
343-
async def absolute(self) -> Path: ...
344-
async def as_posix(self) -> str: ...
345-
async def as_uri(self) -> str: ...
381+
if sys.version_info >= (3, 9):
382+
def is_relative_to(self, *other: StrPath) -> bool: ...
383+
def with_stem(self, stem: str) -> Path: ...
384+
385+
# pathlib.Path methods and properties - async.
386+
@classmethod
387+
async def cwd(self) -> Path: ...
346388

347389
if sys.version_info >= (3, 10):
348390
async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result: ...
@@ -351,51 +393,50 @@ async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None: ...
351393
async def stat(self) -> os.stat_result: ...
352394
async def chmod(self, mode: int) -> None: ...
353395

354-
@classmethod
355-
async def cwd(self) -> Path: ...
356-
357396
async def exists(self) -> bool: ...
358-
async def expanduser(self) -> Path: ...
359397
async def glob(self, pattern: str) -> Iterable[Path]: ...
360-
async def home(self) -> Path: ...
361-
async def is_absolute(self) -> bool: ...
362-
async def is_block_device(self) -> bool: ...
363-
async def is_char_device(self) -> bool: ...
364398
async def is_dir(self) -> bool: ...
365-
async def is_fifo(self) -> bool: ...
366399
async def is_file(self) -> bool: ...
367-
async def is_reserved(self) -> bool: ...
368-
async def is_socket(self) -> bool: ...
369400
async def is_symlink(self) -> bool: ...
401+
async def is_socket(self) -> bool: ...
402+
async def is_fifo(self) -> bool: ...
403+
async def is_block_device(self) -> bool: ...
404+
async def is_char_device(self) -> bool: ...
370405
async def iterdir(self) -> Iterable[Path]: ...
371-
async def joinpath(self, *other: StrPath) -> Path: ...
372406
async def lchmod(self, mode: int) -> None: ...
373407
async def lstat(self) -> os.stat_result: ...
374-
async def match(self, path_pattern: str) -> bool: ...
375408
async def mkdir(self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False) -> None: ...
376-
async def read_bytes(self) -> bytes: ...
377-
async def read_text(self, encoding: str | None = None, errors: str | None = None) -> str: ...
378-
async def relative_to(self, *other: StrPath) -> Path: ...
379409

410+
if sys.platform != "win32":
411+
async def owner(self) -> str: ...
412+
async def group(self) -> str: ...
413+
async def is_mount(self) -> bool: ...
414+
if sys.version_info >= (3, 9):
415+
async def readlink(self) -> Path: ...
380416
if sys.version_info >= (3, 8):
381-
def rename(self, target: str | pathlib.PurePath) -> Path: ...
382-
def replace(self, target: str | pathlib.PurePath) -> Path: ...
417+
async def rename(self, target: StrPath) -> Path: ...
418+
async def replace(self, target: StrPath) -> Path: ...
383419
else:
384-
def rename(self, target: str | pathlib.PurePath) -> None: ...
385-
def replace(self, target: str | pathlib.PurePath) -> None: ...
386-
420+
async def rename(self, target: StrPath) -> None: ...
421+
async def replace(self, target: StrPath) -> None: ...
387422
async def resolve(self, strict: bool = False) -> Path: ...
388423
async def rglob(self, pattern: str) -> Iterable[Path]: ...
389424
async def rmdir(self) -> None: ...
390-
async def samefile(self, other_path: str | bytes | int | Path) -> bool: ...
391-
async def symlink_to(self, target: str | Path, target_is_directory: bool = False) -> None: ...
425+
async def symlink_to(self, target: StrPath, target_is_directory: bool = False) -> None: ...
426+
if sys.version_info >= (3, 10):
427+
async def hardlink_to(self, target: str | pathlib.Path) -> None: ...
392428
async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: ...
393429
if sys.version_info >= (3, 8):
394-
def unlink(self, missing_ok: bool = False) -> None: ...
430+
async def unlink(self, missing_ok: bool = False) -> None: ...
395431
else:
396-
def unlink(self) -> None: ...
397-
async def with_name(self, name: str) -> Path: ...
398-
async def with_suffix(self, suffix: str) -> Path: ...
432+
async def unlink(self) -> None: ...
433+
@classmethod
434+
async def home(self) -> Path: ...
435+
async def absolute(self) -> Path: ...
436+
async def expanduser(self) -> Path: ...
437+
async def read_bytes(self) -> bytes: ...
438+
async def read_text(self, encoding: str | None = None, errors: str | None = None) -> str: ...
439+
async def samefile(self, other_path: bytes | int | StrPath) -> bool: ...
399440
async def write_bytes(self, data: bytes) -> int: ...
400441

401442
if sys.version_info >= (3, 10):
@@ -412,17 +453,6 @@ async def write_text(
412453
errors: str | None = None,
413454
) -> int: ...
414455

415-
if sys.platform != "win32":
416-
async def owner(self) -> str: ...
417-
async def group(self) -> str: ...
418-
async def is_mount(self) -> bool: ...
419-
420-
if sys.version_info >= (3, 9):
421-
async def is_relative_to(self, *other: StrPath) -> bool: ...
422-
async def with_stem(self, stem: str) -> Path: ...
423-
async def readlink(self) -> Path: ...
424-
if sys.version_info >= (3, 10):
425-
async def hardlink_to(self, target: str | pathlib.Path) -> None: ...
426456
if sys.version_info < (3, 12):
427457
async def link_to(self, target: StrPath | bytes) -> None: ...
428458
if sys.version_info >= (3, 12):
@@ -432,7 +462,7 @@ async def with_segments(self, *pathsegments: StrPath) -> Path: ...
432462

433463

434464
Path.iterdir.__doc__ = """
435-
Like :meth:`pathlib.Path.iterdir`, but async.
465+
Like :meth:`~pathlib.Path.iterdir`, but async.
436466
437467
This is an async method that returns a synchronous iterator, so you
438468
use it like::
@@ -446,6 +476,17 @@ async def with_segments(self, *pathsegments: StrPath) -> Path: ...
446476
447477
"""
448478

479+
if sys.version_info < (3, 12):
480+
# Since we synthesise methods from the stdlib, this automatically will
481+
# have deprecation warnings, and disappear entirely in 3.12+.
482+
Path.link_to.__doc__ = """
483+
Like Python 3.8-3.11's :meth:`~pathlib.Path.link_to`, but async.
484+
485+
:deprecated: This method was deprecated in Python 3.10 and entirely \
486+
removed in 3.12. Use :meth:`hardlink_to` instead which has \
487+
a more meaningful parameter order.
488+
"""
489+
449490
# The value of Path.absolute.__doc__ makes a reference to
450491
# :meth:~pathlib.Path.absolute, which does not exist. Removing this makes more
451492
# sense than inventing our own special docstring for this.

trio/_tests/test_exports.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def iter_modules(
126126
# https://github.com/pypa/setuptools/issues/3274
127127
"ignore:module 'sre_constants' is deprecated:DeprecationWarning",
128128
)
129-
def test_static_tool_sees_all_symbols(tool, modname, tmpdir):
129+
def test_static_tool_sees_all_symbols(tool, modname, tmp_path):
130130
module = importlib.import_module(modname)
131131

132132
def no_underscores(symbols):
@@ -273,7 +273,7 @@ def no_underscores(symbols):
273273
@pytest.mark.parametrize("module_name", PUBLIC_MODULE_NAMES)
274274
@pytest.mark.parametrize("tool", ["jedi", "mypy"])
275275
def test_static_tool_sees_class_members(
276-
tool: str, module_name: str, tmpdir: Path
276+
tool: str, module_name: str, tmp_path: Path
277277
) -> None:
278278
module = PUBLIC_MODULES[PUBLIC_MODULE_NAMES.index(module_name)]
279279

@@ -494,9 +494,25 @@ def lookup_symbol(symbol):
494494
missing.remove("__aiter__")
495495
missing.remove("__anext__")
496496

497-
# intentionally hidden behind type guard
497+
# __getattr__ is intentionally hidden behind type guard. That hook then
498+
# forwards property accesses to PurePath, meaning these names aren't directly on
499+
# the class.
498500
if class_ == trio.Path:
499501
missing.remove("__getattr__")
502+
before = len(extra)
503+
extra -= {
504+
"anchor",
505+
"drive",
506+
"name",
507+
"parent",
508+
"parents",
509+
"parts",
510+
"root",
511+
"stem",
512+
"suffix",
513+
"suffixes",
514+
}
515+
assert len(extra) == before - 10
500516

501517
if missing or extra: # pragma: no cover
502518
errors[f"{module_name}.{class_name}"] = {

0 commit comments

Comments
 (0)