@ajksunkang-aios/kgraph-linux-x64 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/kgraph-launcher +15 -3
- package/lib/kgraph/scripts/build-bundle.sh +17 -4
- package/lib/site-packages/outcome/__init__.py +20 -0
- package/lib/site-packages/outcome/_impl.py +239 -0
- package/lib/site-packages/outcome/_util.py +33 -0
- package/lib/site-packages/outcome/_version.py +7 -0
- package/lib/site-packages/outcome/py.typed +0 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE +3 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.MIT +20 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/METADATA +63 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/RECORD +13 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/WHEEL +6 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/top_level.txt +1 -0
- package/lib/site-packages/sniffio/__init__.py +17 -0
- package/lib/site-packages/sniffio/_impl.py +95 -0
- package/lib/site-packages/sniffio/_tests/__init__.py +0 -0
- package/lib/site-packages/sniffio/_tests/test_sniffio.py +84 -0
- package/lib/site-packages/sniffio/_version.py +3 -0
- package/lib/site-packages/sniffio/py.typed +0 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/INSTALLER +1 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE +3 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT +20 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/METADATA +104 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/RECORD +14 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/WHEEL +5 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/top_level.txt +1 -0
- package/lib/site-packages/sortedcontainers/__init__.py +74 -0
- package/lib/site-packages/sortedcontainers/sorteddict.py +812 -0
- package/lib/site-packages/sortedcontainers/sortedlist.py +2646 -0
- package/lib/site-packages/sortedcontainers/sortedset.py +733 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/LICENSE +13 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/METADATA +264 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/RECORD +10 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/WHEEL +6 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/top_level.txt +1 -0
- package/lib/site-packages/trio/__init__.py +133 -0
- package/lib/site-packages/trio/__main__.py +3 -0
- package/lib/site-packages/trio/_abc.py +714 -0
- package/lib/site-packages/trio/_channel.py +610 -0
- package/lib/site-packages/trio/_core/__init__.py +94 -0
- package/lib/site-packages/trio/_core/_asyncgens.py +243 -0
- package/lib/site-packages/trio/_core/_concat_tb.py +26 -0
- package/lib/site-packages/trio/_core/_entry_queue.py +223 -0
- package/lib/site-packages/trio/_core/_exceptions.py +169 -0
- package/lib/site-packages/trio/_core/_generated_instrumentation.py +50 -0
- package/lib/site-packages/trio/_core/_generated_io_epoll.py +98 -0
- package/lib/site-packages/trio/_core/_generated_io_kqueue.py +153 -0
- package/lib/site-packages/trio/_core/_generated_io_windows.py +204 -0
- package/lib/site-packages/trio/_core/_generated_run.py +269 -0
- package/lib/site-packages/trio/_core/_generated_windows_ffi.py +10 -0
- package/lib/site-packages/trio/_core/_instrumentation.py +117 -0
- package/lib/site-packages/trio/_core/_io_common.py +31 -0
- package/lib/site-packages/trio/_core/_io_epoll.py +385 -0
- package/lib/site-packages/trio/_core/_io_kqueue.py +292 -0
- package/lib/site-packages/trio/_core/_io_windows.py +1036 -0
- package/lib/site-packages/trio/_core/_ki.py +271 -0
- package/lib/site-packages/trio/_core/_local.py +104 -0
- package/lib/site-packages/trio/_core/_mock_clock.py +165 -0
- package/lib/site-packages/trio/_core/_parking_lot.py +317 -0
- package/lib/site-packages/trio/_core/_run.py +3148 -0
- package/lib/site-packages/trio/_core/_run_context.py +15 -0
- package/lib/site-packages/trio/_core/_tests/__init__.py +0 -0
- package/lib/site-packages/trio/_core/_tests/test_asyncgen.py +339 -0
- package/lib/site-packages/trio/_core/_tests/test_cancelled.py +222 -0
- package/lib/site-packages/trio/_core/_tests/test_exceptiongroup_gc.py +103 -0
- package/lib/site-packages/trio/_core/_tests/test_guest_mode.py +755 -0
- package/lib/site-packages/trio/_core/_tests/test_instrumentation.py +315 -0
- package/lib/site-packages/trio/_core/_tests/test_io.py +522 -0
- package/lib/site-packages/trio/_core/_tests/test_ki.py +703 -0
- package/lib/site-packages/trio/_core/_tests/test_local.py +118 -0
- package/lib/site-packages/trio/_core/_tests/test_mock_clock.py +193 -0
- package/lib/site-packages/trio/_core/_tests/test_parking_lot.py +389 -0
- package/lib/site-packages/trio/_core/_tests/test_run.py +3024 -0
- package/lib/site-packages/trio/_core/_tests/test_thread_cache.py +227 -0
- package/lib/site-packages/trio/_core/_tests/test_tutil.py +13 -0
- package/lib/site-packages/trio/_core/_tests/test_unbounded_queue.py +154 -0
- package/lib/site-packages/trio/_core/_tests/test_windows.py +305 -0
- package/lib/site-packages/trio/_core/_tests/tutil.py +117 -0
- package/lib/site-packages/trio/_core/_tests/type_tests/nursery_start.py +79 -0
- package/lib/site-packages/trio/_core/_tests/type_tests/run.py +51 -0
- package/lib/site-packages/trio/_core/_thread_cache.py +317 -0
- package/lib/site-packages/trio/_core/_traps.py +318 -0
- package/lib/site-packages/trio/_core/_unbounded_queue.py +163 -0
- package/lib/site-packages/trio/_core/_wakeup_socketpair.py +75 -0
- package/lib/site-packages/trio/_core/_windows_cffi.py +313 -0
- package/lib/site-packages/trio/_deprecate.py +171 -0
- package/lib/site-packages/trio/_dtls.py +1380 -0
- package/lib/site-packages/trio/_file_io.py +513 -0
- package/lib/site-packages/trio/_highlevel_generic.py +125 -0
- package/lib/site-packages/trio/_highlevel_open_tcp_listeners.py +251 -0
- package/lib/site-packages/trio/_highlevel_open_tcp_stream.py +397 -0
- package/lib/site-packages/trio/_highlevel_open_unix_stream.py +65 -0
- package/lib/site-packages/trio/_highlevel_serve_listeners.py +148 -0
- package/lib/site-packages/trio/_highlevel_socket.py +423 -0
- package/lib/site-packages/trio/_highlevel_ssl_helpers.py +180 -0
- package/lib/site-packages/trio/_path.py +289 -0
- package/lib/site-packages/trio/_repl.py +159 -0
- package/lib/site-packages/trio/_signals.py +185 -0
- package/lib/site-packages/trio/_socket.py +1326 -0
- package/lib/site-packages/trio/_ssl.py +964 -0
- package/lib/site-packages/trio/_subprocess.py +1178 -0
- package/lib/site-packages/trio/_subprocess_platform/__init__.py +123 -0
- package/lib/site-packages/trio/_subprocess_platform/kqueue.py +48 -0
- package/lib/site-packages/trio/_subprocess_platform/waitid.py +113 -0
- package/lib/site-packages/trio/_subprocess_platform/windows.py +11 -0
- package/lib/site-packages/trio/_sync.py +908 -0
- package/lib/site-packages/trio/_tests/__init__.py +0 -0
- package/lib/site-packages/trio/_tests/astrill-codesigning-cert.cer +0 -0
- package/lib/site-packages/trio/_tests/check_type_completeness.py +247 -0
- package/lib/site-packages/trio/_tests/module_with_deprecations.py +22 -0
- package/lib/site-packages/trio/_tests/pytest_plugin.py +54 -0
- package/lib/site-packages/trio/_tests/test_abc.py +72 -0
- package/lib/site-packages/trio/_tests/test_channel.py +750 -0
- package/lib/site-packages/trio/_tests/test_contextvars.py +56 -0
- package/lib/site-packages/trio/_tests/test_deprecate.py +277 -0
- package/lib/site-packages/trio/_tests/test_deprecate_strict_exception_groups_false.py +64 -0
- package/lib/site-packages/trio/_tests/test_dtls.py +950 -0
- package/lib/site-packages/trio/_tests/test_exports.py +626 -0
- package/lib/site-packages/trio/_tests/test_fakenet.py +317 -0
- package/lib/site-packages/trio/_tests/test_file_io.py +269 -0
- package/lib/site-packages/trio/_tests/test_highlevel_generic.py +98 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_listeners.py +419 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_stream.py +693 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_unix_stream.py +86 -0
- package/lib/site-packages/trio/_tests/test_highlevel_serve_listeners.py +186 -0
- package/lib/site-packages/trio/_tests/test_highlevel_socket.py +336 -0
- package/lib/site-packages/trio/_tests/test_highlevel_ssl_helpers.py +169 -0
- package/lib/site-packages/trio/_tests/test_path.py +279 -0
- package/lib/site-packages/trio/_tests/test_repl.py +428 -0
- package/lib/site-packages/trio/_tests/test_scheduler_determinism.py +47 -0
- package/lib/site-packages/trio/_tests/test_signals.py +186 -0
- package/lib/site-packages/trio/_tests/test_socket.py +1253 -0
- package/lib/site-packages/trio/_tests/test_ssl.py +1371 -0
- package/lib/site-packages/trio/_tests/test_subprocess.py +767 -0
- package/lib/site-packages/trio/_tests/test_sync.py +735 -0
- package/lib/site-packages/trio/_tests/test_testing.py +682 -0
- package/lib/site-packages/trio/_tests/test_testing_raisesgroup.py +1128 -0
- package/lib/site-packages/trio/_tests/test_threads.py +1173 -0
- package/lib/site-packages/trio/_tests/test_timeouts.py +281 -0
- package/lib/site-packages/trio/_tests/test_tracing.py +88 -0
- package/lib/site-packages/trio/_tests/test_trio.py +8 -0
- package/lib/site-packages/trio/_tests/test_unix_pipes.py +288 -0
- package/lib/site-packages/trio/_tests/test_util.py +349 -0
- package/lib/site-packages/trio/_tests/test_wait_for_object.py +225 -0
- package/lib/site-packages/trio/_tests/test_windows_pipes.py +112 -0
- package/lib/site-packages/trio/_tests/tools/__init__.py +0 -0
- package/lib/site-packages/trio/_tests/tools/test_gen_exports.py +179 -0
- package/lib/site-packages/trio/_tests/tools/test_mypy_annotate.py +140 -0
- package/lib/site-packages/trio/_tests/tools/test_sync_requirements.py +80 -0
- package/lib/site-packages/trio/_tests/type_tests/check_wraps.py +9 -0
- package/lib/site-packages/trio/_tests/type_tests/open_memory_channel.py +4 -0
- package/lib/site-packages/trio/_tests/type_tests/path.py +140 -0
- package/lib/site-packages/trio/_tests/type_tests/subprocesses.py +23 -0
- package/lib/site-packages/trio/_tests/type_tests/task_status.py +29 -0
- package/lib/site-packages/trio/_threads.py +610 -0
- package/lib/site-packages/trio/_timeouts.py +197 -0
- package/lib/site-packages/trio/_tools/__init__.py +0 -0
- package/lib/site-packages/trio/_tools/gen_exports.py +401 -0
- package/lib/site-packages/trio/_tools/mypy_annotate.py +126 -0
- package/lib/site-packages/trio/_tools/sync_requirements.py +98 -0
- package/lib/site-packages/trio/_tools/windows_ffi_build.py +220 -0
- package/lib/site-packages/trio/_unix_pipes.py +197 -0
- package/lib/site-packages/trio/_util.py +385 -0
- package/lib/site-packages/trio/_version.py +3 -0
- package/lib/site-packages/trio/_wait_for_object.py +67 -0
- package/lib/site-packages/trio/_windows_pipes.py +144 -0
- package/lib/site-packages/trio/abc.py +23 -0
- package/lib/site-packages/trio/from_thread.py +13 -0
- package/lib/site-packages/trio/lowlevel.py +95 -0
- package/lib/site-packages/trio/py.typed +0 -0
- package/lib/site-packages/trio/socket.py +602 -0
- package/lib/site-packages/trio/testing/__init__.py +58 -0
- package/lib/site-packages/trio/testing/_check_streams.py +570 -0
- package/lib/site-packages/trio/testing/_checkpoints.py +69 -0
- package/lib/site-packages/trio/testing/_fake_net.py +584 -0
- package/lib/site-packages/trio/testing/_memory_streams.py +633 -0
- package/lib/site-packages/trio/testing/_network.py +36 -0
- package/lib/site-packages/trio/testing/_raises_group.py +1015 -0
- package/lib/site-packages/trio/testing/_sequencer.py +87 -0
- package/lib/site-packages/trio/testing/_trio_test.py +50 -0
- package/lib/site-packages/trio/to_thread.py +4 -0
- package/lib/site-packages/trio-0.33.0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/trio-0.33.0.dist-info/METADATA +186 -0
- package/lib/site-packages/trio-0.33.0.dist-info/RECORD +156 -0
- package/lib/site-packages/trio-0.33.0.dist-info/REQUESTED +0 -0
- package/lib/site-packages/trio-0.33.0.dist-info/WHEEL +5 -0
- package/lib/site-packages/trio-0.33.0.dist-info/entry_points.txt +2 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE +3 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.MIT +22 -0
- package/lib/site-packages/trio-0.33.0.dist-info/top_level.txt +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Uses `from x import y as y` for compatibility with `pyright --verifytypes` (#2625)
|
|
2
|
+
|
|
3
|
+
from .. import _deprecate as _deprecate
|
|
4
|
+
from .._core import (
|
|
5
|
+
MockClock as MockClock,
|
|
6
|
+
wait_all_tasks_blocked as wait_all_tasks_blocked,
|
|
7
|
+
)
|
|
8
|
+
from .._threads import (
|
|
9
|
+
active_thread_count as active_thread_count,
|
|
10
|
+
wait_all_threads_completed as wait_all_threads_completed,
|
|
11
|
+
)
|
|
12
|
+
from .._util import fixup_module_metadata
|
|
13
|
+
from ._check_streams import (
|
|
14
|
+
check_half_closeable_stream as check_half_closeable_stream,
|
|
15
|
+
check_one_way_stream as check_one_way_stream,
|
|
16
|
+
check_two_way_stream as check_two_way_stream,
|
|
17
|
+
)
|
|
18
|
+
from ._checkpoints import (
|
|
19
|
+
assert_checkpoints as assert_checkpoints,
|
|
20
|
+
assert_no_checkpoints as assert_no_checkpoints,
|
|
21
|
+
)
|
|
22
|
+
from ._memory_streams import (
|
|
23
|
+
MemoryReceiveStream as MemoryReceiveStream,
|
|
24
|
+
MemorySendStream as MemorySendStream,
|
|
25
|
+
lockstep_stream_one_way_pair as lockstep_stream_one_way_pair,
|
|
26
|
+
lockstep_stream_pair as lockstep_stream_pair,
|
|
27
|
+
memory_stream_one_way_pair as memory_stream_one_way_pair,
|
|
28
|
+
memory_stream_pair as memory_stream_pair,
|
|
29
|
+
memory_stream_pump as memory_stream_pump,
|
|
30
|
+
)
|
|
31
|
+
from ._network import open_stream_to_socket_listener as open_stream_to_socket_listener
|
|
32
|
+
from ._raises_group import Matcher as _Matcher, RaisesGroup as _RaisesGroup
|
|
33
|
+
from ._sequencer import Sequencer as Sequencer
|
|
34
|
+
from ._trio_test import trio_test as trio_test
|
|
35
|
+
|
|
36
|
+
################################################################
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
_deprecate.deprecate_attributes(
|
|
40
|
+
__name__,
|
|
41
|
+
{
|
|
42
|
+
"RaisesGroup": _deprecate.DeprecatedAttribute(
|
|
43
|
+
_RaisesGroup,
|
|
44
|
+
version="0.33.0",
|
|
45
|
+
issue=3326,
|
|
46
|
+
instead="See https://docs.pytest.org/en/stable/reference/reference.html#pytest.RaisesGroup",
|
|
47
|
+
),
|
|
48
|
+
"Matcher": _deprecate.DeprecatedAttribute(
|
|
49
|
+
_Matcher,
|
|
50
|
+
version="0.33.0",
|
|
51
|
+
issue=3326,
|
|
52
|
+
instead="See https://docs.pytest.org/en/stable/reference/reference.html#pytest.RaisesExc",
|
|
53
|
+
),
|
|
54
|
+
},
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
fixup_module_metadata(__name__, globals())
|
|
58
|
+
del fixup_module_metadata
|
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
# Generic stream tests
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import random
|
|
5
|
+
import sys
|
|
6
|
+
from collections.abc import Awaitable, Callable, Generator
|
|
7
|
+
from contextlib import contextmanager, suppress
|
|
8
|
+
from typing import (
|
|
9
|
+
TYPE_CHECKING,
|
|
10
|
+
Generic,
|
|
11
|
+
TypeAlias,
|
|
12
|
+
TypeVar,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from .. import CancelScope, _core
|
|
16
|
+
from .._abc import AsyncResource, HalfCloseableStream, ReceiveStream, SendStream, Stream
|
|
17
|
+
from .._highlevel_generic import aclose_forcefully
|
|
18
|
+
from ._checkpoints import assert_checkpoints
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from types import TracebackType
|
|
22
|
+
|
|
23
|
+
from typing_extensions import ParamSpec
|
|
24
|
+
|
|
25
|
+
ArgsT = ParamSpec("ArgsT")
|
|
26
|
+
|
|
27
|
+
if sys.version_info < (3, 11):
|
|
28
|
+
from exceptiongroup import BaseExceptionGroup
|
|
29
|
+
|
|
30
|
+
Res1 = TypeVar("Res1", bound=AsyncResource)
|
|
31
|
+
Res2 = TypeVar("Res2", bound=AsyncResource)
|
|
32
|
+
StreamMaker: TypeAlias = Callable[[], Awaitable[tuple[Res1, Res2]]]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class _ForceCloseBoth(Generic[Res1, Res2]):
|
|
36
|
+
def __init__(self, both: tuple[Res1, Res2]) -> None:
|
|
37
|
+
self._first, self._second = both
|
|
38
|
+
|
|
39
|
+
async def __aenter__(self) -> tuple[Res1, Res2]:
|
|
40
|
+
return self._first, self._second
|
|
41
|
+
|
|
42
|
+
async def __aexit__(
|
|
43
|
+
self,
|
|
44
|
+
exc_type: type[BaseException] | None,
|
|
45
|
+
exc_value: BaseException | None,
|
|
46
|
+
traceback: TracebackType | None,
|
|
47
|
+
) -> None:
|
|
48
|
+
try:
|
|
49
|
+
await aclose_forcefully(self._first)
|
|
50
|
+
finally:
|
|
51
|
+
await aclose_forcefully(self._second)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# This is used in this file instead of pytest.raises in order to avoid a dependency
|
|
55
|
+
# on pytest, as the check_* functions are publicly exported.
|
|
56
|
+
@contextmanager
|
|
57
|
+
def _assert_raises(
|
|
58
|
+
expected_exc: type[BaseException],
|
|
59
|
+
wrapped: bool = False,
|
|
60
|
+
) -> Generator[None, None, None]:
|
|
61
|
+
__tracebackhide__ = True
|
|
62
|
+
try:
|
|
63
|
+
yield
|
|
64
|
+
except BaseExceptionGroup as exc:
|
|
65
|
+
assert wrapped, "caught exceptiongroup, but expected an unwrapped exception"
|
|
66
|
+
# assert in except block ignored below
|
|
67
|
+
assert len(exc.exceptions) == 1 # noqa: PT017
|
|
68
|
+
assert isinstance(exc.exceptions[0], expected_exc) # noqa: PT017
|
|
69
|
+
except expected_exc:
|
|
70
|
+
assert not wrapped, "caught exception, but expected an exceptiongroup"
|
|
71
|
+
else:
|
|
72
|
+
raise AssertionError(f"expected exception: {expected_exc}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
async def check_one_way_stream(
|
|
76
|
+
stream_maker: StreamMaker[SendStream, ReceiveStream],
|
|
77
|
+
clogged_stream_maker: StreamMaker[SendStream, ReceiveStream] | None,
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Perform a number of generic tests on a custom one-way stream
|
|
80
|
+
implementation.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
stream_maker: An async (!) function which returns a connected
|
|
84
|
+
(:class:`~trio.abc.SendStream`, :class:`~trio.abc.ReceiveStream`)
|
|
85
|
+
pair.
|
|
86
|
+
clogged_stream_maker: Either None, or an async function similar to
|
|
87
|
+
stream_maker, but with the extra property that the returned stream
|
|
88
|
+
is in a state where ``send_all`` and
|
|
89
|
+
``wait_send_all_might_not_block`` will block until ``receive_some``
|
|
90
|
+
has been called. This allows for more thorough testing of some edge
|
|
91
|
+
cases, especially around ``wait_send_all_might_not_block``.
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
AssertionError: if a test fails.
|
|
95
|
+
|
|
96
|
+
"""
|
|
97
|
+
async with _ForceCloseBoth(await stream_maker()) as (s, r):
|
|
98
|
+
assert isinstance(s, SendStream)
|
|
99
|
+
assert isinstance(r, ReceiveStream)
|
|
100
|
+
|
|
101
|
+
async def do_send_all(data: bytes | bytearray | memoryview) -> None:
|
|
102
|
+
with assert_checkpoints(): # We're testing that it doesn't return anything.
|
|
103
|
+
assert await s.send_all(data) is None # type: ignore[func-returns-value]
|
|
104
|
+
|
|
105
|
+
async def do_receive_some(max_bytes: int | None = None) -> bytes | bytearray:
|
|
106
|
+
with assert_checkpoints():
|
|
107
|
+
return await r.receive_some(max_bytes)
|
|
108
|
+
|
|
109
|
+
async def checked_receive_1(expected: bytes) -> None:
|
|
110
|
+
assert await do_receive_some(1) == expected
|
|
111
|
+
|
|
112
|
+
async def do_aclose(resource: AsyncResource) -> None:
|
|
113
|
+
with assert_checkpoints():
|
|
114
|
+
await resource.aclose()
|
|
115
|
+
|
|
116
|
+
# Simple sending/receiving
|
|
117
|
+
async with _core.open_nursery() as nursery:
|
|
118
|
+
nursery.start_soon(do_send_all, b"x")
|
|
119
|
+
nursery.start_soon(checked_receive_1, b"x")
|
|
120
|
+
|
|
121
|
+
async def send_empty_then_y() -> None:
|
|
122
|
+
# Streams should tolerate sending b"" without giving it any
|
|
123
|
+
# special meaning.
|
|
124
|
+
await do_send_all(b"")
|
|
125
|
+
await do_send_all(b"y")
|
|
126
|
+
|
|
127
|
+
async with _core.open_nursery() as nursery:
|
|
128
|
+
nursery.start_soon(send_empty_then_y)
|
|
129
|
+
nursery.start_soon(checked_receive_1, b"y")
|
|
130
|
+
|
|
131
|
+
# ---- Checking various argument types ----
|
|
132
|
+
|
|
133
|
+
# send_all accepts bytearray and memoryview
|
|
134
|
+
async with _core.open_nursery() as nursery:
|
|
135
|
+
nursery.start_soon(do_send_all, bytearray(b"1"))
|
|
136
|
+
nursery.start_soon(checked_receive_1, b"1")
|
|
137
|
+
|
|
138
|
+
async with _core.open_nursery() as nursery:
|
|
139
|
+
nursery.start_soon(do_send_all, memoryview(b"2"))
|
|
140
|
+
nursery.start_soon(checked_receive_1, b"2")
|
|
141
|
+
|
|
142
|
+
# max_bytes must be a positive integer
|
|
143
|
+
with _assert_raises(ValueError):
|
|
144
|
+
await r.receive_some(-1)
|
|
145
|
+
with _assert_raises(ValueError):
|
|
146
|
+
await r.receive_some(0)
|
|
147
|
+
with _assert_raises(TypeError):
|
|
148
|
+
await r.receive_some(1.5) # type: ignore[arg-type]
|
|
149
|
+
# it can also be missing or None
|
|
150
|
+
async with _core.open_nursery() as nursery:
|
|
151
|
+
nursery.start_soon(do_send_all, b"x")
|
|
152
|
+
assert await do_receive_some() == b"x"
|
|
153
|
+
async with _core.open_nursery() as nursery:
|
|
154
|
+
nursery.start_soon(do_send_all, b"x")
|
|
155
|
+
assert await do_receive_some(None) == b"x"
|
|
156
|
+
|
|
157
|
+
with _assert_raises(_core.BusyResourceError, wrapped=True):
|
|
158
|
+
async with _core.open_nursery() as nursery:
|
|
159
|
+
nursery.start_soon(do_receive_some, 1)
|
|
160
|
+
nursery.start_soon(do_receive_some, 1)
|
|
161
|
+
|
|
162
|
+
# Method always has to exist, and an empty stream with a blocked
|
|
163
|
+
# receive_some should *always* allow send_all. (Technically it's legal
|
|
164
|
+
# for send_all to wait until receive_some is called to run, though; a
|
|
165
|
+
# stream doesn't *have* to have any internal buffering. That's why we
|
|
166
|
+
# start a concurrent receive_some call, then cancel it.)
|
|
167
|
+
async def simple_check_wait_send_all_might_not_block(
|
|
168
|
+
scope: CancelScope,
|
|
169
|
+
) -> None:
|
|
170
|
+
with assert_checkpoints():
|
|
171
|
+
await s.wait_send_all_might_not_block()
|
|
172
|
+
scope.cancel()
|
|
173
|
+
|
|
174
|
+
async with _core.open_nursery() as nursery:
|
|
175
|
+
nursery.start_soon(
|
|
176
|
+
simple_check_wait_send_all_might_not_block,
|
|
177
|
+
nursery.cancel_scope,
|
|
178
|
+
)
|
|
179
|
+
nursery.start_soon(do_receive_some, 1)
|
|
180
|
+
|
|
181
|
+
# closing the r side leads to BrokenResourceError on the s side
|
|
182
|
+
# (eventually)
|
|
183
|
+
async def expect_broken_stream_on_send() -> None:
|
|
184
|
+
with _assert_raises(_core.BrokenResourceError):
|
|
185
|
+
while True:
|
|
186
|
+
await do_send_all(b"x" * 100)
|
|
187
|
+
|
|
188
|
+
async with _core.open_nursery() as nursery:
|
|
189
|
+
nursery.start_soon(expect_broken_stream_on_send)
|
|
190
|
+
nursery.start_soon(do_aclose, r)
|
|
191
|
+
|
|
192
|
+
# once detected, the stream stays broken
|
|
193
|
+
with _assert_raises(_core.BrokenResourceError):
|
|
194
|
+
await do_send_all(b"x" * 100)
|
|
195
|
+
|
|
196
|
+
# r closed -> ClosedResourceError on the receive side
|
|
197
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
198
|
+
await do_receive_some(4096)
|
|
199
|
+
|
|
200
|
+
# we can close the same stream repeatedly, it's fine
|
|
201
|
+
await do_aclose(r)
|
|
202
|
+
await do_aclose(r)
|
|
203
|
+
|
|
204
|
+
# closing the sender side
|
|
205
|
+
await do_aclose(s)
|
|
206
|
+
|
|
207
|
+
# now trying to send raises ClosedResourceError
|
|
208
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
209
|
+
await do_send_all(b"x" * 100)
|
|
210
|
+
|
|
211
|
+
# even if it's an empty send
|
|
212
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
213
|
+
await do_send_all(b"")
|
|
214
|
+
|
|
215
|
+
# ditto for wait_send_all_might_not_block
|
|
216
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
217
|
+
with assert_checkpoints():
|
|
218
|
+
await s.wait_send_all_might_not_block()
|
|
219
|
+
|
|
220
|
+
# and again, repeated closing is fine
|
|
221
|
+
await do_aclose(s)
|
|
222
|
+
await do_aclose(s)
|
|
223
|
+
|
|
224
|
+
async with _ForceCloseBoth(await stream_maker()) as (s, r):
|
|
225
|
+
# if send-then-graceful-close, receiver gets data then b""
|
|
226
|
+
async def send_then_close() -> None:
|
|
227
|
+
await do_send_all(b"y")
|
|
228
|
+
await do_aclose(s)
|
|
229
|
+
|
|
230
|
+
async def receive_send_then_close() -> None:
|
|
231
|
+
# We want to make sure that if the sender closes the stream before
|
|
232
|
+
# we read anything, then we still get all the data. But some
|
|
233
|
+
# streams might block on the do_send_all call. So we let the
|
|
234
|
+
# sender get as far as it can, then we receive.
|
|
235
|
+
await _core.wait_all_tasks_blocked()
|
|
236
|
+
await checked_receive_1(b"y")
|
|
237
|
+
await checked_receive_1(b"")
|
|
238
|
+
await do_aclose(r)
|
|
239
|
+
|
|
240
|
+
async with _core.open_nursery() as nursery:
|
|
241
|
+
nursery.start_soon(send_then_close)
|
|
242
|
+
nursery.start_soon(receive_send_then_close)
|
|
243
|
+
|
|
244
|
+
async with _ForceCloseBoth(await stream_maker()) as (s, r):
|
|
245
|
+
await aclose_forcefully(r)
|
|
246
|
+
|
|
247
|
+
with _assert_raises(_core.BrokenResourceError):
|
|
248
|
+
while True:
|
|
249
|
+
await do_send_all(b"x" * 100)
|
|
250
|
+
|
|
251
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
252
|
+
await do_receive_some(4096)
|
|
253
|
+
|
|
254
|
+
async with _ForceCloseBoth(await stream_maker()) as (s, r):
|
|
255
|
+
await aclose_forcefully(s)
|
|
256
|
+
|
|
257
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
258
|
+
await do_send_all(b"123")
|
|
259
|
+
|
|
260
|
+
# after the sender does a forceful close, the receiver might either
|
|
261
|
+
# get BrokenResourceError or a clean b""; either is OK. Not OK would be
|
|
262
|
+
# if it freezes, or returns data.
|
|
263
|
+
with suppress(_core.BrokenResourceError):
|
|
264
|
+
await checked_receive_1(b"")
|
|
265
|
+
|
|
266
|
+
# cancelled aclose still closes
|
|
267
|
+
async with _ForceCloseBoth(await stream_maker()) as (s, r):
|
|
268
|
+
with _core.CancelScope() as scope:
|
|
269
|
+
scope.cancel()
|
|
270
|
+
await r.aclose()
|
|
271
|
+
|
|
272
|
+
with _core.CancelScope() as scope:
|
|
273
|
+
scope.cancel()
|
|
274
|
+
await s.aclose()
|
|
275
|
+
|
|
276
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
277
|
+
await do_send_all(b"123")
|
|
278
|
+
|
|
279
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
280
|
+
await do_receive_some(4096)
|
|
281
|
+
|
|
282
|
+
# Check that we can still gracefully close a stream after an operation has
|
|
283
|
+
# been cancelled. This can be challenging if cancellation can leave the
|
|
284
|
+
# stream internals in an inconsistent state, e.g. for
|
|
285
|
+
# SSLStream. Unfortunately this test isn't very thorough; the really
|
|
286
|
+
# challenging case for something like SSLStream is it gets cancelled
|
|
287
|
+
# *while* it's sending data on the underlying, not before. But testing
|
|
288
|
+
# that requires some special-case handling of the particular stream setup;
|
|
289
|
+
# we can't do it here. Maybe we could do a bit better with
|
|
290
|
+
# https://github.com/python-trio/trio/issues/77
|
|
291
|
+
async with _ForceCloseBoth(await stream_maker()) as (s, r):
|
|
292
|
+
|
|
293
|
+
async def expect_cancelled(
|
|
294
|
+
afn: Callable[ArgsT, Awaitable[object]],
|
|
295
|
+
*args: ArgsT.args,
|
|
296
|
+
**kwargs: ArgsT.kwargs,
|
|
297
|
+
) -> None:
|
|
298
|
+
with _assert_raises(_core.Cancelled):
|
|
299
|
+
await afn(*args, **kwargs)
|
|
300
|
+
|
|
301
|
+
with _core.CancelScope() as scope:
|
|
302
|
+
scope.cancel()
|
|
303
|
+
async with _core.open_nursery() as nursery:
|
|
304
|
+
nursery.start_soon(expect_cancelled, do_send_all, b"x")
|
|
305
|
+
nursery.start_soon(expect_cancelled, do_receive_some, 1)
|
|
306
|
+
|
|
307
|
+
async with _core.open_nursery() as nursery:
|
|
308
|
+
nursery.start_soon(do_aclose, s)
|
|
309
|
+
nursery.start_soon(do_aclose, r)
|
|
310
|
+
|
|
311
|
+
# Check that if a task is blocked in receive_some, then closing the
|
|
312
|
+
# receive stream causes it to wake up.
|
|
313
|
+
async with _ForceCloseBoth(await stream_maker()) as (s, r):
|
|
314
|
+
|
|
315
|
+
async def receive_expecting_closed() -> None:
|
|
316
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
317
|
+
await r.receive_some(10)
|
|
318
|
+
|
|
319
|
+
async with _core.open_nursery() as nursery:
|
|
320
|
+
nursery.start_soon(receive_expecting_closed)
|
|
321
|
+
await _core.wait_all_tasks_blocked()
|
|
322
|
+
await aclose_forcefully(r)
|
|
323
|
+
|
|
324
|
+
# check wait_send_all_might_not_block, if we can
|
|
325
|
+
if clogged_stream_maker is not None:
|
|
326
|
+
async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):
|
|
327
|
+
record: list[str] = []
|
|
328
|
+
|
|
329
|
+
async def waiter(cancel_scope: CancelScope) -> None:
|
|
330
|
+
record.append("waiter sleeping")
|
|
331
|
+
with assert_checkpoints():
|
|
332
|
+
await s.wait_send_all_might_not_block()
|
|
333
|
+
record.append("waiter wokeup")
|
|
334
|
+
cancel_scope.cancel()
|
|
335
|
+
|
|
336
|
+
async def receiver() -> None:
|
|
337
|
+
# give wait_send_all_might_not_block a chance to block
|
|
338
|
+
await _core.wait_all_tasks_blocked()
|
|
339
|
+
record.append("receiver starting")
|
|
340
|
+
while True:
|
|
341
|
+
await r.receive_some(16834)
|
|
342
|
+
|
|
343
|
+
async with _core.open_nursery() as nursery:
|
|
344
|
+
nursery.start_soon(waiter, nursery.cancel_scope)
|
|
345
|
+
await _core.wait_all_tasks_blocked()
|
|
346
|
+
nursery.start_soon(receiver)
|
|
347
|
+
|
|
348
|
+
assert record == [
|
|
349
|
+
"waiter sleeping",
|
|
350
|
+
"receiver starting",
|
|
351
|
+
"waiter wokeup",
|
|
352
|
+
]
|
|
353
|
+
|
|
354
|
+
async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):
|
|
355
|
+
# simultaneous wait_send_all_might_not_block fails
|
|
356
|
+
with _assert_raises(_core.BusyResourceError, wrapped=True):
|
|
357
|
+
async with _core.open_nursery() as nursery:
|
|
358
|
+
nursery.start_soon(s.wait_send_all_might_not_block)
|
|
359
|
+
nursery.start_soon(s.wait_send_all_might_not_block)
|
|
360
|
+
|
|
361
|
+
# and simultaneous send_all and wait_send_all_might_not_block (NB
|
|
362
|
+
# this test might destroy the stream b/c we end up cancelling
|
|
363
|
+
# send_all and e.g. SSLStream can't handle that, so we have to
|
|
364
|
+
# recreate afterwards)
|
|
365
|
+
with _assert_raises(_core.BusyResourceError, wrapped=True):
|
|
366
|
+
async with _core.open_nursery() as nursery:
|
|
367
|
+
nursery.start_soon(s.wait_send_all_might_not_block)
|
|
368
|
+
nursery.start_soon(s.send_all, b"123")
|
|
369
|
+
|
|
370
|
+
async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):
|
|
371
|
+
# send_all and send_all blocked simultaneously should also raise
|
|
372
|
+
# (but again this might destroy the stream)
|
|
373
|
+
with _assert_raises(_core.BusyResourceError, wrapped=True):
|
|
374
|
+
async with _core.open_nursery() as nursery:
|
|
375
|
+
nursery.start_soon(s.send_all, b"123")
|
|
376
|
+
nursery.start_soon(s.send_all, b"123")
|
|
377
|
+
|
|
378
|
+
# closing the receiver causes wait_send_all_might_not_block to return,
|
|
379
|
+
# with or without an exception
|
|
380
|
+
async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):
|
|
381
|
+
|
|
382
|
+
async def sender() -> None:
|
|
383
|
+
try:
|
|
384
|
+
with assert_checkpoints():
|
|
385
|
+
await s.wait_send_all_might_not_block()
|
|
386
|
+
except _core.BrokenResourceError: # pragma: no cover
|
|
387
|
+
pass
|
|
388
|
+
|
|
389
|
+
async def receiver() -> None:
|
|
390
|
+
await _core.wait_all_tasks_blocked()
|
|
391
|
+
await aclose_forcefully(r)
|
|
392
|
+
|
|
393
|
+
async with _core.open_nursery() as nursery:
|
|
394
|
+
nursery.start_soon(sender)
|
|
395
|
+
nursery.start_soon(receiver)
|
|
396
|
+
|
|
397
|
+
# and again with the call starting after the close
|
|
398
|
+
async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):
|
|
399
|
+
await aclose_forcefully(r)
|
|
400
|
+
try:
|
|
401
|
+
with assert_checkpoints():
|
|
402
|
+
await s.wait_send_all_might_not_block()
|
|
403
|
+
except _core.BrokenResourceError: # pragma: no cover
|
|
404
|
+
pass
|
|
405
|
+
|
|
406
|
+
# Check that if a task is blocked in a send-side method, then closing
|
|
407
|
+
# the send stream causes it to wake up.
|
|
408
|
+
async def close_soon(s: SendStream) -> None:
|
|
409
|
+
await _core.wait_all_tasks_blocked()
|
|
410
|
+
await aclose_forcefully(s)
|
|
411
|
+
|
|
412
|
+
async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):
|
|
413
|
+
async with _core.open_nursery() as nursery:
|
|
414
|
+
nursery.start_soon(close_soon, s)
|
|
415
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
416
|
+
await s.send_all(b"xyzzy")
|
|
417
|
+
|
|
418
|
+
async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):
|
|
419
|
+
async with _core.open_nursery() as nursery:
|
|
420
|
+
nursery.start_soon(close_soon, s)
|
|
421
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
422
|
+
await s.wait_send_all_might_not_block()
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
async def check_two_way_stream(
|
|
426
|
+
stream_maker: StreamMaker[Stream, Stream],
|
|
427
|
+
clogged_stream_maker: StreamMaker[Stream, Stream] | None,
|
|
428
|
+
) -> None:
|
|
429
|
+
"""Perform a number of generic tests on a custom two-way stream
|
|
430
|
+
implementation.
|
|
431
|
+
|
|
432
|
+
This is similar to :func:`check_one_way_stream`, except that the maker
|
|
433
|
+
functions are expected to return objects implementing the
|
|
434
|
+
:class:`~trio.abc.Stream` interface.
|
|
435
|
+
|
|
436
|
+
This function tests a *superset* of what :func:`check_one_way_stream`
|
|
437
|
+
checks – if you call this, then you don't need to also call
|
|
438
|
+
:func:`check_one_way_stream`.
|
|
439
|
+
|
|
440
|
+
"""
|
|
441
|
+
await check_one_way_stream(stream_maker, clogged_stream_maker)
|
|
442
|
+
|
|
443
|
+
async def flipped_stream_maker() -> tuple[Stream, Stream]:
|
|
444
|
+
return (await stream_maker())[::-1]
|
|
445
|
+
|
|
446
|
+
flipped_clogged_stream_maker: Callable[[], Awaitable[tuple[Stream, Stream]]] | None
|
|
447
|
+
|
|
448
|
+
if clogged_stream_maker is not None:
|
|
449
|
+
|
|
450
|
+
async def flipped_clogged_stream_maker() -> tuple[Stream, Stream]:
|
|
451
|
+
return (await clogged_stream_maker())[::-1]
|
|
452
|
+
|
|
453
|
+
else:
|
|
454
|
+
flipped_clogged_stream_maker = None
|
|
455
|
+
await check_one_way_stream(flipped_stream_maker, flipped_clogged_stream_maker)
|
|
456
|
+
|
|
457
|
+
async with _ForceCloseBoth(await stream_maker()) as (s1, s2):
|
|
458
|
+
assert isinstance(s1, Stream)
|
|
459
|
+
assert isinstance(s2, Stream)
|
|
460
|
+
|
|
461
|
+
# Duplex can be a bit tricky, might as well check it as well
|
|
462
|
+
DUPLEX_TEST_SIZE = 2**20
|
|
463
|
+
CHUNK_SIZE_MAX = 2**14
|
|
464
|
+
|
|
465
|
+
r = random.Random(0)
|
|
466
|
+
i = r.getrandbits(8 * DUPLEX_TEST_SIZE)
|
|
467
|
+
test_data = i.to_bytes(DUPLEX_TEST_SIZE, "little")
|
|
468
|
+
|
|
469
|
+
async def sender(
|
|
470
|
+
s: Stream,
|
|
471
|
+
data: bytes | bytearray | memoryview,
|
|
472
|
+
seed: int,
|
|
473
|
+
) -> None:
|
|
474
|
+
r = random.Random(seed)
|
|
475
|
+
m = memoryview(data)
|
|
476
|
+
while m:
|
|
477
|
+
chunk_size = r.randint(1, CHUNK_SIZE_MAX)
|
|
478
|
+
await s.send_all(m[:chunk_size])
|
|
479
|
+
m = m[chunk_size:]
|
|
480
|
+
|
|
481
|
+
async def receiver(s: Stream, data: bytes | bytearray, seed: int) -> None:
|
|
482
|
+
r = random.Random(seed)
|
|
483
|
+
got = bytearray()
|
|
484
|
+
while len(got) < len(data):
|
|
485
|
+
chunk = await s.receive_some(r.randint(1, CHUNK_SIZE_MAX))
|
|
486
|
+
assert chunk
|
|
487
|
+
got += chunk
|
|
488
|
+
assert got == data
|
|
489
|
+
|
|
490
|
+
async with _core.open_nursery() as nursery:
|
|
491
|
+
nursery.start_soon(sender, s1, test_data, 0)
|
|
492
|
+
nursery.start_soon(sender, s2, test_data[::-1], 1)
|
|
493
|
+
nursery.start_soon(receiver, s1, test_data[::-1], 2)
|
|
494
|
+
nursery.start_soon(receiver, s2, test_data, 3)
|
|
495
|
+
|
|
496
|
+
async def expect_receive_some_empty() -> None:
|
|
497
|
+
assert await s2.receive_some(10) == b""
|
|
498
|
+
await s2.aclose()
|
|
499
|
+
|
|
500
|
+
async with _core.open_nursery() as nursery:
|
|
501
|
+
nursery.start_soon(expect_receive_some_empty)
|
|
502
|
+
nursery.start_soon(s1.aclose)
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
async def check_half_closeable_stream(
|
|
506
|
+
stream_maker: StreamMaker[HalfCloseableStream, HalfCloseableStream],
|
|
507
|
+
clogged_stream_maker: StreamMaker[HalfCloseableStream, HalfCloseableStream] | None,
|
|
508
|
+
) -> None:
|
|
509
|
+
"""Perform a number of generic tests on a custom half-closeable stream
|
|
510
|
+
implementation.
|
|
511
|
+
|
|
512
|
+
This is similar to :func:`check_two_way_stream`, except that the maker
|
|
513
|
+
functions are expected to return objects that implement the
|
|
514
|
+
:class:`~trio.abc.HalfCloseableStream` interface.
|
|
515
|
+
|
|
516
|
+
This function tests a *superset* of what :func:`check_two_way_stream`
|
|
517
|
+
checks – if you call this, then you don't need to also call
|
|
518
|
+
:func:`check_two_way_stream`.
|
|
519
|
+
|
|
520
|
+
"""
|
|
521
|
+
await check_two_way_stream(stream_maker, clogged_stream_maker)
|
|
522
|
+
|
|
523
|
+
async with _ForceCloseBoth(await stream_maker()) as (s1, s2):
|
|
524
|
+
assert isinstance(s1, HalfCloseableStream)
|
|
525
|
+
assert isinstance(s2, HalfCloseableStream)
|
|
526
|
+
|
|
527
|
+
async def send_x_then_eof(s: HalfCloseableStream) -> None:
|
|
528
|
+
await s.send_all(b"x")
|
|
529
|
+
with assert_checkpoints():
|
|
530
|
+
await s.send_eof()
|
|
531
|
+
|
|
532
|
+
async def expect_x_then_eof(r: HalfCloseableStream) -> None:
|
|
533
|
+
await _core.wait_all_tasks_blocked()
|
|
534
|
+
assert await r.receive_some(10) == b"x"
|
|
535
|
+
assert await r.receive_some(10) == b""
|
|
536
|
+
|
|
537
|
+
async with _core.open_nursery() as nursery:
|
|
538
|
+
nursery.start_soon(send_x_then_eof, s1)
|
|
539
|
+
nursery.start_soon(expect_x_then_eof, s2)
|
|
540
|
+
|
|
541
|
+
# now sending is disallowed
|
|
542
|
+
with _assert_raises(_core.ClosedResourceError):
|
|
543
|
+
await s1.send_all(b"y")
|
|
544
|
+
|
|
545
|
+
# but we can do send_eof again
|
|
546
|
+
with assert_checkpoints():
|
|
547
|
+
await s1.send_eof()
|
|
548
|
+
|
|
549
|
+
# and we can still send stuff back the other way
|
|
550
|
+
async with _core.open_nursery() as nursery:
|
|
551
|
+
nursery.start_soon(send_x_then_eof, s2)
|
|
552
|
+
nursery.start_soon(expect_x_then_eof, s1)
|
|
553
|
+
|
|
554
|
+
if clogged_stream_maker is not None:
|
|
555
|
+
async with _ForceCloseBoth(await clogged_stream_maker()) as (s1, s2):
|
|
556
|
+
# send_all and send_eof simultaneously is not ok
|
|
557
|
+
with _assert_raises(_core.BusyResourceError, wrapped=True):
|
|
558
|
+
async with _core.open_nursery() as nursery:
|
|
559
|
+
nursery.start_soon(s1.send_all, b"x")
|
|
560
|
+
await _core.wait_all_tasks_blocked()
|
|
561
|
+
nursery.start_soon(s1.send_eof)
|
|
562
|
+
|
|
563
|
+
async with _ForceCloseBoth(await clogged_stream_maker()) as (s1, s2):
|
|
564
|
+
# wait_send_all_might_not_block and send_eof simultaneously is not
|
|
565
|
+
# ok either
|
|
566
|
+
with _assert_raises(_core.BusyResourceError, wrapped=True):
|
|
567
|
+
async with _core.open_nursery() as nursery:
|
|
568
|
+
nursery.start_soon(s1.wait_send_all_might_not_block)
|
|
569
|
+
await _core.wait_all_tasks_blocked()
|
|
570
|
+
nursery.start_soon(s1.send_eof)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import AbstractContextManager, contextmanager
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from .. import _core
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from collections.abc import Generator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@contextmanager
|
|
13
|
+
def _assert_yields_or_not(expected: bool) -> Generator[None, None, None]:
|
|
14
|
+
"""Check if checkpoints are executed in a block of code."""
|
|
15
|
+
__tracebackhide__ = True
|
|
16
|
+
task = _core.current_task()
|
|
17
|
+
orig_cancel = task._cancel_points
|
|
18
|
+
orig_schedule = task._schedule_points
|
|
19
|
+
try:
|
|
20
|
+
yield
|
|
21
|
+
if expected and (
|
|
22
|
+
task._cancel_points == orig_cancel or task._schedule_points == orig_schedule
|
|
23
|
+
):
|
|
24
|
+
raise AssertionError("assert_checkpoints block did not yield!")
|
|
25
|
+
finally:
|
|
26
|
+
if not expected and (
|
|
27
|
+
task._cancel_points != orig_cancel or task._schedule_points != orig_schedule
|
|
28
|
+
):
|
|
29
|
+
raise AssertionError("assert_no_checkpoints block yielded!")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def assert_checkpoints() -> AbstractContextManager[None]:
|
|
33
|
+
"""Use as a context manager to check that the code inside the ``with``
|
|
34
|
+
block either exits with an exception or executes at least one
|
|
35
|
+
:ref:`checkpoint <checkpoints>`.
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
AssertionError: if no checkpoint was executed.
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
Check that :func:`trio.sleep` is a checkpoint, even if it doesn't
|
|
42
|
+
block::
|
|
43
|
+
|
|
44
|
+
with trio.testing.assert_checkpoints():
|
|
45
|
+
await trio.sleep(0)
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
__tracebackhide__ = True
|
|
49
|
+
return _assert_yields_or_not(True)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def assert_no_checkpoints() -> AbstractContextManager[None]:
|
|
53
|
+
"""Use as a context manager to check that the code inside the ``with``
|
|
54
|
+
block does not execute any :ref:`checkpoints <checkpoints>`.
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
AssertionError: if a checkpoint was executed.
|
|
58
|
+
|
|
59
|
+
Example:
|
|
60
|
+
Synchronous code never contains any checkpoints, but we can double-check
|
|
61
|
+
that::
|
|
62
|
+
|
|
63
|
+
send_channel, receive_channel = trio.open_memory_channel(10)
|
|
64
|
+
with trio.testing.assert_no_checkpoints():
|
|
65
|
+
send_channel.send_nowait(None)
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
__tracebackhide__ = True
|
|
69
|
+
return _assert_yields_or_not(False)
|