Skip to content

Commit

Permalink
Merge branch 'master' into enable-pyupgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
CoolCat467 authored Oct 19, 2023
2 parents c31e0a6 + b324b3a commit bccd2ed
Show file tree
Hide file tree
Showing 21 changed files with 815 additions and 270 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
Expand All @@ -14,7 +14,7 @@ repos:
hooks:
- id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.292
rev: v0.1.0
hooks:
- id: ruff
types: [file]
Expand Down
14 changes: 14 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ def autodoc_process_signature(
def setup(app):
app.add_css_file("hackrtd.css")
app.connect("autodoc-process-signature", autodoc_process_signature)
# After Intersphinx runs, add additional mappings.
app.connect("builder-inited", add_intersphinx, priority=1000)


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


def add_intersphinx(app) -> None:
"""Add some specific intersphinx mappings."""
# This has been removed in Py3.12, so add a link to the 3.11 version with deprecation warnings.
app.builder.env.intersphinx_inventory["py:method"]["pathlib.Path.link_to"] = (
"Python",
"3.11",
"https://docs.python.org/3.11/library/pathlib.html#pathlib.Path.link_to",
"-",
)


autodoc_member_order = "bysource"

# Add any paths that contain templates here, relative to this directory.
Expand Down
19 changes: 19 additions & 0 deletions docs/source/reference-core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1823,6 +1823,25 @@ to spawn a child thread, and then use a :ref:`memory channel

.. literalinclude:: reference-core/from-thread-example.py

.. note::

The ``from_thread.run*`` functions reuse the host task that called
:func:`trio.to_thread.run_sync` to run your provided function, as long as you're
using the default ``cancellable=False`` so Trio can be sure that the task will remain
around to perform the work. If you pass ``cancellable=True`` at the outset, or if
you provide a :class:`~trio.lowlevel.TrioToken` when calling back in to Trio, your
functions will be executed in a new system task. Therefore, the
:func:`~trio.lowlevel.current_task`, :func:`current_effective_deadline`, or other
task-tree specific values may differ depending on keyword argument values.

You can also use :func:`trio.from_thread.check_cancelled` to check for cancellation from
a thread that was spawned by :func:`trio.to_thread.run_sync`. If the call to
:func:`~trio.to_thread.run_sync` was cancelled (even if ``cancellable=False``!), then
:func:`~trio.from_thread.check_cancelled` will raise :func:`trio.Cancelled`.
It's like ``trio.from_thread.run(trio.sleep, 0)``, but much faster.

.. autofunction:: trio.from_thread.check_cancelled

Threads and task-local storage
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
5 changes: 5 additions & 0 deletions newsfragments/2392.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
If called from a thread spawned by `trio.to_thread.run_sync`, `trio.from_thread.run` and
`trio.from_thread.run_sync` now reuse the task and cancellation status of the host task;
this means that context variables and cancel scopes naturally propagate 'through'
threads spawned by Trio. You can also use `trio.from_thread.check_cancelled`
to efficiently check for cancellation without reentering the Trio thread.
1 change: 1 addition & 0 deletions newsfragments/543.headline.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add type hints.
5 changes: 0 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,6 @@ disallow_untyped_calls = false
# files not yet fully typed
[[tool.mypy.overrides]]
module = [
# internal
"trio/_windows_pipes",

# tests
"trio/testing/_fake_net",
"trio/_core/_tests/test_guest_mode",
Expand All @@ -106,7 +103,6 @@ module = [
"trio/_tests/test_highlevel_open_unix_stream",
"trio/_tests/test_highlevel_serve_listeners",
"trio/_tests/test_highlevel_ssl_helpers",
"trio/_tests/test_path",
"trio/_tests/test_scheduler_determinism",
"trio/_tests/test_ssl",
"trio/_tests/test_subprocess",
Expand All @@ -117,7 +113,6 @@ module = [
"trio/_tests/test_tracing",
"trio/_tests/test_util",
"trio/_tests/test_wait_for_object",
"trio/_tests/test_windows_pipes",
"trio/_tests/tools/test_gen_exports",
]
check_untyped_defs = false
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
keywords=["async", "io", "networking", "trio"],
classifiers=[
"Development Status :: 3 - Alpha",
"Framework :: Trio",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"License :: OSI Approved :: Apache Software License",
Expand All @@ -120,7 +121,7 @@
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: System :: Networking",
"Framework :: Trio",
"Typing :: Typed",
],
project_urls={
"Documentation": "https://trio.readthedocs.io/",
Expand Down
6 changes: 3 additions & 3 deletions trio/_file_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,8 @@ def tell(self) -> int: ...
class _CanTruncate(Protocol):
def truncate(self, size: int | None = ..., /) -> int: ...

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

class _CanWriteLines(Protocol[T_contra]):
# The lines parameter varies for bytes/str, so use a typevar to make the async match.
Expand Down Expand Up @@ -334,7 +334,7 @@ async def readlines(self: AsyncIOWrapper[_CanReadLines[AnyStr]]) -> list[AnyStr]
async def seek(self: AsyncIOWrapper[_CanSeek], target: int, whence: int = 0, /) -> int: ...
async def tell(self: AsyncIOWrapper[_CanTell]) -> int: ...
async def truncate(self: AsyncIOWrapper[_CanTruncate], size: int | None = None, /) -> int: ...
async def write(self: AsyncIOWrapper[_CanWrite[AnyStr]], data: AnyStr, /) -> int: ...
async def write(self: AsyncIOWrapper[_CanWrite[T]], data: T, /) -> int: ...
async def writelines(self: AsyncIOWrapper[_CanWriteLines[T]], lines: Iterable[T], /) -> None: ...
async def readinto1(self: AsyncIOWrapper[_CanReadInto1], buffer: Buffer, /) -> int: ...
async def peek(self: AsyncIOWrapper[_CanPeek[AnyStr]], size: int = 0, /) -> AnyStr: ...
Expand Down
143 changes: 92 additions & 51 deletions trio/_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pathlib
import sys
import types
from collections.abc import Awaitable, Callable, Iterable
from collections.abc import Awaitable, Callable, Iterable, Sequence
from functools import partial
from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
from typing import (
Expand Down Expand Up @@ -245,7 +245,7 @@ def __fspath__(self) -> str:
return os.fspath(self._wrapped)

@overload
def open(
async def open(
self,
mode: OpenTextMode = "r",
buffering: int = -1,
Expand All @@ -256,7 +256,7 @@ def open(
...

@overload
def open(
async def open(
self,
mode: OpenBinaryMode,
buffering: Literal[0],
Expand All @@ -267,7 +267,7 @@ def open(
...

@overload
def open(
async def open(
self,
mode: OpenBinaryModeUpdating,
buffering: Literal[-1, 1] = -1,
Expand All @@ -278,7 +278,7 @@ def open(
...

@overload
def open(
async def open(
self,
mode: OpenBinaryModeWriting,
buffering: Literal[-1, 1] = -1,
Expand All @@ -289,7 +289,7 @@ def open(
...

@overload
def open(
async def open(
self,
mode: OpenBinaryModeReading,
buffering: Literal[-1, 1] = -1,
Expand All @@ -300,7 +300,7 @@ def open(
...

@overload
def open(
async def open(
self,
mode: OpenBinaryMode,
buffering: int = -1,
Expand All @@ -311,7 +311,7 @@ def open(
...

@overload
def open(
async def open(
self,
mode: str,
buffering: int = -1,
Expand All @@ -338,11 +338,53 @@ async def open(self, *args: Any, **kwargs: Any) -> _AsyncIOWrapper[IO[Any]]:
def __bytes__(self) -> bytes: ...
def __truediv__(self, other: StrPath) -> Path: ...
def __rtruediv__(self, other: StrPath) -> Path: ...
def __lt__(self, other: Path | pathlib.PurePath) -> bool: ...
def __le__(self, other: Path | pathlib.PurePath) -> bool: ...
def __gt__(self, other: Path | pathlib.PurePath) -> bool: ...
def __ge__(self, other: Path | pathlib.PurePath) -> bool: ...

# The following are ordered the same as in typeshed.

# Properties produced by __getattr__() - all synchronous.
@property
def parts(self) -> tuple[str, ...]: ...
@property
def drive(self) -> str: ...
@property
def root(self) -> str: ...
@property
def anchor(self) -> str: ...
@property
def name(self) -> str: ...
@property
def suffix(self) -> str: ...
@property
def suffixes(self) -> list[str]: ...
@property
def stem(self) -> str: ...
@property
def parents(self) -> Sequence[pathlib.Path]: ... # TODO: Convert these to trio Paths?
@property
def parent(self) -> Path: ...

# PurePath methods - synchronous.
def as_posix(self) -> str: ...
def as_uri(self) -> str: ...
def is_absolute(self) -> bool: ...
def is_reserved(self) -> bool: ...
def match(self, path_pattern: str) -> bool: ...
def relative_to(self, *other: StrPath) -> Path: ...
def with_name(self, name: str) -> Path: ...
def with_suffix(self, suffix: str) -> Path: ...
def joinpath(self, *other: StrPath) -> Path: ...

# wrapped methods handled by __getattr__
async def absolute(self) -> Path: ...
async def as_posix(self) -> str: ...
async def as_uri(self) -> str: ...
if sys.version_info >= (3, 9):
def is_relative_to(self, *other: StrPath) -> bool: ...
def with_stem(self, stem: str) -> Path: ...

# pathlib.Path methods and properties - async.
@classmethod
async def cwd(self) -> Path: ...

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

@classmethod
async def cwd(self) -> Path: ...

async def exists(self) -> bool: ...
async def expanduser(self) -> Path: ...
async def glob(self, pattern: str) -> Iterable[Path]: ...
async def home(self) -> Path: ...
async def is_absolute(self) -> bool: ...
async def is_block_device(self) -> bool: ...
async def is_char_device(self) -> bool: ...
async def is_dir(self) -> bool: ...
async def is_fifo(self) -> bool: ...
async def is_file(self) -> bool: ...
async def is_reserved(self) -> bool: ...
async def is_socket(self) -> bool: ...
async def is_symlink(self) -> bool: ...
async def is_socket(self) -> bool: ...
async def is_fifo(self) -> bool: ...
async def is_block_device(self) -> bool: ...
async def is_char_device(self) -> bool: ...
async def iterdir(self) -> Iterable[Path]: ...
async def joinpath(self, *other: StrPath) -> Path: ...
async def lchmod(self, mode: int) -> None: ...
async def lstat(self) -> os.stat_result: ...
async def match(self, path_pattern: str) -> bool: ...
async def mkdir(self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False) -> None: ...
async def read_bytes(self) -> bytes: ...
async def read_text(self, encoding: str | None = None, errors: str | None = None) -> str: ...
async def relative_to(self, *other: StrPath) -> Path: ...

if sys.platform != "win32":
async def owner(self) -> str: ...
async def group(self) -> str: ...
async def is_mount(self) -> bool: ...
if sys.version_info >= (3, 9):
async def readlink(self) -> Path: ...
if sys.version_info >= (3, 8):
def rename(self, target: str | pathlib.PurePath) -> Path: ...
def replace(self, target: str | pathlib.PurePath) -> Path: ...
async def rename(self, target: StrPath) -> Path: ...
async def replace(self, target: StrPath) -> Path: ...
else:
def rename(self, target: str | pathlib.PurePath) -> None: ...
def replace(self, target: str | pathlib.PurePath) -> None: ...

async def rename(self, target: StrPath) -> None: ...
async def replace(self, target: StrPath) -> None: ...
async def resolve(self, strict: bool = False) -> Path: ...
async def rglob(self, pattern: str) -> Iterable[Path]: ...
async def rmdir(self) -> None: ...
async def samefile(self, other_path: str | bytes | int | Path) -> bool: ...
async def symlink_to(self, target: str | Path, target_is_directory: bool = False) -> None: ...
async def symlink_to(self, target: StrPath, target_is_directory: bool = False) -> None: ...
if sys.version_info >= (3, 10):
async def hardlink_to(self, target: str | pathlib.Path) -> None: ...
async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: ...
if sys.version_info >= (3, 8):
def unlink(self, missing_ok: bool = False) -> None: ...
async def unlink(self, missing_ok: bool = False) -> None: ...
else:
def unlink(self) -> None: ...
async def with_name(self, name: str) -> Path: ...
async def with_suffix(self, suffix: str) -> Path: ...
async def unlink(self) -> None: ...
@classmethod
async def home(self) -> Path: ...
async def absolute(self) -> Path: ...
async def expanduser(self) -> Path: ...
async def read_bytes(self) -> bytes: ...
async def read_text(self, encoding: str | None = None, errors: str | None = None) -> str: ...
async def samefile(self, other_path: bytes | int | StrPath) -> bool: ...
async def write_bytes(self, data: bytes) -> int: ...

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

if sys.platform != "win32":
async def owner(self) -> str: ...
async def group(self) -> str: ...
async def is_mount(self) -> bool: ...

if sys.version_info >= (3, 9):
async def is_relative_to(self, *other: StrPath) -> bool: ...
async def with_stem(self, stem: str) -> Path: ...
async def readlink(self) -> Path: ...
if sys.version_info >= (3, 10):
async def hardlink_to(self, target: str | pathlib.Path) -> None: ...
if sys.version_info < (3, 12):
async def link_to(self, target: StrPath | bytes) -> None: ...
if sys.version_info >= (3, 12):
Expand All @@ -432,7 +462,7 @@ async def with_segments(self, *pathsegments: StrPath) -> Path: ...


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

if sys.version_info < (3, 12):
# Since we synthesise methods from the stdlib, this automatically will
# have deprecation warnings, and disappear entirely in 3.12+.
Path.link_to.__doc__ = """
Like Python 3.8-3.11's :meth:`~pathlib.Path.link_to`, but async.
:deprecated: This method was deprecated in Python 3.10 and entirely \
removed in 3.12. Use :meth:`hardlink_to` instead which has \
a more meaningful parameter order.
"""

# The value of Path.absolute.__doc__ makes a reference to
# :meth:~pathlib.Path.absolute, which does not exist. Removing this makes more
# sense than inventing our own special docstring for this.
Expand Down
Loading

0 comments on commit bccd2ed

Please sign in to comment.