Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into raisesgroup_fail_reason
Browse files Browse the repository at this point in the history
  • Loading branch information
jakkdl committed Dec 9, 2024
2 parents debe557 + da89ae8 commit a8e263c
Show file tree
Hide file tree
Showing 23 changed files with 128 additions and 58 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches-ignore:
- "dependabot/**"
pull_request:
merge_group:

concurrency:
group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && format('-{0}', github.sha) || '' }}
Expand Down Expand Up @@ -189,7 +190,8 @@ jobs:
# can't use setup-python because that python doesn't seem to work;
# `python3-dev` (rather than `python:alpine`) for some ctypes reason,
# `nodejs` for pyright (`node-env` pulls in nodejs but that takes a while and can time out the test).
run: apk update && apk add python3-dev bash nodejs
# `perl` for a platform independent `sed -i` alternative
run: apk update && apk add python3-dev bash nodejs perl
- name: Enter virtual environment
run: python -m venv .venv
- name: Run tests
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ repos:
hooks:
- id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.0
rev: v0.8.1
hooks:
- id: ruff
types: [file]
Expand Down
14 changes: 10 additions & 4 deletions ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -116,23 +116,29 @@ else
echo "::group::Setup for tests"

# We run the tests from inside an empty directory, to make sure Python
# doesn't pick up any .py files from our working dir. Might have been
# pre-created by some of the code above.
# doesn't pick up any .py files from our working dir. Might have already
# been created by a previous run.
mkdir empty || true
cd empty

INSTALLDIR=$(python -c "import os, trio; print(os.path.dirname(trio.__file__))")
cp ../pyproject.toml "$INSTALLDIR"
cp ../pyproject.toml "$INSTALLDIR" # TODO: remove this

# get mypy tests a nice cache
MYPYPATH=".." mypy --config-file= --cache-dir=./.mypy_cache -c "import trio" >/dev/null 2>/dev/null || true

# support subprocess spawning with coverage.py
echo "import coverage; coverage.process_startup()" | tee -a "$INSTALLDIR/../sitecustomize.py"

perl -i -pe 's/-p trio\._tests\.pytest_plugin//' "$INSTALLDIR/pyproject.toml"

echo "::endgroup::"
echo "::group:: Run Tests"
if COVERAGE_PROCESS_START=$(pwd)/../pyproject.toml coverage run --rcfile=../pyproject.toml -m pytest -ra --junitxml=../test-results.xml --run-slow "${INSTALLDIR}" --verbose --durations=10 $flags; then
if PYTHONPATH=../tests COVERAGE_PROCESS_START=$(pwd)/../pyproject.toml \
coverage run --rcfile=../pyproject.toml -m \
pytest -ra --junitxml=../test-results.xml \
-p _trio_check_attrs_aliases --verbose --durations=10 \
-p trio._tests.pytest_plugin --run-slow $flags "${INSTALLDIR}"; then
PASSED=true
else
PASSED=false
Expand Down
10 changes: 5 additions & 5 deletions docs-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ colorama==0.4.6 ; sys_platform == 'win32' or platform_system == 'Windows'
# via
# click
# sphinx
cryptography==43.0.3
cryptography==44.0.0
# via pyopenssl
docutils==0.21.2
# via
Expand All @@ -49,13 +49,13 @@ markupsafe==3.0.2
# via jinja2
outcome==1.3.0.post0
# via -r docs-requirements.in
packaging==24.1
packaging==24.2
# via sphinx
pycparser==2.22 ; platform_python_implementation != 'PyPy' or os_name == 'nt'
# via cffi
pygments==2.18.0
# via sphinx
pyopenssl==24.2.1
pyopenssl==24.3.0
# via -r docs-requirements.in
requests==2.32.3
# via sphinx
Expand All @@ -77,9 +77,9 @@ sphinx==8.1.3
# sphinxcontrib-trio
sphinx-codeautolink==0.15.2
# via -r docs-requirements.in
sphinx-hoverxref==1.4.1
sphinx-hoverxref==1.4.2
# via -r docs-requirements.in
sphinx-rtd-theme==3.0.1
sphinx-rtd-theme==3.0.2
# via -r docs-requirements.in
sphinxcontrib-applehelp==2.0.0
# via sphinx
Expand Down
1 change: 1 addition & 0 deletions newsfragments/3114.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensure that Pyright recognizes our underscore prefixed attributes for attrs classes.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ reportUnnecessaryTypeIgnoreComment = true
typeCheckingMode = "strict"

