@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,281 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from typing import TYPE_CHECKING, Protocol, TypeVar
|
|
5
|
+
|
|
6
|
+
import outcome
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
import trio
|
|
10
|
+
|
|
11
|
+
from .. import _core
|
|
12
|
+
from .._core._tests.tutil import slow
|
|
13
|
+
from .._timeouts import (
|
|
14
|
+
TooSlowError,
|
|
15
|
+
fail_after,
|
|
16
|
+
fail_at,
|
|
17
|
+
move_on_after,
|
|
18
|
+
move_on_at,
|
|
19
|
+
sleep,
|
|
20
|
+
sleep_forever,
|
|
21
|
+
sleep_until,
|
|
22
|
+
)
|
|
23
|
+
from ..testing import assert_checkpoints
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from collections.abc import Awaitable, Callable
|
|
27
|
+
|
|
28
|
+
T = TypeVar("T")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def check_takes_about(f: Callable[[], Awaitable[T]], expected_dur: float) -> T:
|
|
32
|
+
start = time.perf_counter()
|
|
33
|
+
result = await outcome.acapture(f)
|
|
34
|
+
dur = time.perf_counter() - start
|
|
35
|
+
print(dur / expected_dur)
|
|
36
|
+
# 1.5 is an arbitrary fudge factor because there's always some delay
|
|
37
|
+
# between when we become eligible to wake up and when we actually do. We
|
|
38
|
+
# used to sleep for 0.05, and regularly observed overruns of 1.6x on
|
|
39
|
+
# Appveyor, and then started seeing overruns of 2.3x on Travis's macOS, so
|
|
40
|
+
# now we bumped up the sleep to 1 second, marked the tests as slow, and
|
|
41
|
+
# hopefully now the proportional error will be less huge.
|
|
42
|
+
#
|
|
43
|
+
# We also also for durations that are a hair shorter than expected. For
|
|
44
|
+
# example, here's a run on Windows where a 1.0 second sleep was measured
|
|
45
|
+
# to take 0.9999999999999858 seconds:
|
|
46
|
+
# https://ci.appveyor.com/project/njsmith/trio/build/1.0.768/job/3lbdyxl63q3h9s21
|
|
47
|
+
# I believe that what happened here is that Windows's low clock resolution
|
|
48
|
+
# meant that our calls to time.monotonic() returned exactly the same
|
|
49
|
+
# values as the calls inside the actual run loop, but the two subtractions
|
|
50
|
+
# returned slightly different values because the run loop's clock adds a
|
|
51
|
+
# random floating point offset to both times, which should cancel out, but
|
|
52
|
+
# lol floating point we got slightly different rounding errors. (That
|
|
53
|
+
# value above is exactly 128 ULPs below 1.0, which would make sense if it
|
|
54
|
+
# started as a 1 ULP error at a different dynamic range.)
|
|
55
|
+
assert (1 - 1e-8) <= (dur / expected_dur) < 1.5
|
|
56
|
+
|
|
57
|
+
return result.unwrap()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# How long to (attempt to) sleep for when testing. Smaller numbers make the
|
|
61
|
+
# test suite go faster.
|
|
62
|
+
TARGET = 1.0
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@slow
|
|
66
|
+
async def test_sleep() -> None:
|
|
67
|
+
async def sleep_1() -> None:
|
|
68
|
+
await sleep_until(_core.current_time() + TARGET)
|
|
69
|
+
|
|
70
|
+
await check_takes_about(sleep_1, TARGET)
|
|
71
|
+
|
|
72
|
+
async def sleep_2() -> None:
|
|
73
|
+
await sleep(TARGET)
|
|
74
|
+
|
|
75
|
+
await check_takes_about(sleep_2, TARGET)
|
|
76
|
+
|
|
77
|
+
with assert_checkpoints():
|
|
78
|
+
await sleep(0)
|
|
79
|
+
# This also serves as a test of the trivial move_on_at
|
|
80
|
+
with move_on_at(_core.current_time()):
|
|
81
|
+
with pytest.raises(_core.Cancelled):
|
|
82
|
+
await sleep(0)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@slow
|
|
86
|
+
async def test_move_on_after() -> None:
|
|
87
|
+
async def sleep_3() -> None:
|
|
88
|
+
with move_on_after(TARGET):
|
|
89
|
+
await sleep(100)
|
|
90
|
+
|
|
91
|
+
await check_takes_about(sleep_3, TARGET)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
async def test_cannot_wake_sleep_forever() -> None:
|
|
95
|
+
# Test an error occurs if you manually wake sleep_forever().
|
|
96
|
+
task = trio.lowlevel.current_task()
|
|
97
|
+
|
|
98
|
+
async def wake_task() -> None:
|
|
99
|
+
await trio.lowlevel.checkpoint()
|
|
100
|
+
trio.lowlevel.reschedule(task, outcome.Value(None))
|
|
101
|
+
|
|
102
|
+
async with trio.open_nursery() as nursery:
|
|
103
|
+
nursery.start_soon(wake_task)
|
|
104
|
+
with pytest.raises(RuntimeError):
|
|
105
|
+
await trio.sleep_forever()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class TimeoutScope(Protocol):
|
|
109
|
+
def __call__(self, seconds: float, *, shield: bool) -> trio.CancelScope: ...
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@pytest.mark.parametrize("scope", [move_on_after, fail_after])
|
|
113
|
+
async def test_context_shields_from_outer(scope: TimeoutScope) -> None:
|
|
114
|
+
with _core.CancelScope() as outer, scope(TARGET, shield=True) as inner:
|
|
115
|
+
outer.cancel()
|
|
116
|
+
try:
|
|
117
|
+
await trio.lowlevel.checkpoint()
|
|
118
|
+
except trio.Cancelled: # pragma: no cover
|
|
119
|
+
pytest.fail("shield didn't work")
|
|
120
|
+
inner.shield = False
|
|
121
|
+
with pytest.raises(trio.Cancelled):
|
|
122
|
+
await trio.lowlevel.checkpoint()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@slow
|
|
126
|
+
async def test_move_on_after_moves_on_even_if_shielded() -> None:
|
|
127
|
+
async def task() -> None:
|
|
128
|
+
with _core.CancelScope() as outer, move_on_after(TARGET, shield=True):
|
|
129
|
+
outer.cancel()
|
|
130
|
+
# The outer scope is cancelled, but this task is protected by the
|
|
131
|
+
# shield, so it manages to get to sleep until deadline is met
|
|
132
|
+
await sleep_forever()
|
|
133
|
+
|
|
134
|
+
await check_takes_about(task, TARGET)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@slow
|
|
138
|
+
async def test_fail_after_fails_even_if_shielded() -> None:
|
|
139
|
+
async def task() -> None:
|
|
140
|
+
with (
|
|
141
|
+
pytest.raises(TooSlowError),
|
|
142
|
+
_core.CancelScope() as outer,
|
|
143
|
+
fail_after(
|
|
144
|
+
TARGET,
|
|
145
|
+
shield=True,
|
|
146
|
+
),
|
|
147
|
+
):
|
|
148
|
+
outer.cancel()
|
|
149
|
+
# The outer scope is cancelled, but this task is protected by the
|
|
150
|
+
# shield, so it manages to get to sleep until deadline is met
|
|
151
|
+
await sleep_forever()
|
|
152
|
+
|
|
153
|
+
await check_takes_about(task, TARGET)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@slow
|
|
157
|
+
async def test_fail() -> None:
|
|
158
|
+
async def sleep_4() -> None:
|
|
159
|
+
with fail_at(_core.current_time() + TARGET):
|
|
160
|
+
await sleep(100)
|
|
161
|
+
|
|
162
|
+
with pytest.raises(TooSlowError):
|
|
163
|
+
await check_takes_about(sleep_4, TARGET)
|
|
164
|
+
|
|
165
|
+
with fail_at(_core.current_time() + 100):
|
|
166
|
+
await sleep(0)
|
|
167
|
+
|
|
168
|
+
async def sleep_5() -> None:
|
|
169
|
+
with fail_after(TARGET):
|
|
170
|
+
await sleep(100)
|
|
171
|
+
|
|
172
|
+
with pytest.raises(TooSlowError):
|
|
173
|
+
await check_takes_about(sleep_5, TARGET)
|
|
174
|
+
|
|
175
|
+
with fail_after(100):
|
|
176
|
+
await sleep(0)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
async def test_timeouts_raise_value_error() -> None:
|
|
180
|
+
# deadlines are allowed to be negative, but not delays.
|
|
181
|
+
# neither delays nor deadlines are allowed to be NaN
|
|
182
|
+
|
|
183
|
+
nan = float("nan")
|
|
184
|
+
|
|
185
|
+
for fun, val in (
|
|
186
|
+
(sleep, -1),
|
|
187
|
+
(sleep, nan),
|
|
188
|
+
(sleep_until, nan),
|
|
189
|
+
):
|
|
190
|
+
with pytest.raises(
|
|
191
|
+
ValueError,
|
|
192
|
+
match=r"^(deadline|`seconds`) must (not )*be (non-negative|NaN)$",
|
|
193
|
+
):
|
|
194
|
+
await fun(val)
|
|
195
|
+
|
|
196
|
+
for cm, val in (
|
|
197
|
+
(fail_after, -1),
|
|
198
|
+
(fail_after, nan),
|
|
199
|
+
(fail_at, nan),
|
|
200
|
+
(move_on_after, -1),
|
|
201
|
+
(move_on_after, nan),
|
|
202
|
+
(move_on_at, nan),
|
|
203
|
+
):
|
|
204
|
+
with pytest.raises(
|
|
205
|
+
ValueError,
|
|
206
|
+
match=r"^(deadline|`seconds`) must (not )*be (non-negative|NaN)$",
|
|
207
|
+
):
|
|
208
|
+
with cm(val):
|
|
209
|
+
pass # pragma: no cover
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
async def test_timeout_deadline_on_entry(mock_clock: _core.MockClock) -> None:
|
|
213
|
+
rcs = move_on_after(5)
|
|
214
|
+
assert rcs.relative_deadline == 5
|
|
215
|
+
|
|
216
|
+
mock_clock.jump(3)
|
|
217
|
+
start = _core.current_time()
|
|
218
|
+
with rcs as cs:
|
|
219
|
+
assert cs.is_relative is None
|
|
220
|
+
|
|
221
|
+
# This would previously be start+2
|
|
222
|
+
assert cs.deadline == start + 5
|
|
223
|
+
assert cs.relative_deadline == 5
|
|
224
|
+
|
|
225
|
+
cs.deadline = start + 3
|
|
226
|
+
assert cs.deadline == start + 3
|
|
227
|
+
assert cs.relative_deadline == 3
|
|
228
|
+
|
|
229
|
+
cs.relative_deadline = 4
|
|
230
|
+
assert cs.deadline == start + 4
|
|
231
|
+
assert cs.relative_deadline == 4
|
|
232
|
+
|
|
233
|
+
rcs = move_on_after(5)
|
|
234
|
+
assert rcs.shield is False
|
|
235
|
+
rcs.shield = True
|
|
236
|
+
assert rcs.shield is True
|
|
237
|
+
|
|
238
|
+
mock_clock.jump(3)
|
|
239
|
+
start = _core.current_time()
|
|
240
|
+
with rcs as cs:
|
|
241
|
+
assert cs.deadline == start + 5
|
|
242
|
+
|
|
243
|
+
assert rcs is cs
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
async def test_invalid_access_unentered(mock_clock: _core.MockClock) -> None:
|
|
247
|
+
cs = move_on_after(5)
|
|
248
|
+
mock_clock.jump(3)
|
|
249
|
+
start = _core.current_time()
|
|
250
|
+
|
|
251
|
+
match_str = "^unentered relative cancel scope does not have an absolute deadline"
|
|
252
|
+
with pytest.warns(DeprecationWarning, match=match_str):
|
|
253
|
+
assert cs.deadline == start + 5
|
|
254
|
+
mock_clock.jump(1)
|
|
255
|
+
# this is hella sketchy, but they *have* been warned
|
|
256
|
+
with pytest.warns(DeprecationWarning, match=match_str):
|
|
257
|
+
assert cs.deadline == start + 6
|
|
258
|
+
|
|
259
|
+
with pytest.warns(DeprecationWarning, match=match_str):
|
|
260
|
+
cs.deadline = 7
|
|
261
|
+
# now transformed into absolute
|
|
262
|
+
assert cs.deadline == 7
|
|
263
|
+
assert not cs.is_relative
|
|
264
|
+
|
|
265
|
+
cs = move_on_at(5)
|
|
266
|
+
|
|
267
|
+
match_str = (
|
|
268
|
+
"^unentered non-relative cancel scope does not have a relative deadline$"
|
|
269
|
+
)
|
|
270
|
+
with pytest.raises(RuntimeError, match=match_str):
|
|
271
|
+
assert cs.relative_deadline
|
|
272
|
+
with pytest.raises(RuntimeError, match=match_str):
|
|
273
|
+
cs.relative_deadline = 7
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@pytest.mark.xfail(reason="not implemented")
|
|
277
|
+
async def test_fail_access_before_entering() -> None: # pragma: no cover
|
|
278
|
+
my_fail_at = fail_at(5)
|
|
279
|
+
assert my_fail_at.deadline # type: ignore[attr-defined]
|
|
280
|
+
my_fail_after = fail_after(5)
|
|
281
|
+
assert my_fail_after.relative_deadline # type: ignore[attr-defined]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
import trio
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import AsyncGenerator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def coro1(event: trio.Event) -> None:
|
|
12
|
+
event.set()
|
|
13
|
+
await trio.sleep_forever()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def coro2(event: trio.Event) -> None:
|
|
17
|
+
await coro1(event)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def coro3(event: trio.Event) -> None:
|
|
21
|
+
await coro2(event)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
async def coro2_async_gen(event: trio.Event) -> AsyncGenerator[None, None]:
|
|
25
|
+
# mypy does not like `yield await trio.lowlevel.checkpoint()` - but that
|
|
26
|
+
# should be equivalent to splitting the statement
|
|
27
|
+
await trio.lowlevel.checkpoint()
|
|
28
|
+
yield
|
|
29
|
+
await coro1(event)
|
|
30
|
+
yield # pragma: no cover
|
|
31
|
+
await trio.lowlevel.checkpoint() # pragma: no cover
|
|
32
|
+
yield # pragma: no cover
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def coro3_async_gen(event: trio.Event) -> None:
|
|
36
|
+
async for _ in coro2_async_gen(event):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def test_task_iter_await_frames() -> None:
|
|
41
|
+
async with trio.open_nursery() as nursery:
|
|
42
|
+
event = trio.Event()
|
|
43
|
+
nursery.start_soon(coro3, event)
|
|
44
|
+
await event.wait()
|
|
45
|
+
|
|
46
|
+
(task,) = nursery.child_tasks
|
|
47
|
+
|
|
48
|
+
assert [frame.f_code.co_name for frame, _ in task.iter_await_frames()][:3] == [
|
|
49
|
+
"coro3",
|
|
50
|
+
"coro2",
|
|
51
|
+
"coro1",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
nursery.cancel_scope.cancel()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def test_task_iter_await_frames_async_gen() -> None:
|
|
58
|
+
async with trio.open_nursery() as nursery:
|
|
59
|
+
event = trio.Event()
|
|
60
|
+
nursery.start_soon(coro3_async_gen, event)
|
|
61
|
+
await event.wait()
|
|
62
|
+
|
|
63
|
+
(task,) = nursery.child_tasks
|
|
64
|
+
|
|
65
|
+
assert [frame.f_code.co_name for frame, _ in task.iter_await_frames()][:3] == [
|
|
66
|
+
"coro3_async_gen",
|
|
67
|
+
"coro2_async_gen",
|
|
68
|
+
"coro1",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
nursery.cancel_scope.cancel()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def test_closed_task_iter_await_frames() -> None:
|
|
75
|
+
async with trio.open_nursery() as nursery:
|
|
76
|
+
task = object()
|
|
77
|
+
|
|
78
|
+
async def capture_task() -> None:
|
|
79
|
+
nonlocal task
|
|
80
|
+
task = trio.lowlevel.current_task()
|
|
81
|
+
await trio.lowlevel.checkpoint()
|
|
82
|
+
|
|
83
|
+
nursery.start_soon(capture_task)
|
|
84
|
+
|
|
85
|
+
# Task has completed, so coro.cr_frame should be None, thus no frames
|
|
86
|
+
assert isinstance(task, trio.lowlevel.Task) # Ran `capture_task`
|
|
87
|
+
assert task.coro.cr_frame is None # and the task was over, but
|
|
88
|
+
assert list(task.iter_await_frames()) == [] # look, no crash!
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import errno
|
|
4
|
+
import os
|
|
5
|
+
import select
|
|
6
|
+
import sys
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from .. import _core
|
|
12
|
+
from .._core._tests.tutil import gc_collect_harder, skip_if_fbsd_pipes_broken
|
|
13
|
+
from ..testing import check_one_way_stream, wait_all_tasks_blocked
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from .._file_io import _HasFileNo
|
|
17
|
+
|
|
18
|
+
posix = os.name == "posix"
|
|
19
|
+
pytestmark = pytest.mark.skipif(not posix, reason="posix only")
|
|
20
|
+
|
|
21
|
+
assert not TYPE_CHECKING or sys.platform == "unix"
|
|
22
|
+
|
|
23
|
+
if posix:
|
|
24
|
+
from .._unix_pipes import FdStream
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
async def make_pipe() -> tuple[FdStream, FdStream]:
|
|
28
|
+
"""Makes a new pair of pipes."""
|
|
29
|
+
r, w = os.pipe()
|
|
30
|
+
return FdStream(w), FdStream(r)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async def make_clogged_pipe() -> tuple[FdStream, FdStream]:
|
|
34
|
+
s, r = await make_pipe()
|
|
35
|
+
try:
|
|
36
|
+
while True:
|
|
37
|
+
# We want to totally fill up the pipe buffer.
|
|
38
|
+
# This requires working around a weird feature that POSIX pipes
|
|
39
|
+
# have.
|
|
40
|
+
# If you do a write of <= PIPE_BUF bytes, then it's guaranteed
|
|
41
|
+
# to either complete entirely, or not at all. So if we tried to
|
|
42
|
+
# write PIPE_BUF bytes, and the buffer's free space is only
|
|
43
|
+
# PIPE_BUF/2, then the write will raise BlockingIOError... even
|
|
44
|
+
# though a smaller write could still succeed! To avoid this,
|
|
45
|
+
# make sure to write >PIPE_BUF bytes each time, which disables
|
|
46
|
+
# the special behavior.
|
|
47
|
+
# For details, search for PIPE_BUF here:
|
|
48
|
+
# http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html
|
|
49
|
+
|
|
50
|
+
# for the getattr:
|
|
51
|
+
# https://bitbucket.org/pypy/pypy/issues/2876/selectpipe_buf-is-missing-on-pypy3
|
|
52
|
+
buf_size = getattr(select, "PIPE_BUF", 8192)
|
|
53
|
+
os.write(s.fileno(), b"x" * buf_size * 2)
|
|
54
|
+
except BlockingIOError:
|
|
55
|
+
pass
|
|
56
|
+
return s, r
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
async def test_send_pipe() -> None:
|
|
60
|
+
r, w = os.pipe()
|
|
61
|
+
async with FdStream(w) as send:
|
|
62
|
+
assert send.fileno() == w
|
|
63
|
+
await send.send_all(b"123")
|
|
64
|
+
assert (os.read(r, 8)) == b"123"
|
|
65
|
+
|
|
66
|
+
os.close(r)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def test_receive_pipe() -> None:
|
|
70
|
+
r, w = os.pipe()
|
|
71
|
+
async with FdStream(r) as recv:
|
|
72
|
+
assert (recv.fileno()) == r
|
|
73
|
+
os.write(w, b"123")
|
|
74
|
+
assert (await recv.receive_some(8)) == b"123"
|
|
75
|
+
|
|
76
|
+
os.close(w)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def test_pipes_combined() -> None:
|
|
80
|
+
write, read = await make_pipe()
|
|
81
|
+
count = 2**20
|
|
82
|
+
|
|
83
|
+
async def sender() -> None:
|
|
84
|
+
big = bytearray(count)
|
|
85
|
+
await write.send_all(big)
|
|
86
|
+
|
|
87
|
+
async def reader() -> None:
|
|
88
|
+
await wait_all_tasks_blocked()
|
|
89
|
+
received = 0
|
|
90
|
+
while received < count:
|
|
91
|
+
received += len(await read.receive_some(4096))
|
|
92
|
+
|
|
93
|
+
assert received == count
|
|
94
|
+
|
|
95
|
+
async with _core.open_nursery() as n:
|
|
96
|
+
n.start_soon(sender)
|
|
97
|
+
n.start_soon(reader)
|
|
98
|
+
|
|
99
|
+
await read.aclose()
|
|
100
|
+
await write.aclose()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
async def test_pipe_errors() -> None:
|
|
104
|
+
with pytest.raises(TypeError):
|
|
105
|
+
FdStream(None)
|
|
106
|
+
|
|
107
|
+
r, w = os.pipe()
|
|
108
|
+
os.close(w)
|
|
109
|
+
async with FdStream(r) as s:
|
|
110
|
+
with pytest.raises(ValueError, match=r"^max_bytes must be integer >= 1$"):
|
|
111
|
+
await s.receive_some(0)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def test_del() -> None:
|
|
115
|
+
w, r = await make_pipe()
|
|
116
|
+
f1, f2 = w.fileno(), r.fileno()
|
|
117
|
+
del w, r
|
|
118
|
+
gc_collect_harder()
|
|
119
|
+
|
|
120
|
+
with pytest.raises(OSError, match=r"Bad file descriptor$") as excinfo:
|
|
121
|
+
os.close(f1)
|
|
122
|
+
assert excinfo.value.errno == errno.EBADF
|
|
123
|
+
|
|
124
|
+
with pytest.raises(OSError, match=r"Bad file descriptor$") as excinfo:
|
|
125
|
+
os.close(f2)
|
|
126
|
+
assert excinfo.value.errno == errno.EBADF
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
async def test_async_with() -> None:
|
|
130
|
+
w, r = await make_pipe()
|
|
131
|
+
async with w, r:
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
assert w.fileno() == -1
|
|
135
|
+
assert r.fileno() == -1
|
|
136
|
+
|
|
137
|
+
with pytest.raises(OSError, match=r"Bad file descriptor$") as excinfo:
|
|
138
|
+
os.close(w.fileno())
|
|
139
|
+
assert excinfo.value.errno == errno.EBADF
|
|
140
|
+
|
|
141
|
+
with pytest.raises(OSError, match=r"Bad file descriptor$") as excinfo:
|
|
142
|
+
os.close(r.fileno())
|
|
143
|
+
assert excinfo.value.errno == errno.EBADF
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
async def test_misdirected_aclose_regression() -> None:
|
|
147
|
+
# https://github.com/python-trio/trio/issues/661#issuecomment-456582356
|
|
148
|
+
w, r = await make_pipe()
|
|
149
|
+
old_r_fd = r.fileno()
|
|
150
|
+
|
|
151
|
+
# Close the original objects
|
|
152
|
+
await w.aclose()
|
|
153
|
+
await r.aclose()
|
|
154
|
+
|
|
155
|
+
# Do a little dance to get a new pipe whose receive handle matches the old
|
|
156
|
+
# receive handle.
|
|
157
|
+
r2_fd, w2_fd = os.pipe()
|
|
158
|
+
if r2_fd != old_r_fd: # pragma: no cover
|
|
159
|
+
os.dup2(r2_fd, old_r_fd)
|
|
160
|
+
os.close(r2_fd)
|
|
161
|
+
async with FdStream(old_r_fd) as r2:
|
|
162
|
+
assert r2.fileno() == old_r_fd
|
|
163
|
+
|
|
164
|
+
# And now set up a background task that's working on the new receive
|
|
165
|
+
# handle
|
|
166
|
+
async def expect_eof() -> None:
|
|
167
|
+
assert await r2.receive_some(10) == b""
|
|
168
|
+
|
|
169
|
+
async with _core.open_nursery() as nursery:
|
|
170
|
+
nursery.start_soon(expect_eof)
|
|
171
|
+
await wait_all_tasks_blocked()
|
|
172
|
+
|
|
173
|
+
# Here's the key test: does calling aclose() again on the *old*
|
|
174
|
+
# handle, cause the task blocked on the *new* handle to raise
|
|
175
|
+
# ClosedResourceError?
|
|
176
|
+
await r.aclose()
|
|
177
|
+
await wait_all_tasks_blocked()
|
|
178
|
+
|
|
179
|
+
# Guess we survived! Close the new write handle so that the task
|
|
180
|
+
# gets an EOF and can exit cleanly.
|
|
181
|
+
os.close(w2_fd)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
async def test_close_at_bad_time_for_receive_some(
|
|
185
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
186
|
+
) -> None:
|
|
187
|
+
# We used to have race conditions where if one task was using the pipe,
|
|
188
|
+
# and another closed it at *just* the wrong moment, it would give an
|
|
189
|
+
# unexpected error instead of ClosedResourceError:
|
|
190
|
+
# https://github.com/python-trio/trio/issues/661
|
|
191
|
+
#
|
|
192
|
+
# This tests what happens if the pipe gets closed in the moment *between*
|
|
193
|
+
# when receive_some wakes up, and when it tries to call os.read
|
|
194
|
+
async def expect_closedresourceerror() -> None:
|
|
195
|
+
with pytest.raises(_core.ClosedResourceError):
|
|
196
|
+
await r.receive_some(10)
|
|
197
|
+
|
|
198
|
+
orig_wait_readable = _core._run.TheIOManager.wait_readable
|
|
199
|
+
|
|
200
|
+
async def patched_wait_readable(
|
|
201
|
+
self: _core._run.TheIOManager,
|
|
202
|
+
fd: int | _HasFileNo,
|
|
203
|
+
) -> None:
|
|
204
|
+
await orig_wait_readable(self, fd)
|
|
205
|
+
await r.aclose()
|
|
206
|
+
|
|
207
|
+
monkeypatch.setattr(_core._run.TheIOManager, "wait_readable", patched_wait_readable)
|
|
208
|
+
s, r = await make_pipe()
|
|
209
|
+
async with s, r:
|
|
210
|
+
async with _core.open_nursery() as nursery:
|
|
211
|
+
nursery.start_soon(expect_closedresourceerror)
|
|
212
|
+
await wait_all_tasks_blocked()
|
|
213
|
+
# Trigger everything by waking up the receiver
|
|
214
|
+
await s.send_all(b"x")
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
async def test_close_at_bad_time_for_send_all(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
218
|
+
# We used to have race conditions where if one task was using the pipe,
|
|
219
|
+
# and another closed it at *just* the wrong moment, it would give an
|
|
220
|
+
# unexpected error instead of ClosedResourceError:
|
|
221
|
+
# https://github.com/python-trio/trio/issues/661
|
|
222
|
+
#
|
|
223
|
+
# This tests what happens if the pipe gets closed in the moment *between*
|
|
224
|
+
# when send_all wakes up, and when it tries to call os.write
|
|
225
|
+
async def expect_closedresourceerror() -> None:
|
|
226
|
+
with pytest.raises(_core.ClosedResourceError):
|
|
227
|
+
await s.send_all(b"x" * 100)
|
|
228
|
+
|
|
229
|
+
orig_wait_writable = _core._run.TheIOManager.wait_writable
|
|
230
|
+
|
|
231
|
+
async def patched_wait_writable(
|
|
232
|
+
self: _core._run.TheIOManager,
|
|
233
|
+
fd: int | _HasFileNo,
|
|
234
|
+
) -> None:
|
|
235
|
+
await orig_wait_writable(self, fd)
|
|
236
|
+
await s.aclose()
|
|
237
|
+
|
|
238
|
+
monkeypatch.setattr(_core._run.TheIOManager, "wait_writable", patched_wait_writable)
|
|
239
|
+
s, r = await make_clogged_pipe()
|
|
240
|
+
async with s, r:
|
|
241
|
+
async with _core.open_nursery() as nursery:
|
|
242
|
+
nursery.start_soon(expect_closedresourceerror)
|
|
243
|
+
await wait_all_tasks_blocked()
|
|
244
|
+
# Trigger everything by waking up the sender. On ppc64el, PIPE_BUF
|
|
245
|
+
# is 8192 but make_clogged_pipe() ends up writing a total of
|
|
246
|
+
# 1048576 bytes before the pipe is full, and then a subsequent
|
|
247
|
+
# receive_some(10000) isn't sufficient for orig_wait_writable() to
|
|
248
|
+
# return for our subsequent aclose() call. It's necessary to empty
|
|
249
|
+
# the pipe further before this happens. So we loop here until the
|
|
250
|
+
# pipe is empty to make sure that the sender wakes up even in this
|
|
251
|
+
# case. Otherwise patched_wait_writable() never gets to the
|
|
252
|
+
# aclose(), so expect_closedresourceerror() never returns, the
|
|
253
|
+
# nursery never finishes all tasks and this test hangs.
|
|
254
|
+
received_data = await r.receive_some(10000)
|
|
255
|
+
while received_data:
|
|
256
|
+
received_data = await r.receive_some(10000)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# On FreeBSD, directories are readable, and we haven't found any other trick
|
|
260
|
+
# for making an unreadable fd, so there's no way to run this test. Fortunately
|
|
261
|
+
# the logic this is testing doesn't depend on the platform, so testing on
|
|
262
|
+
# other platforms is probably good enough.
|
|
263
|
+
@pytest.mark.skipif(
|
|
264
|
+
sys.platform.startswith("freebsd"),
|
|
265
|
+
reason="no way to make read() return a bizarro error on FreeBSD",
|
|
266
|
+
)
|
|
267
|
+
async def test_bizarro_OSError_from_receive() -> None:
|
|
268
|
+
# Make sure that if the read syscall returns some bizarro error, then we
|
|
269
|
+
# get a BrokenResourceError. This is incredibly unlikely; there's almost
|
|
270
|
+
# no way to trigger a failure here intentionally (except for EBADF, but we
|
|
271
|
+
# exploit that to detect file closure, so it takes a different path). So
|
|
272
|
+
# we set up a strange scenario where the pipe fd somehow transmutes into a
|
|
273
|
+
# directory fd, causing os.read to raise IsADirectoryError (yes, that's a
|
|
274
|
+
# real built-in exception type).
|
|
275
|
+
s, r = await make_pipe()
|
|
276
|
+
async with s, r:
|
|
277
|
+
dir_fd = os.open("/", os.O_DIRECTORY, 0)
|
|
278
|
+
try:
|
|
279
|
+
os.dup2(dir_fd, r.fileno())
|
|
280
|
+
with pytest.raises(_core.BrokenResourceError):
|
|
281
|
+
await r.receive_some(10)
|
|
282
|
+
finally:
|
|
283
|
+
os.close(dir_fd)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@skip_if_fbsd_pipes_broken
|
|
287
|
+
async def test_pipe_fully() -> None:
|
|
288
|
+
await check_one_way_stream(make_pipe, make_clogged_pipe)
|