commit 1876632bc6a173fbc886446b1e7177128490f93b Author: tigro Date: Sat Jan 4 18:00:13 2025 +0300 import python-trio-0.23.1-6.el10 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fcfa290 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/trio-0.23.1.tar.gz diff --git a/.python-trio.metadata b/.python-trio.metadata new file mode 100644 index 0000000..de7b947 --- /dev/null +++ b/.python-trio.metadata @@ -0,0 +1 @@ +3e65818ecc4f708ed4faf0ebb2a3c62ef990b9b6 SOURCES/trio-0.23.1.tar.gz diff --git a/SOURCES/python3.13-PR-2959.patch b/SOURCES/python3.13-PR-2959.patch new file mode 100644 index 0000000..b4081fa --- /dev/null +++ b/SOURCES/python3.13-PR-2959.patch @@ -0,0 +1,846 @@ +From 86182cb193a71533a7a12b8d45a6ad4103d41ccc Mon Sep 17 00:00:00 2001 +From: Dave Hall +Date: Mon, 11 Mar 2024 21:35:12 +0000 +Subject: [PATCH] `Path` refactor (#2959) + +--- + docs/source/reference-io.rst | 5 + + newsfragments/2959.feature.rst | 5 + + trio/__init__.py | 2 +- + trio/_path.py | 537 ++++++++++----------------------- + trio/_tests/test_exports.py | 41 +-- + trio/_tests/test_path.py | 48 +-- + 6 files changed, 193 insertions(+), 445 deletions(-) + create mode 100644 newsfragments/2959.feature.rst + +diff --git a/docs/source/reference-io.rst b/docs/source/reference-io.rst +index d0525e3..45752c1 100644 +--- a/docs/source/reference-io.rst ++++ b/docs/source/reference-io.rst +@@ -631,6 +631,11 @@ Asynchronous path objects + + .. autoclass:: Path + :members: ++ :inherited-members: ++ ++.. autoclass:: PosixPath ++ ++.. autoclass:: WindowsPath + + + .. _async-file-objects: +diff --git a/newsfragments/2959.feature.rst b/newsfragments/2959.feature.rst +new file mode 100644 +index 0000000..ca90d56 +--- /dev/null ++++ b/newsfragments/2959.feature.rst +@@ -0,0 +1,5 @@ ++:class:`Path` is now a subclass of :class:`pathlib.PurePath`, allowing it to interoperate with other standard ++:mod:`pathlib` types. ++ ++Instantiating :class:`Path` now returns a concrete platform-specific subclass, one of :class:`PosixPath` or ++:class:`WindowsPath`, matching the behavior of :class:`pathlib.Path`. +diff --git a/trio/__init__.py b/trio/__init__.py +index 5adf146..8fdee02 100644 +--- a/trio/__init__.py ++++ b/trio/__init__.py +@@ -74,7 +74,7 @@ from ._highlevel_ssl_helpers import ( + open_ssl_over_tcp_stream as open_ssl_over_tcp_stream, + serve_ssl_over_tcp as serve_ssl_over_tcp, + ) +-from ._path import Path as Path ++from ._path import Path as Path, PosixPath as PosixPath, WindowsPath as WindowsPath + from ._signals import open_signal_receiver as open_signal_receiver + from ._ssl import ( + NeedHandshakeError as NeedHandshakeError, +diff --git a/trio/_path.py b/trio/_path.py +index 508ad5d..eb06a6b 100644 +--- a/trio/_path.py ++++ b/trio/_path.py +@@ -1,28 +1,18 @@ + from __future__ import annotations + +-import inspect + import os + import pathlib + import sys +-import types +-from collections.abc import Awaitable, Callable, Iterable, Sequence +-from functools import partial ++from collections.abc import Awaitable, Callable, Iterable + from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper +-from typing import ( +- IO, +- TYPE_CHECKING, +- Any, +- BinaryIO, +- ClassVar, +- TypeVar, +- Union, +- cast, +- overload, +-) +- +-import trio +-from trio._file_io import AsyncIOWrapper as _AsyncIOWrapper +-from trio._util import async_wraps, final, wraps ++from functools import partial, update_wrapper ++from inspect import cleandoc ++from typing import IO, TYPE_CHECKING, Any, BinaryIO, ClassVar, TypeVar, overload ++ ++from trio._file_io import AsyncIOWrapper, wrap_file ++from trio._util import final ++from trio.to_thread import run_sync ++ + + if TYPE_CHECKING: + from _typeshed import ( +@@ -32,217 +22,103 @@ if TYPE_CHECKING: + OpenBinaryModeWriting, + OpenTextMode, + ) +- from typing_extensions import Concatenate, Literal, ParamSpec, TypeAlias ++ from typing_extensions import Concatenate, Literal, ParamSpec, Self + + P = ParamSpec("P") + +-T = TypeVar("T") +-StrPath: TypeAlias = Union[str, "os.PathLike[str]"] # Only subscriptable in 3.9+ +- +- +-# re-wrap return value from methods that return new instances of pathlib.Path +-def rewrap_path(value: T) -> T | Path: +- if isinstance(value, pathlib.Path): +- return Path(value) +- else: +- return value ++ PathT = TypeVar("PathT", bound="Path") ++ T = TypeVar("T") + + +-def _forward_factory( +- cls: AsyncAutoWrapperType, +- attr_name: str, +- attr: Callable[Concatenate[pathlib.Path, P], T], +-) -> Callable[Concatenate[Path, P], T | Path]: +- @wraps(attr) +- def wrapper(self: Path, *args: P.args, **kwargs: P.kwargs) -> T | Path: +- attr = getattr(self._wrapped, attr_name) +- value = attr(*args, **kwargs) +- return rewrap_path(value) ++def _wraps_async( ++ wrapped: Callable[..., Any] ++) -> Callable[[Callable[P, T]], Callable[P, Awaitable[T]]]: ++ def decorator(fn: Callable[P, T]) -> Callable[P, Awaitable[T]]: ++ async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: ++ return await run_sync(partial(fn, *args, **kwargs)) + +- # Assigning this makes inspect and therefore Sphinx show the original parameters. +- # It's not defined on functions normally though, this is a custom attribute. +- assert isinstance(wrapper, types.FunctionType) +- wrapper.__signature__ = inspect.signature(attr) +- +- return wrapper ++ update_wrapper(wrapper, wrapped) ++ assert wrapped.__doc__ is not None ++ wrapper.__doc__ = ( ++ f"Like :meth:`~{wrapped.__module__}.{wrapped.__qualname__}`, but async.\n" ++ f"\n" ++ f"{cleandoc(wrapped.__doc__)}\n" ++ ) ++ return wrapper + ++ return decorator + +-def _forward_magic( +- cls: AsyncAutoWrapperType, attr: Callable[..., T] +-) -> Callable[..., Path | T]: +- sentinel = object() + +- @wraps(attr) +- def wrapper(self: Path, other: object = sentinel) -> Path | T: +- if other is sentinel: +- return attr(self._wrapped) +- if isinstance(other, cls): +- other = cast(Path, other)._wrapped +- value = attr(self._wrapped, other) +- return rewrap_path(value) ++def _wrap_method( ++ fn: Callable[Concatenate[pathlib.Path, P], T], ++) -> Callable[Concatenate[Path, P], Awaitable[T]]: ++ @_wraps_async(fn) ++ def wrapper(self: Path, /, *args: P.args, **kwargs: P.kwargs) -> T: ++ return fn(self._wrapped_cls(self), *args, **kwargs) + +- assert isinstance(wrapper, types.FunctionType) +- wrapper.__signature__ = inspect.signature(attr) + return wrapper + + +-def iter_wrapper_factory( +- cls: AsyncAutoWrapperType, meth_name: str +-) -> Callable[Concatenate[Path, P], Awaitable[Iterable[Path]]]: +- @async_wraps(cls, cls._wraps, meth_name) +- async def wrapper(self: Path, *args: P.args, **kwargs: P.kwargs) -> Iterable[Path]: +- meth = getattr(self._wrapped, meth_name) +- func = partial(meth, *args, **kwargs) +- # Make sure that the full iteration is performed in the thread +- # by converting the generator produced by pathlib into a list +- items = await trio.to_thread.run_sync(lambda: list(func())) +- return (rewrap_path(item) for item in items) ++def _wrap_method_path( ++ fn: Callable[Concatenate[pathlib.Path, P], pathlib.Path], ++) -> Callable[Concatenate[PathT, P], Awaitable[PathT]]: ++ @_wraps_async(fn) ++ def wrapper(self: PathT, /, *args: P.args, **kwargs: P.kwargs) -> PathT: ++ return self.__class__(fn(self._wrapped_cls(self), *args, **kwargs)) + + return wrapper + + +-def thread_wrapper_factory( +- cls: AsyncAutoWrapperType, meth_name: str +-) -> Callable[Concatenate[Path, P], Awaitable[Path]]: +- @async_wraps(cls, cls._wraps, meth_name) +- async def wrapper(self: Path, *args: P.args, **kwargs: P.kwargs) -> Path: +- meth = getattr(self._wrapped, meth_name) +- func = partial(meth, *args, **kwargs) +- value = await trio.to_thread.run_sync(func) +- return rewrap_path(value) +- ++def _wrap_method_path_iterable( ++ fn: Callable[Concatenate[pathlib.Path, P], Iterable[pathlib.Path]], ++) -> Callable[Concatenate[PathT, P], Awaitable[Iterable[PathT]]]: ++ @_wraps_async(fn) ++ def wrapper(self: PathT, /, *args: P.args, **kwargs: P.kwargs) -> Iterable[PathT]: ++ return map(self.__class__, [*fn(self._wrapped_cls(self), *args, **kwargs)]) ++ ++ assert wrapper.__doc__ is not None ++ wrapper.__doc__ += ( ++ f"\n" ++ f"This is an async method that returns a synchronous iterator, so you\n" ++ f"use it like:\n" ++ f"\n" ++ f".. code:: python\n" ++ f"\n" ++ f" for subpath in await mypath.{fn.__name__}():\n" ++ f" ...\n" ++ f"\n" ++ f".. note::\n" ++ f"\n" ++ f" The iterator is loaded into memory immediately during the initial\n" ++ f" call (see `issue #501\n" ++ f" `__ for discussion).\n" ++ ) + return wrapper + ++class Path(pathlib.PurePath): ++ """An async :class:`pathlib.Path` that executes blocking methods in :meth:`trio.to_thread.run_sync`. ++ Instantiating :class:`Path` returns a concrete platform-specific subclass, one of :class:`PosixPath` or ++ :class:`WindowsPath`. ++ """ + +-def classmethod_wrapper_factory( +- cls: AsyncAutoWrapperType, meth_name: str +-) -> classmethod: # type: ignore[type-arg] +- @async_wraps(cls, cls._wraps, meth_name) +- async def wrapper(cls: type[Path], *args: Any, **kwargs: Any) -> Path: # type: ignore[misc] # contains Any +- meth = getattr(cls._wraps, meth_name) +- func = partial(meth, *args, **kwargs) +- value = await trio.to_thread.run_sync(func) +- return rewrap_path(value) +- +- assert isinstance(wrapper, types.FunctionType) +- wrapper.__signature__ = inspect.signature(getattr(cls._wraps, meth_name)) +- return classmethod(wrapper) +- +- +-class AsyncAutoWrapperType(type): +- _forwards: type +- _wraps: type +- _forward_magic: list[str] +- _wrap_iter: list[str] +- _forward: list[str] +- +- def __init__( +- cls, name: str, bases: tuple[type, ...], attrs: dict[str, object] +- ) -> None: +- super().__init__(name, bases, attrs) +- +- cls._forward = [] +- type(cls).generate_forwards(cls, attrs) +- type(cls).generate_wraps(cls, attrs) +- type(cls).generate_magic(cls, attrs) +- type(cls).generate_iter(cls, attrs) +- +- def generate_forwards(cls, attrs: dict[str, object]) -> None: +- # forward functions of _forwards +- for attr_name, attr in cls._forwards.__dict__.items(): +- if attr_name.startswith("_") or attr_name in attrs: +- continue +- +- if isinstance(attr, property): +- cls._forward.append(attr_name) +- elif isinstance(attr, types.FunctionType): +- wrapper = _forward_factory(cls, attr_name, attr) +- setattr(cls, attr_name, wrapper) +- else: +- raise TypeError(attr_name, type(attr)) +- +- def generate_wraps(cls, attrs: dict[str, object]) -> None: +- # generate wrappers for functions of _wraps +- wrapper: classmethod | Callable[..., object] # type: ignore[type-arg] +- for attr_name, attr in cls._wraps.__dict__.items(): +- # .z. exclude cls._wrap_iter +- if attr_name.startswith("_") or attr_name in attrs: +- continue +- if isinstance(attr, classmethod): +- wrapper = classmethod_wrapper_factory(cls, attr_name) +- setattr(cls, attr_name, wrapper) +- elif isinstance(attr, types.FunctionType): +- wrapper = thread_wrapper_factory(cls, attr_name) +- assert isinstance(wrapper, types.FunctionType) +- wrapper.__signature__ = inspect.signature(attr) +- setattr(cls, attr_name, wrapper) +- else: +- raise TypeError(attr_name, type(attr)) +- +- def generate_magic(cls, attrs: dict[str, object]) -> None: +- # generate wrappers for magic +- for attr_name in cls._forward_magic: +- attr = getattr(cls._forwards, attr_name) +- wrapper = _forward_magic(cls, attr) +- setattr(cls, attr_name, wrapper) +- +- def generate_iter(cls, attrs: dict[str, object]) -> None: +- # generate wrappers for methods that return iterators +- wrapper: Callable[..., object] +- for attr_name, attr in cls._wraps.__dict__.items(): +- if attr_name in cls._wrap_iter: +- wrapper = iter_wrapper_factory(cls, attr_name) +- assert isinstance(wrapper, types.FunctionType) +- wrapper.__signature__ = inspect.signature(attr) +- setattr(cls, attr_name, wrapper) +- ++ __slots__ = () + +-@final +-class Path(metaclass=AsyncAutoWrapperType): +- """A :class:`pathlib.Path` wrapper that executes blocking methods in +- :meth:`trio.to_thread.run_sync`. ++ _wrapped_cls: ClassVar[type[pathlib.Path]] + +- """ ++ def __new__(cls, *args: str | os.PathLike[str]) -> Self: ++ if cls is Path: ++ cls = WindowsPath if os.name == "nt" else PosixPath # type: ignore[assignment] ++ return super().__new__(cls, *args) + +- _forward: ClassVar[list[str]] +- _wraps: ClassVar[type] = pathlib.Path +- _forwards: ClassVar[type] = pathlib.PurePath +- _forward_magic: ClassVar[list[str]] = [ +- "__str__", +- "__bytes__", +- "__truediv__", +- "__rtruediv__", +- "__eq__", +- "__lt__", +- "__le__", +- "__gt__", +- "__ge__", +- "__hash__", +- ] +- _wrap_iter: ClassVar[list[str]] = ["glob", "rglob", "iterdir"] +- +- def __init__(self, *args: StrPath) -> None: +- self._wrapped = pathlib.Path(*args) +- +- # type checkers allow accessing any attributes on class instances with `__getattr__` +- # so we hide it behind a type guard forcing it to rely on the hardcoded attribute +- # list below. +- if not TYPE_CHECKING: +- +- def __getattr__(self, name): +- if name in self._forward: +- value = getattr(self._wrapped, name) +- return rewrap_path(value) +- raise AttributeError(name) +- +- def __dir__(self) -> list[str]: +- return [*super().__dir__(), *self._forward] +- +- def __repr__(self) -> str: +- return f"trio.Path({str(self)!r})" ++ @classmethod ++ @_wraps_async(pathlib.Path.cwd) ++ def cwd(cls) -> Self: ++ return cls(pathlib.Path.cwd()) + +- def __fspath__(self) -> str: +- return os.fspath(self._wrapped) ++ @classmethod ++ @_wraps_async(pathlib.Path.home) ++ def home(cls) -> Self: ++ return cls(pathlib.Path.home()) + + @overload + async def open( +@@ -252,7 +128,7 @@ class Path(metaclass=AsyncAutoWrapperType): + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, +- ) -> _AsyncIOWrapper[TextIOWrapper]: ++ ) -> AsyncIOWrapper[TextIOWrapper]: + ... + + @overload +@@ -263,7 +139,7 @@ class Path(metaclass=AsyncAutoWrapperType): + encoding: None = None, + errors: None = None, + newline: None = None, +- ) -> _AsyncIOWrapper[FileIO]: ++ ) -> AsyncIOWrapper[FileIO]: + ... + + @overload +@@ -274,7 +150,7 @@ class Path(metaclass=AsyncAutoWrapperType): + encoding: None = None, + errors: None = None, + newline: None = None, +- ) -> _AsyncIOWrapper[BufferedRandom]: ++ ) -> AsyncIOWrapper[BufferedRandom]: + ... + + @overload +@@ -285,7 +161,7 @@ class Path(metaclass=AsyncAutoWrapperType): + encoding: None = None, + errors: None = None, + newline: None = None, +- ) -> _AsyncIOWrapper[BufferedWriter]: ++ ) -> AsyncIOWrapper[BufferedWriter]: + ... + + @overload +@@ -296,7 +172,7 @@ class Path(metaclass=AsyncAutoWrapperType): + encoding: None = None, + errors: None = None, + newline: None = None, +- ) -> _AsyncIOWrapper[BufferedReader]: ++ ) -> AsyncIOWrapper[BufferedReader]: + ... + + @overload +@@ -307,7 +183,7 @@ class Path(metaclass=AsyncAutoWrapperType): + encoding: None = None, + errors: None = None, + newline: None = None, +- ) -> _AsyncIOWrapper[BinaryIO]: ++ ) -> AsyncIOWrapper[BinaryIO]: + ... + + @overload +@@ -318,173 +194,74 @@ class Path(metaclass=AsyncAutoWrapperType): + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, +- ) -> _AsyncIOWrapper[IO[Any]]: ++ ) -> AsyncIOWrapper[IO[Any]]: + ... + +- @wraps(pathlib.Path.open) # type: ignore[misc] # Overload return mismatch. +- async def open(self, *args: Any, **kwargs: Any) -> _AsyncIOWrapper[IO[Any]]: +- """Open the file pointed to by the path, like the :func:`trio.open_file` +- function does. +- +- """ +- +- func = partial(self._wrapped.open, *args, **kwargs) +- value = await trio.to_thread.run_sync(func) +- return trio.wrap_file(value) +- +- if TYPE_CHECKING: +- # the dunders listed in _forward_magic that aren't seen otherwise +- # fmt: off +- 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: ... +- +- 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: ... +- async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None: ... +- else: +- async def stat(self) -> os.stat_result: ... +- async def chmod(self, mode: int) -> None: ... +- +- async def exists(self) -> bool: ... +- async def glob(self, pattern: str) -> Iterable[Path]: ... +- async def is_dir(self) -> bool: ... +- async def is_file(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 lchmod(self, mode: int) -> None: ... +- async def lstat(self) -> os.stat_result: ... +- async def mkdir(self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False) -> None: ... +- +- 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: ... +- async def rename(self, target: StrPath) -> Path: ... +- async def replace(self, target: StrPath) -> Path: ... +- async def resolve(self, strict: bool = False) -> Path: ... +- async def rglob(self, pattern: str) -> Iterable[Path]: ... +- async def rmdir(self) -> 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: ... +- async def unlink(self, missing_ok: bool = False) -> 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): +- async def write_text( +- self, data: str, +- encoding: str | None = None, +- errors: str | None = None, +- newline: str | None = None, +- ) -> int: ... +- else: +- async def write_text( +- self, data: str, +- encoding: str | None = None, +- errors: str | None = None, +- ) -> int: ... +- +- if sys.version_info < (3, 12): +- async def link_to(self, target: StrPath | bytes) -> None: ... +- if sys.version_info >= (3, 12): +- async def is_junction(self) -> bool: ... +- walk: Any # TODO +- async def with_segments(self, *pathsegments: StrPath) -> Path: ... +- +- +-Path.iterdir.__doc__ = """ +- Like :meth:`~pathlib.Path.iterdir`, but async. +- +- This is an async method that returns a synchronous iterator, so you +- use it like:: +- +- for subpath in await mypath.iterdir(): +- ... +- +- Note that it actually loads the whole directory list into memory +- immediately, during the initial call. (See `issue #501 +- `__ for discussion.) +- +-""" +- +-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. +-del Path.absolute.__doc__ +- +-# TODO: This is likely not supported by all the static tools out there, see discussion in +-# https://github.com/python-trio/trio/pull/2631#discussion_r1185612528 +-os.PathLike.register(Path) ++ @_wraps_async(pathlib.Path.open) # type: ignore[misc] # Overload return mismatch. ++ def open(self, *args: Any, **kwargs: Any) -> AsyncIOWrapper[IO[Any]]: ++ return wrap_file(self._wrapped_cls(self).open(*args, **kwargs)) ++ ++ def __repr__(self) -> str: ++ return f"trio.Path({str(self)!r})" ++ ++ stat = _wrap_method(pathlib.Path.stat) ++ chmod = _wrap_method(pathlib.Path.chmod) ++ exists = _wrap_method(pathlib.Path.exists) ++ glob = _wrap_method_path_iterable(pathlib.Path.glob) ++ rglob = _wrap_method_path_iterable(pathlib.Path.rglob) ++ is_dir = _wrap_method(pathlib.Path.is_dir) ++ is_file = _wrap_method(pathlib.Path.is_file) ++ is_symlink = _wrap_method(pathlib.Path.is_symlink) ++ is_socket = _wrap_method(pathlib.Path.is_socket) ++ is_fifo = _wrap_method(pathlib.Path.is_fifo) ++ is_block_device = _wrap_method(pathlib.Path.is_block_device) ++ is_char_device = _wrap_method(pathlib.Path.is_char_device) ++ if sys.version_info >= (3, 12): ++ is_junction = _wrap_method(pathlib.Path.is_junction) ++ iterdir = _wrap_method_path_iterable(pathlib.Path.iterdir) ++ lchmod = _wrap_method(pathlib.Path.lchmod) ++ lstat = _wrap_method(pathlib.Path.lstat) ++ mkdir = _wrap_method(pathlib.Path.mkdir) ++ if sys.platform != "win32": ++ owner = _wrap_method(pathlib.Path.owner) ++ group = _wrap_method(pathlib.Path.group) ++ if sys.platform != "win32" or sys.version_info >= (3, 12): ++ is_mount = _wrap_method(pathlib.Path.is_mount) ++ if sys.version_info >= (3, 9): ++ readlink = _wrap_method_path(pathlib.Path.readlink) ++ rename = _wrap_method_path(pathlib.Path.rename) ++ replace = _wrap_method_path(pathlib.Path.replace) ++ resolve = _wrap_method_path(pathlib.Path.resolve) ++ rmdir = _wrap_method(pathlib.Path.rmdir) ++ symlink_to = _wrap_method(pathlib.Path.symlink_to) ++ if sys.version_info >= (3, 10): ++ hardlink_to = _wrap_method(pathlib.Path.hardlink_to) ++ touch = _wrap_method(pathlib.Path.touch) ++ unlink = _wrap_method(pathlib.Path.unlink) ++ absolute = _wrap_method_path(pathlib.Path.absolute) ++ expanduser = _wrap_method_path(pathlib.Path.expanduser) ++ read_bytes = _wrap_method(pathlib.Path.read_bytes) ++ read_text = _wrap_method(pathlib.Path.read_text) ++ samefile = _wrap_method(pathlib.Path.samefile) ++ write_bytes = _wrap_method(pathlib.Path.write_bytes) ++ write_text = _wrap_method(pathlib.Path.write_text) ++ if sys.version_info < (3, 12): ++ link_to = _wrap_method(pathlib.Path.link_to) ++ ++ ++@final ++class PosixPath(Path, pathlib.PurePosixPath): ++ """An async :class:`pathlib.PosixPath` that executes blocking methods in :meth:`trio.to_thread.run_sync`.""" ++ ++ __slots__ = () ++ ++ _wrapped_cls: ClassVar[type[pathlib.Path]] = pathlib.PosixPath ++ ++ ++@final ++class WindowsPath(Path, pathlib.PureWindowsPath): ++ """An async :class:`pathlib.WindowsPath` that executes blocking methods in :meth:`trio.to_thread.run_sync`.""" ++ ++ __slots__ = () ++ ++ _wrapped_cls: ClassVar[type[pathlib.Path]] = pathlib.WindowsPath +diff --git a/trio/_tests/test_exports.py b/trio/_tests/test_exports.py +index 7b38137..e1ced60 100644 +--- a/trio/_tests/test_exports.py ++++ b/trio/_tests/test_exports.py +@@ -11,7 +11,7 @@ import socket as stdlib_socket + import sys + import types + from collections.abc import Iterator +-from pathlib import Path ++from pathlib import Path, PurePath + from types import ModuleType + from typing import Iterable, Protocol + +@@ -333,7 +333,8 @@ def test_static_tool_sees_class_members( + mod_cache = next_cache / "__init__.data.json" + else: + mod_cache = mod_cache / (modname[-1] + ".data.json") +- ++ elif mod_cache.is_dir(): ++ mod_cache /= "__init__.data.json" + with mod_cache.open() as f: + return json.loads(f.read())["names"][name] # type: ignore[no-any-return] + +@@ -480,12 +481,6 @@ def test_static_tool_sees_class_members( + extra -= EXTRAS[class_] + assert len(extra) == before - len(EXTRAS[class_]) + +- # probably an issue with mypy.... +- if tool == "mypy" and class_ == trio.Path and sys.platform == "win32": +- before = len(missing) +- missing -= {"owner", "group", "is_mount"} +- assert len(missing) == before - 3 +- + # TODO: why is this? Is it a problem? + # see https://github.com/python-trio/trio/pull/2631#discussion_r1185615916 + if class_ == trio.StapledStream: +@@ -508,25 +503,14 @@ def test_static_tool_sees_class_members( + missing.remove("__aiter__") + missing.remove("__anext__") + +- # __getattr__ is intentionally hidden behind type guard. That hook then +- # forwards property accesses to PurePath, meaning these names aren't directly on +- # the class. +- if class_ == trio.Path: +- missing.remove("__getattr__") +- before = len(extra) +- extra -= { +- "anchor", +- "drive", +- "name", +- "parent", +- "parents", +- "parts", +- "root", +- "stem", +- "suffix", +- "suffixes", +- } +- assert len(extra) == before - 10 ++ if class_ in (trio.Path, trio.WindowsPath, trio.PosixPath): ++ # These are from inherited subclasses. ++ missing -= PurePath.__dict__.keys() ++ # These are unix-only. ++ if tool == "mypy" and sys.platform == "win32": ++ missing -= {"owner", "is_mount", "group"} ++ if tool == "jedi" and sys.platform == "win32": ++ extra -= {"owner", "is_mount", "group"} + + if missing or extra: # pragma: no cover + errors[f"{module_name}.{class_name}"] = { +@@ -588,6 +572,9 @@ def test_classes_are_final() -> None: + continue + # ... insert other special cases here ... + ++ # The `Path` class needs to support inheritance to allow `WindowsPath` and `PosixPath`. ++ if class_ is trio.Path: ++ continue + # don't care about the *Statistics classes + if name.endswith("Statistics"): + continue +diff --git a/trio/_tests/test_path.py b/trio/_tests/test_path.py +index 30158f8..3528053 100644 +--- a/trio/_tests/test_path.py ++++ b/trio/_tests/test_path.py +@@ -3,13 +3,12 @@ from __future__ import annotations + import os + import pathlib + from collections.abc import Awaitable, Callable +-from typing import Any, Type, Union ++from typing import Type, Union + + import pytest + + import trio + from trio._file_io import AsyncIOWrapper +-from trio._path import AsyncAutoWrapperType as WrapperType + + + @pytest.fixture +@@ -26,6 +25,16 @@ def method_pair( + return getattr(sync_path, method_name), getattr(async_path, method_name) + + ++@pytest.mark.skipif(os.name == "nt", reason="OS is not posix") ++async def test_instantiate_posix() -> None: ++ assert isinstance(trio.Path(), trio.PosixPath) ++ ++ ++@pytest.mark.skipif(os.name != "nt", reason="OS is not Windows") ++async def test_instantiate_windows() -> None: ++ assert isinstance(trio.Path(), trio.WindowsPath) ++ ++ + async def test_open_is_async_context_manager(path: trio.Path) -> None: + async with await path.open("w") as f: + assert isinstance(f, AsyncIOWrapper) +@@ -166,41 +175,6 @@ async def test_repr() -> None: + assert repr(path) == "trio.Path('.')" + + +-class MockWrapped: +- unsupported = "unsupported" +- _private = "private" +- +- +-class _MockWrapper: +- _forwards = MockWrapped +- _wraps = MockWrapped +- +- +-MockWrapper: Any = _MockWrapper # Disable type checking, it's a mock. +- +- +-async def test_type_forwards_unsupported() -> None: +- with pytest.raises(TypeError): +- WrapperType.generate_forwards(MockWrapper, {}) +- +- +-async def test_type_wraps_unsupported() -> None: +- with pytest.raises(TypeError): +- WrapperType.generate_wraps(MockWrapper, {}) +- +- +-async def test_type_forwards_private() -> None: +- WrapperType.generate_forwards(MockWrapper, {"unsupported": None}) +- +- assert not hasattr(MockWrapper, "_private") +- +- +-async def test_type_wraps_private() -> None: +- WrapperType.generate_wraps(MockWrapper, {"unsupported": None}) +- +- assert not hasattr(MockWrapper, "_private") +- +- + @pytest.mark.parametrize("meth", [trio.Path.__init__, trio.Path.joinpath]) + async def test_path_wraps_path( + path: trio.Path, +-- +2.45.0 + diff --git a/SOURCES/python3.13-PR-3005.patch b/SOURCES/python3.13-PR-3005.patch new file mode 100644 index 0000000..ff36635 --- /dev/null +++ b/SOURCES/python3.13-PR-3005.patch @@ -0,0 +1,364 @@ +From d2d3d4dcdc2182e8e126c73473108eeb50eb25e7 Mon Sep 17 00:00:00 2001 +From: EXPLOSION +Date: Mon, 27 May 2024 12:23:31 +0200 +Subject: [PATCH] Fixes for 3.13 + +--- + trio/_core/_generated_instrumentation.py | 5 +++-- + trio/_core/_generated_io_epoll.py | 6 +++--- + trio/_core/_generated_io_kqueue.py | 12 ++++++------ + trio/_core/_generated_io_windows.py | 18 +++++++++--------- + trio/_core/_generated_run.py | 17 +++++++++-------- + trio/_core/_ki.py | 2 +- + trio/_core/_run.py | 6 +++--- + trio/_tests/test_path.py | 2 +- + 8 files changed, 35 insertions(+), 33 deletions(-) + +diff --git a/trio/_core/_generated_instrumentation.py b/trio/_core/_generated_instrumentation.py +index c7fefc3..990c405 100644 +--- a/trio/_core/_generated_instrumentation.py ++++ b/trio/_core/_generated_instrumentation.py +@@ -3,6 +3,7 @@ + # ************************************************************* + from __future__ import annotations + ++import sys + from ._instrumentation import Instrument + from ._ki import LOCALS_KEY_KI_PROTECTION_ENABLED + from ._run import GLOBAL_RUN_CONTEXT +@@ -17,7 +18,7 @@ def add_instrument(instrument: Instrument) -> None: + If ``instrument`` is already active, does nothing. + + """ +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.instruments.add_instrument(instrument) + except AttributeError: +@@ -37,7 +38,7 @@ def remove_instrument(instrument: Instrument) -> None: + deactivated. + + """ +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.instruments.remove_instrument(instrument) + except AttributeError: +diff --git a/trio/_core/_generated_io_epoll.py b/trio/_core/_generated_io_epoll.py +index f80e988..7927f79 100644 +--- a/trio/_core/_generated_io_epoll.py ++++ b/trio/_core/_generated_io_epoll.py +@@ -16,7 +16,7 @@ assert not TYPE_CHECKING or sys.platform == "linux" + + + async def wait_readable(fd: (int | _HasFileNo)) -> None: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(fd) + except AttributeError: +@@ -24,7 +24,7 @@ async def wait_readable(fd: (int | _HasFileNo)) -> None: + + + async def wait_writable(fd: (int | _HasFileNo)) -> None: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(fd) + except AttributeError: +@@ -32,7 +32,7 @@ async def wait_writable(fd: (int | _HasFileNo)) -> None: + + + def notify_closing(fd: (int | _HasFileNo)) -> None: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd) + except AttributeError: +diff --git a/trio/_core/_generated_io_kqueue.py b/trio/_core/_generated_io_kqueue.py +index b572831..1dd0626 100644 +--- a/trio/_core/_generated_io_kqueue.py ++++ b/trio/_core/_generated_io_kqueue.py +@@ -20,7 +20,7 @@ assert not TYPE_CHECKING or sys.platform == "darwin" + + + def current_kqueue() -> select.kqueue: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.io_manager.current_kqueue() + except AttributeError: +@@ -30,7 +30,7 @@ def current_kqueue() -> select.kqueue: + def monitor_kevent( + ident: int, filter: int + ) -> ContextManager[_core.UnboundedQueue[select.kevent]]: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.io_manager.monitor_kevent(ident, filter) + except AttributeError: +@@ -40,7 +40,7 @@ def monitor_kevent( + async def wait_kevent( + ident: int, filter: int, abort_func: Callable[[RaiseCancelT], Abort] + ) -> Abort: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_kevent( + ident, filter, abort_func +@@ -50,7 +50,7 @@ async def wait_kevent( + + + async def wait_readable(fd: (int | _HasFileNo)) -> None: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(fd) + except AttributeError: +@@ -58,7 +58,7 @@ async def wait_readable(fd: (int | _HasFileNo)) -> None: + + + async def wait_writable(fd: (int | _HasFileNo)) -> None: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(fd) + except AttributeError: +@@ -66,7 +66,7 @@ async def wait_writable(fd: (int | _HasFileNo)) -> None: + + + def notify_closing(fd: (int | _HasFileNo)) -> None: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd) + except AttributeError: +diff --git a/trio/_core/_generated_io_windows.py b/trio/_core/_generated_io_windows.py +index e859829..f0bef74 100644 +--- a/trio/_core/_generated_io_windows.py ++++ b/trio/_core/_generated_io_windows.py +@@ -20,7 +20,7 @@ assert not TYPE_CHECKING or sys.platform == "win32" + + + async def wait_readable(sock: (_HasFileNo | int)) -> None: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(sock) + except AttributeError: +@@ -28,7 +28,7 @@ async def wait_readable(sock: (_HasFileNo | int)) -> None: + + + async def wait_writable(sock: (_HasFileNo | int)) -> None: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(sock) + except AttributeError: +@@ -36,7 +36,7 @@ async def wait_writable(sock: (_HasFileNo | int)) -> None: + + + def notify_closing(handle: (Handle | int | _HasFileNo)) -> None: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(handle) + except AttributeError: +@@ -44,7 +44,7 @@ def notify_closing(handle: (Handle | int | _HasFileNo)) -> None: + + + def register_with_iocp(handle: (int | CData)) -> None: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.io_manager.register_with_iocp(handle) + except AttributeError: +@@ -54,7 +54,7 @@ def register_with_iocp(handle: (int | CData)) -> None: + async def wait_overlapped( + handle_: (int | CData), lpOverlapped: (CData | int) + ) -> object: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_overlapped( + handle_, lpOverlapped +@@ -66,7 +66,7 @@ async def wait_overlapped( + async def write_overlapped( + handle: (int | CData), data: Buffer, file_offset: int = 0 + ) -> int: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return await GLOBAL_RUN_CONTEXT.runner.io_manager.write_overlapped( + handle, data, file_offset +@@ -78,7 +78,7 @@ async def write_overlapped( + async def readinto_overlapped( + handle: (int | CData), buffer: Buffer, file_offset: int = 0 + ) -> int: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return await GLOBAL_RUN_CONTEXT.runner.io_manager.readinto_overlapped( + handle, buffer, file_offset +@@ -88,7 +88,7 @@ async def readinto_overlapped( + + + def current_iocp() -> int: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.io_manager.current_iocp() + except AttributeError: +@@ -96,7 +96,7 @@ def current_iocp() -> int: + + + def monitor_completion_key() -> ContextManager[tuple[int, UnboundedQueue[object]]]: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.io_manager.monitor_completion_key() + except AttributeError: +diff --git a/trio/_core/_generated_run.py b/trio/_core/_generated_run.py +index 399e1db..6bfc5f2 100644 +--- a/trio/_core/_generated_run.py ++++ b/trio/_core/_generated_run.py +@@ -3,6 +3,7 @@ + # ************************************************************* + from __future__ import annotations + ++import sys + import contextvars + from collections.abc import Awaitable, Callable + from typing import Any +@@ -38,7 +39,7 @@ def current_statistics() -> RunStatistics: + other attributes vary between backends. + + """ +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.current_statistics() + except AttributeError: +@@ -55,7 +56,7 @@ def current_time() -> float: + RuntimeError: if not inside a call to :func:`trio.run`. + + """ +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.current_time() + except AttributeError: +@@ -64,7 +65,7 @@ def current_time() -> float: + + def current_clock() -> Clock: + """Returns the current :class:`~trio.abc.Clock`.""" +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.current_clock() + except AttributeError: +@@ -77,7 +78,7 @@ def current_root_task() -> Task | None: + This is the task that is the ultimate parent of all other tasks. + + """ +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.current_root_task() + except AttributeError: +@@ -102,7 +103,7 @@ def reschedule(task: Task, next_send: Outcome[Any] = _NO_SEND) -> None: + raise) from :func:`wait_task_rescheduled`. + + """ +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.reschedule(task, next_send) + except AttributeError: +@@ -166,7 +167,7 @@ def spawn_system_task( + Task: the newly spawned task + + """ +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.spawn_system_task( + async_fn, *args, name=name, context=context +@@ -180,7 +181,7 @@ def current_trio_token() -> TrioToken: + :func:`trio.run`. + + """ +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return GLOBAL_RUN_CONTEXT.runner.current_trio_token() + except AttributeError: +@@ -245,7 +246,7 @@ async def wait_all_tasks_blocked(cushion: float = 0.0) -> None: + print("FAIL") + + """ +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + try: + return await GLOBAL_RUN_CONTEXT.runner.wait_all_tasks_blocked(cushion) + except AttributeError: +diff --git a/trio/_core/_ki.py b/trio/_core/_ki.py +index 0ea3461..a5e32d5 100644 +--- a/trio/_core/_ki.py ++++ b/trio/_core/_ki.py +@@ -176,7 +176,7 @@ def _ki_protection_decorator( + + @wraps(fn) + def wrapper(*args: ArgsT.args, **kwargs: ArgsT.kwargs) -> RetT: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = enabled ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = enabled + return fn(*args, **kwargs) + + return wrapper +diff --git a/trio/_core/_run.py b/trio/_core/_run.py +index b5a4912..16657c0 100644 +--- a/trio/_core/_run.py ++++ b/trio/_core/_run.py +@@ -617,7 +617,7 @@ class CancelScope: + + # This inlines the enable_ki_protection decorator so we can fix + # f_locals *locally* below to avoid reference cycles +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + + # Tracebacks show the 'raise' line below out of context, so let's give + # this variable a name that makes sense out of context. +@@ -640,7 +640,7 @@ class CancelScope: + # see test_cancel_scope_exit_doesnt_create_cyclic_garbage + del remaining_error_after_cancel_scope, value, _, exc + # deep magic to remove refs via f_locals +- locals() ++ sys._getframe().f_locals + # TODO: check if PEP558 changes the need for this call + # https://github.com/python/cpython/pull/3640 + +@@ -2409,7 +2409,7 @@ def unrolled_run( + args: tuple[object, ...], + host_uses_signal_set_wakeup_fd: bool = False, + ) -> Generator[float, EventResult, None]: +- locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True ++ sys._getframe().f_locals[LOCALS_KEY_KI_PROTECTION_ENABLED] = True + __tracebackhide__ = True + + try: +diff --git a/trio/_tests/test_path.py b/trio/_tests/test_path.py +index 3528053..dd3ec66 100644 +--- a/trio/_tests/test_path.py ++++ b/trio/_tests/test_path.py +@@ -120,7 +120,7 @@ async def test_async_method_signature(path: trio.Path) -> None: + assert path.resolve.__qualname__ == "Path.resolve" + + assert path.resolve.__doc__ is not None +- assert "pathlib.Path.resolve" in path.resolve.__doc__ ++ assert path.resolve.__qualname__ in path.resolve.__doc__ + + + @pytest.mark.parametrize("method_name", ["is_dir", "is_file"]) +-- +2.44.0 + diff --git a/SPECS/python-trio.spec b/SPECS/python-trio.spec new file mode 100644 index 0000000..359c649 --- /dev/null +++ b/SPECS/python-trio.spec @@ -0,0 +1,189 @@ +## START: Set by rpmautospec +## (rpmautospec version 0.6.5) +## RPMAUTOSPEC: autorelease, autochangelog +%define autorelease(e:s:pb:n) %{?-p:0.}%{lua: + release_number = 6; + base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}")); + print(release_number + base_release_number - 1); +}%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}} +## END: Set by rpmautospec + +%global common_description %{expand: +The Trio project's goal is to produce a production-quality, permissively +licensed, async/await-native I/O library for Python. Like all async libraries, +its main purpose is to help you write programs that do multiple things at the +same time with parallelized I/O. A web spider that wants to fetch lots of +pages in parallel, a web server that needs to juggle lots of downloads and +websocket connections at the same time, a process supervisor monitoring +multiple subprocesses... that sort of thing. Compared to other libraries, Trio +attempts to distinguish itself with an obsessive focus on usability and +correctness. Concurrency is complicated; we try to make it easy to get things +right.} + + +Name: python-trio +Version: 0.23.1 +Release: %autorelease +Summary: A friendly Python library for async concurrency and I/O +License: Apache-2.0 OR MIT +URL: https://github.com/python-trio/trio +Source: %pypi_source trio + +# Python 3.13 support +# Manually rebased from https://github.com/python-trio/trio/pull/2959 +Patch: python3.13-PR-2959.patch +# Manually rebased from https://github.com/python-trio/trio/pull/3005 +Patch: python3.13-PR-3005.patch + +BuildArch: noarch + + +%description %{common_description} + + +%package -n python3-trio +Summary: %{summary} +BuildRequires: python3-devel +BuildRequires: python3-pytest + + +%description -n python3-trio %{common_description} + + +%prep +%autosetup -p 1 -n trio-%{version} + + +%generate_buildrequires +%pyproject_buildrequires + + +%build +%pyproject_wheel + + +%install +%pyproject_install +%pyproject_save_files trio + + +%check +# https://github.com/python-trio/trio/issues/2863 +# https://github.com/python-trio/trio/pull/2870 +# https://docs.pytest.org/en/stable/explanation/goodpractices.html#tests-as-part-of-application-code +%pytest --pyargs trio -p trio._tests.pytest_plugin --verbose --skip-optional-imports + + +%files -n python3-trio -f %{pyproject_files} +%doc README.rst + + +%changelog +* Sat Jan 04 2025 Arkady L. Shane - 0.23.1-6 +- Rebuilt for MSVSphere 10 + +## START: Generated by rpmautospec +* Fri Jul 19 2024 Fedora Release Engineering - 0.23.1-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + +* Fri Jun 07 2024 Python Maint - 0.23.1-5 +- Rebuilt for Python 3.13 + +* Mon May 27 2024 Miro Hrončok - 0.23.1-4 +- Python 3.13 support + +* Fri Jan 26 2024 Fedora Release Engineering - 0.23.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Mon Jan 22 2024 Fedora Release Engineering - 0.23.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Tue Nov 07 2023 Carl George - 0.23.1-1 +- Update to version 0.23.1, resolves rhbz#2219110 + +* Fri Jul 21 2023 Fedora Release Engineering - 0.22.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Sat Jul 01 2023 Python Maint - 0.22.0-4 +- Rebuilt for Python 3.12 + +* Sat Jul 01 2023 Miro Hrončok - 0.22.0-3 +- Remove async_generator as dependency + +* Fri Jan 20 2023 Fedora Release Engineering - 0.22.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Mon Oct 31 2022 Carl George - 0.22.0-1 +- Update to 0.22.0, resolves rhbz#2094511 + +* Fri Jul 22 2022 Fedora Release Engineering - 0.20.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Mon Jun 13 2022 Python Maint - 0.20.0-4 +- Rebuilt for Python 3.11 + +* Thu Mar 24 2022 Miro Hrončok - 0.20.0-3 +- Add fix for Python 3.11 +- Fixes: rhbz#2049632 + +* Thu Mar 03 2022 Carl George - 0.20.0-1 +- Latest upstream rhbz#2056578 + +* Fri Jan 21 2022 Fedora Release Engineering - 0.19.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Sat Jul 24 2021 Carl George - 0.19.0-1 +- Latest upstream rhbz#1972135 + +* Fri Jun 04 2021 Python Maint - 0.18.0^20210519gitd883dbe-4 +- Rebuilt for Python 3.10 + +* Fri Jun 04 2021 Python Maint - 0.18.0^20210519gitd883dbe-3 +- Bootstrap for Python 3.10 + +* Mon May 17 2021 Tomas Hrnciar - 0.18.0-2 +- Backport upstream fixes to bring compatibility with Python 3.10 + +* Tue Feb 09 2021 Joel Capitao - 0.18.0-1 +- Latest upstream rhbz#1879061 + +* Wed Jan 27 2021 Fedora Release Engineering - 0.16.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Sun Sep 06 2020 Carl George - 0.16.0-1 +- Latest upstream + +* Wed Jul 29 2020 Fedora Release Engineering - 0.15.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Thu Jun 04 2020 Carl George - 0.15.1-1 +- Latest upstream rhbz#1828266 + +* Tue May 26 2020 Miro Hrončok - 0.13.0-3 +- Rebuilt for Python 3.9 + +* Thu Jan 30 2020 Fedora Release Engineering - 0.13.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Thu Jan 02 2020 Carl George - 0.13.0-1 +- Latest upstream rhbz#1742425 + +* Thu Oct 03 2019 Miro Hrončok - 0.11.0-4 +- Rebuilt for Python 3.8.0rc1 (#1748018) + +* Mon Aug 19 2019 Miro Hrončok - 0.11.0-3 +- Rebuilt for Python 3.8 + +* Fri Jul 26 2019 Fedora Release Engineering - 0.11.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Thu Feb 28 2019 Carl George - 0.11.0-1 +- Latest upstream + +* Sat Feb 02 2019 Fedora Release Engineering - 0.7.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Thu Sep 20 2018 Carl George - 0.7.0-1 +- Initial package + +## END: Generated by rpmautospec