commit
1876632bc6
@ -0,0 +1 @@
|
||||
SOURCES/trio-0.23.1.tar.gz
|
@ -0,0 +1 @@
|
||||
3e65818ecc4f708ed4faf0ebb2a3c62ef990b9b6 SOURCES/trio-0.23.1.tar.gz
|
@ -0,0 +1,846 @@
|
||||
From 86182cb193a71533a7a12b8d45a6ad4103d41ccc Mon Sep 17 00:00:00 2001
|
||||
From: Dave Hall <dave@etianen.com>
|
||||
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" <https://github.com/python-trio/trio/issues/501>`__ 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
|
||||
- <https://github.com/python-trio/trio/issues/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
|
||||
|
@ -0,0 +1,364 @@
|
||||
From d2d3d4dcdc2182e8e126c73473108eeb50eb25e7 Mon Sep 17 00:00:00 2001
|
||||
From: EXPLOSION <git@helvetica.moe>
|
||||
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
|
||||
|
@ -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 <tigro@msvsphere-os.ru> - 0.23.1-6
|
||||
- Rebuilt for MSVSphere 10
|
||||
|
||||
## START: Generated by rpmautospec
|
||||
* Fri Jul 19 2024 Fedora Release Engineering <releng@fedoraproject.org> - 0.23.1-6
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild
|
||||
|
||||
* Fri Jun 07 2024 Python Maint <python-maint@redhat.com> - 0.23.1-5
|
||||
- Rebuilt for Python 3.13
|
||||
|
||||
* Mon May 27 2024 Miro Hrončok <miro@hroncok.cz> - 0.23.1-4
|
||||
- Python 3.13 support
|
||||
|
||||
* Fri Jan 26 2024 Fedora Release Engineering <releng@fedoraproject.org> - 0.23.1-3
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
|
||||
|
||||
* Mon Jan 22 2024 Fedora Release Engineering <releng@fedoraproject.org> - 0.23.1-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
|
||||
|
||||
* Tue Nov 07 2023 Carl George <carlwgeorge@fedoraproject.org> - 0.23.1-1
|
||||
- Update to version 0.23.1, resolves rhbz#2219110
|
||||
|
||||
* Fri Jul 21 2023 Fedora Release Engineering <releng@fedoraproject.org> - 0.22.0-5
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
|
||||
|
||||
* Sat Jul 01 2023 Python Maint <python-maint@redhat.com> - 0.22.0-4
|
||||
- Rebuilt for Python 3.12
|
||||
|
||||
* Sat Jul 01 2023 Miro Hrončok <mhroncok@redhat.com> - 0.22.0-3
|
||||
- Remove async_generator as dependency
|
||||
|
||||
* Fri Jan 20 2023 Fedora Release Engineering <releng@fedoraproject.org> - 0.22.0-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
|
||||
|
||||
* Mon Oct 31 2022 Carl George <carl@george.computer> - 0.22.0-1
|
||||
- Update to 0.22.0, resolves rhbz#2094511
|
||||
|
||||
* Fri Jul 22 2022 Fedora Release Engineering <releng@fedoraproject.org> - 0.20.0-5
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
|
||||
|
||||
* Mon Jun 13 2022 Python Maint <python-maint@redhat.com> - 0.20.0-4
|
||||
- Rebuilt for Python 3.11
|
||||
|
||||
* Thu Mar 24 2022 Miro Hrončok <mhroncok@redhat.com> - 0.20.0-3
|
||||
- Add fix for Python 3.11
|
||||
- Fixes: rhbz#2049632
|
||||
|
||||
* Thu Mar 03 2022 Carl George <carl@george.computer> - 0.20.0-1
|
||||
- Latest upstream rhbz#2056578
|
||||
|
||||
* Fri Jan 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 0.19.0-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
|
||||
|
||||
* Sat Jul 24 2021 Carl George <carl@george.computer> - 0.19.0-1
|
||||
- Latest upstream rhbz#1972135
|
||||
|
||||
* Fri Jun 04 2021 Python Maint <python-maint@redhat.com> - 0.18.0^20210519gitd883dbe-4
|
||||
- Rebuilt for Python 3.10
|
||||
|
||||
* Fri Jun 04 2021 Python Maint <python-maint@redhat.com> - 0.18.0^20210519gitd883dbe-3
|
||||
- Bootstrap for Python 3.10
|
||||
|
||||
* Mon May 17 2021 Tomas Hrnciar <thrnciar@redhat.com> - 0.18.0-2
|
||||
- Backport upstream fixes to bring compatibility with Python 3.10
|
||||
|
||||
* Tue Feb 09 2021 Joel Capitao <jcapitao@redhat.com> - 0.18.0-1
|
||||
- Latest upstream rhbz#1879061
|
||||
|
||||
* Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 0.16.0-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
|
||||
|
||||
* Sun Sep 06 2020 Carl George <carl@george.computer> - 0.16.0-1
|
||||
- Latest upstream
|
||||
|
||||
* Wed Jul 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.15.1-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
|
||||
|
||||
* Thu Jun 04 2020 Carl George <carl@george.computer> - 0.15.1-1
|
||||
- Latest upstream rhbz#1828266
|
||||
|
||||
* Tue May 26 2020 Miro Hrončok <mhroncok@redhat.com> - 0.13.0-3
|
||||
- Rebuilt for Python 3.9
|
||||
|
||||
* Thu Jan 30 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.13.0-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
|
||||
|
||||
* Thu Jan 02 2020 Carl George <carl@george.computer> - 0.13.0-1
|
||||
- Latest upstream rhbz#1742425
|
||||
|
||||
* Thu Oct 03 2019 Miro Hrončok <mhroncok@redhat.com> - 0.11.0-4
|
||||
- Rebuilt for Python 3.8.0rc1 (#1748018)
|
||||
|
||||
* Mon Aug 19 2019 Miro Hrončok <mhroncok@redhat.com> - 0.11.0-3
|
||||
- Rebuilt for Python 3.8
|
||||
|
||||
* Fri Jul 26 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.11.0-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
|
||||
|
||||
* Thu Feb 28 2019 Carl George <carl@george.computer> - 0.11.0-1
|
||||
- Latest upstream
|
||||
|
||||
* Sat Feb 02 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.7.0-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
|
||||
|
||||
* Thu Sep 20 2018 Carl George <carl@george.computer> - 0.7.0-1
|
||||
- Initial package
|
||||
|
||||
## END: Generated by rpmautospec
|
Loading…
Reference in new issue