[tool.pytest.ini_options]
addopts = ["--strict-markers", "--strict-config", "-p trio._tests.pytest_plugin"]
addopts = ["--strict-markers", "--strict-config", "-p trio._tests.pytest_plugin", "--import-mode=importlib"]
faulthandler_timeout = 60
filterwarnings = [
"error",
Expand Down
6 changes: 4 additions & 2 deletions src/trio/_core/_concat_tb.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

from types import TracebackType
from typing import TYPE_CHECKING, ClassVar, cast

if TYPE_CHECKING:
from types import TracebackType

################################################################
# concat_tb
################################################################
Expand Down Expand Up @@ -107,7 +109,7 @@ def controller( # type: ignore[no-any-unimported]
return operation.delegate() # type: ignore[no-any-return]

return cast(
TracebackType,
"TracebackType",
tputil.make_proxy(controller, type(base_tb), base_tb),
) # Returns proxy to traceback

Expand Down
10 changes: 5 additions & 5 deletions src/trio/_core/_io_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ def _refresh_afd(self, base_handle: Handle) -> None:

lpOverlapped = ffi.new("LPOVERLAPPED")

poll_info = cast(_AFDPollInfo, ffi.new("AFD_POLL_INFO *"))
poll_info = cast("_AFDPollInfo", ffi.new("AFD_POLL_INFO *"))
poll_info.Timeout = 2**63 - 1 # INT64_MAX
poll_info.NumberOfHandles = 1
poll_info.Exclusive = 0
Expand All @@ -713,9 +713,9 @@ def _refresh_afd(self, base_handle: Handle) -> None:
kernel32.DeviceIoControl(
afd_group.handle,
IoControlCodes.IOCTL_AFD_POLL,
cast(CType, poll_info),
cast("CType", poll_info),
ffi.sizeof("AFD_POLL_INFO"),
cast(CType, poll_info),
cast("CType", poll_info),
ffi.sizeof("AFD_POLL_INFO"),
ffi.NULL,
lpOverlapped,
Expand Down Expand Up @@ -937,13 +937,13 @@ async def _perform_overlapped(
# operation will not be cancellable, depending on how Windows is
# feeling today. So we need to check for cancellation manually.
await _core.checkpoint_if_cancelled()
lpOverlapped = cast(_Overlapped, ffi.new("LPOVERLAPPED"))
lpOverlapped = cast("_Overlapped", ffi.new("LPOVERLAPPED"))
try:
submit_fn(lpOverlapped)
except OSError as exc:
if exc.winerror != ErrorCodes.ERROR_IO_PENDING:
raise
await self.wait_overlapped(handle, cast(CData, lpOverlapped))
await self.wait_overlapped(handle, cast("CData", lpOverlapped))
return lpOverlapped

@_public
Expand Down
6 changes: 3 additions & 3 deletions src/trio/_core/_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ class RunVar(Generic[T]):
"""

_name: str
_default: T | type[_NoValue] = _NoValue
_name: str = attrs.field(alias="name")
_default: T | type[_NoValue] = attrs.field(default=_NoValue, alias="default")

def get(self, default: T | type[_NoValue] = _NoValue) -> T:
"""Gets the value of this :class:`RunVar` for the current run call."""
try:
return cast(T, _run.GLOBAL_RUN_CONTEXT.runner._locals[self])
return cast("T", _run.GLOBAL_RUN_CONTEXT.runner._locals[self])
except AttributeError:
raise RuntimeError("Cannot be used outside of a run context") from None
except KeyError:
Expand Down
16 changes: 10 additions & 6 deletions src/trio/_core/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,9 +543,13 @@ class CancelScope:
cancelled_caught: bool = attrs.field(default=False, init=False)

# Constructor arguments:
_relative_deadline: float = attrs.field(default=inf, kw_only=True)
_deadline: float = attrs.field(default=inf, kw_only=True)
_shield: bool = attrs.field(default=False, kw_only=True)
_relative_deadline: float = attrs.field(
default=inf,
kw_only=True,
alias="relative_deadline",
)
_deadline: float = attrs.field(default=inf, kw_only=True, alias="deadline")
_shield: bool = attrs.field(default=False, kw_only=True, alias="shield")

def __attrs_post_init__(self) -> None:
if isnan(self._deadline):
Expand Down Expand Up @@ -941,7 +945,7 @@ def started(self: _TaskStatus[StatusT], value: StatusT) -> None: ...
def started(self, value: StatusT | None = None) -> None:
if self._value is not _NoStatus:
raise RuntimeError("called 'started' twice on the same task status")
self._value = cast(StatusT, value) # If None, StatusT == None
self._value = cast("StatusT", value) # If None, StatusT == None

# If the old nursery is cancelled, then quietly quit now; the child
# will eventually exit on its own, and we don't want to risk moving
Expand Down Expand Up @@ -2407,7 +2411,7 @@ def run(
# Inlined copy of runner.main_task_outcome.unwrap() to avoid
# cluttering every single Trio traceback with an extra frame.
if isinstance(runner.main_task_outcome, Value):
return cast(RetT, runner.main_task_outcome.value)
return cast("RetT", runner.main_task_outcome.value)
elif isinstance(runner.main_task_outcome, Error):
raise runner.main_task_outcome.error
else: # pragma: no cover
Expand Down Expand Up @@ -2531,7 +2535,7 @@ def my_done_callback(run_outcome):
# this time, so it shouldn't be possible to get an exception here,
# except for a TrioInternalError.
next_send = cast(
EventResult,
"EventResult",
None,
) # First iteration must be `None`, every iteration after that is EventResult
for _tick in range(5): # expected need is 2 iterations + leave some wiggle room
Expand Down
2 changes: 1 addition & 1 deletion src/trio/_core/_tests/test_guest_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ def trio_done_callback(main_outcome: Outcome[T]) -> None:
trio_done_fut.set_result(main_outcome)

if pass_not_threadsafe:
run_sync_soon_not_threadsafe = cast(InHost, loop.call_soon)
run_sync_soon_not_threadsafe = cast("InHost", loop.call_soon)

trio.lowlevel.start_guest_run(
trio_fn,
Expand Down
2 changes: 1 addition & 1 deletion src/trio/_core/_tests/tutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import pytest

# See trio/_tests/conftest.py for the other half of this
# See trio/_tests/pytest_plugin.py for the other half of this
from trio._tests.pytest_plugin import RUN_SLOW

if TYPE_CHECKING:
Expand Down
5 changes: 2 additions & 3 deletions src/trio/_core/_traps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import enum
import types
from collections.abc import Awaitable

# Jedi gets mad in test_static_tool_sees_class_members if we use collections Callable
from typing import TYPE_CHECKING, Any, Callable, NoReturn, Union, cast
Expand All @@ -15,7 +14,7 @@
from . import _run

if TYPE_CHECKING:
from collections.abc import Generator
from collections.abc import Awaitable, Generator

from typing_extensions import TypeAlias

Expand Down Expand Up @@ -67,7 +66,7 @@ def _real_async_yield(
# Real yield value is from trio's main loop, but type checkers can't
# understand that, so we cast it to make type checkers understand.
_async_yield = cast(
Callable[[MessageType], Awaitable[outcome.Outcome[object]]],
"Callable[[MessageType], Awaitable[outcome.Outcome[object]]]",
_real_async_yield,
)

Expand Down
6 changes: 3 additions & 3 deletions src/trio/_core/_windows_cffi.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,9 @@ class _Overlapped(Protocol):
hEvent: Handle


kernel32 = cast(_Kernel32, ffi.dlopen("kernel32.dll"))
ntdll = cast(_Nt, ffi.dlopen("ntdll.dll"))
ws2_32 = cast(_Ws2, ffi.dlopen("ws2_32.dll"))
kernel32 = cast("_Kernel32", ffi.dlopen("kernel32.dll"))
ntdll = cast("_Nt", ffi.dlopen("ntdll.dll"))
ws2_32 = cast("_Ws2", ffi.dlopen("ws2_32.dll"))

################################################################
# Magic numbers
Expand Down
37 changes: 35 additions & 2 deletions src/trio/_tests/test_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@

import trio
import trio.testing
from trio._tests.pytest_plugin import skip_if_optional_else_raise
from trio._tests.pytest_plugin import RUN_SLOW, skip_if_optional_else_raise

from .. import _core, _util
from .._core._tests.tutil import slow
from .pytest_plugin import RUN_SLOW

if TYPE_CHECKING:
from collections.abc import Iterable, Iterator
Expand Down Expand Up @@ -572,3 +571,37 @@ def test_classes_are_final() -> None:
continue

assert class_is_final(class_)


# Plugin might not be running, especially if running from an installed version.
@pytest.mark.skipif(
not hasattr(attrs.field, "trio_modded"),
reason="Pytest plugin not installed.",
)
def test_pyright_recognizes_init_attributes() -> None:
"""Check whether we provide `alias` for all underscore prefixed attributes.
Attrs always sets the `alias` attribute on fields, so a pytest plugin is used
to monkeypatch `field()` to record whether an alias was defined in the metadata.
See `_trio_check_attrs_aliases`.
"""
for module in PUBLIC_MODULES:
for class_ in module.__dict__.values():
if not attrs.has(class_):
continue
if isinstance(class_, _util.NoPublicConstructor):
continue

attributes = [
attr
for attr in attrs.fields(class_)
if attr.init
if attr.alias
not in (
attr.name,
# trio_original_args may not be present in autoattribs
attr.metadata.get("trio_original_args", {}).get("alias"),
)
]

assert attributes == [], class_
2 changes: 1 addition & 1 deletion src/trio/_tests/test_highlevel_open_tcp_listeners.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ async def handler(stream: SendStream) -> None:
# nursery.start is incorrectly typed, awaiting #2773
value = await nursery.start(serve_tcp, handler, 0)
assert isinstance(value, list)
listeners = cast(list[SocketListener], value)
listeners = cast("list[SocketListener]", value)
stream = await open_stream_to_socket_listener(listeners[0])
async with stream:
assert await stream.receive_some(1) == b"x"
Expand Down
2 changes: 1 addition & 1 deletion src/trio/_tests/test_highlevel_serve_listeners.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ async def do_tests(parent_nursery: Nursery) -> None:
listeners,
)
assert isinstance(value, list)
l2 = cast(list[MemoryListener], value)
l2 = cast("list[MemoryListener]", value)
assert l2 == listeners
# This is just split into another function because gh-136 isn't
# implemented yet
Expand Down
7 changes: 4 additions & 3 deletions src/trio/_tests/test_highlevel_ssl_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@
import trio.testing
from trio.socket import AF_INET, IPPROTO_TCP, SOCK_STREAM

from .._highlevel_socket import SocketListener
from .._highlevel_ssl_helpers import (
open_ssl_over_tcp_listeners,
open_ssl_over_tcp_stream,
serve_ssl_over_tcp,
)
from .._ssl import SSLListener

# using noqa because linters don't understand how pytest fixtures work.
from .test_ssl import SERVER_CTX, client_ctx # noqa: F401
Expand All @@ -27,6 +25,9 @@

from trio.abc import Stream

from .._highlevel_socket import SocketListener
from .._ssl import SSLListener


async def echo_handler(stream: Stream) -> None:
async with stream:
Expand Down Expand Up @@ -92,7 +93,7 @@ async def test_open_ssl_over_tcp_stream_and_everything_else(
),
)
assert isinstance(value, list)
res = cast(list[SSLListener[SocketListener]], value) # type: ignore[type-var]
res = cast("list[SSLListener[SocketListener]]", value) # type: ignore[type-var]
(listener,) = res
async with listener:
# listener.transport_listener is of type Listener[Stream]
Expand Down
2 changes: 1 addition & 1 deletion src/trio/_tests/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ async def res(
local=local, # noqa: B023 # local is not bound in function definition
)
assert isinstance(value, tuple)
return cast(tuple[Union[str, int], ...], value)
return cast("tuple[Union[str, int], ...]", value)

assert_eq(await res((addrs.arbitrary, "http")), (addrs.arbitrary, 80))
if v6:
Expand Down
Loading

0 comments on commit a8e263c

Please sign in to comment.