From 97200580306772a484b4206f385c54504307b0f8 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Sun, 20 Oct 2024 13:49:14 +0200
Subject: [PATCH 01/66] remove notes-to-self
---
notes-to-self/afd-lab.py | 182 ----------------
notes-to-self/aio-guest-test.py | 53 -----
notes-to-self/atomic-local.py | 35 ---
notes-to-self/blocking-read-hack.py | 53 -----
notes-to-self/estimate-task-size.py | 33 ---
notes-to-self/fbsd-pipe-close-notify.py | 37 ----
notes-to-self/file-read-latency.py | 27 ---
notes-to-self/graceful-shutdown-idea.py | 66 ------
.../how-does-windows-so-reuseaddr-work.py | 79 -------
notes-to-self/loopy.py | 23 --
notes-to-self/lots-of-tasks.py | 15 --
notes-to-self/manual-signal-handler.py | 25 ---
notes-to-self/measure-listen-backlog.py | 28 ---
notes-to-self/ntp-example.py | 96 ---------
notes-to-self/print-task-tree.py | 113 ----------
notes-to-self/proxy-benchmarks.py | 175 ---------------
notes-to-self/reopen-pipe.py | 70 ------
notes-to-self/schedule-timing.py | 42 ----
notes-to-self/server.crt | 19 --
notes-to-self/server.csr | 16 --
notes-to-self/server.key | 27 ---
notes-to-self/server.orig.key | 30 ---
notes-to-self/sleep-time.py | 61 ------
notes-to-self/socket-scaling.py | 60 ------
notes-to-self/socketpair-buffering.py | 37 ----
.../ssl-close-notify/ssl-close-notify.py | 78 -------
notes-to-self/ssl-close-notify/ssl2.py | 63 ------
.../ssl-close-notify/trio-test-1.pem | 64 ------
.../ssl-close-notify/trio-test-CA.pem | 19 --
notes-to-self/ssl-handshake/ssl-handshake.py | 147 -------------
notes-to-self/ssl-handshake/trio-test-1.pem | 64 ------
notes-to-self/ssl-handshake/trio-test-CA.pem | 19 --
notes-to-self/sslobject.py | 78 -------
notes-to-self/subprocess-notes.txt | 73 -------
notes-to-self/thread-closure-bug-demo.py | 60 ------
notes-to-self/thread-dispatch-bench.py | 36 ----
.../time-wait-windows-exclusiveaddruse.py | 69 ------
notes-to-self/time-wait.py | 113 ----------
notes-to-self/trace.py | 154 --------------
notes-to-self/trivial-err.py | 33 ---
notes-to-self/trivial.py | 10 -
notes-to-self/wakeup-fd-racer.py | 105 ---------
notes-to-self/win-waitable-timer.py | 201 ------------------
notes-to-self/windows-vm-notes.txt | 16 --
44 files changed, 2804 deletions(-)
delete mode 100644 notes-to-self/afd-lab.py
delete mode 100644 notes-to-self/aio-guest-test.py
delete mode 100644 notes-to-self/atomic-local.py
delete mode 100644 notes-to-self/blocking-read-hack.py
delete mode 100644 notes-to-self/estimate-task-size.py
delete mode 100644 notes-to-self/fbsd-pipe-close-notify.py
delete mode 100644 notes-to-self/file-read-latency.py
delete mode 100644 notes-to-self/graceful-shutdown-idea.py
delete mode 100644 notes-to-self/how-does-windows-so-reuseaddr-work.py
delete mode 100644 notes-to-self/loopy.py
delete mode 100644 notes-to-self/lots-of-tasks.py
delete mode 100644 notes-to-self/manual-signal-handler.py
delete mode 100644 notes-to-self/measure-listen-backlog.py
delete mode 100644 notes-to-self/ntp-example.py
delete mode 100644 notes-to-self/print-task-tree.py
delete mode 100644 notes-to-self/proxy-benchmarks.py
delete mode 100644 notes-to-self/reopen-pipe.py
delete mode 100644 notes-to-self/schedule-timing.py
delete mode 100644 notes-to-self/server.crt
delete mode 100644 notes-to-self/server.csr
delete mode 100644 notes-to-self/server.key
delete mode 100644 notes-to-self/server.orig.key
delete mode 100644 notes-to-self/sleep-time.py
delete mode 100644 notes-to-self/socket-scaling.py
delete mode 100644 notes-to-self/socketpair-buffering.py
delete mode 100644 notes-to-self/ssl-close-notify/ssl-close-notify.py
delete mode 100644 notes-to-self/ssl-close-notify/ssl2.py
delete mode 100644 notes-to-self/ssl-close-notify/trio-test-1.pem
delete mode 100644 notes-to-self/ssl-close-notify/trio-test-CA.pem
delete mode 100644 notes-to-self/ssl-handshake/ssl-handshake.py
delete mode 100644 notes-to-self/ssl-handshake/trio-test-1.pem
delete mode 100644 notes-to-self/ssl-handshake/trio-test-CA.pem
delete mode 100644 notes-to-self/sslobject.py
delete mode 100644 notes-to-self/subprocess-notes.txt
delete mode 100644 notes-to-self/thread-closure-bug-demo.py
delete mode 100644 notes-to-self/thread-dispatch-bench.py
delete mode 100644 notes-to-self/time-wait-windows-exclusiveaddruse.py
delete mode 100644 notes-to-self/time-wait.py
delete mode 100644 notes-to-self/trace.py
delete mode 100644 notes-to-self/trivial-err.py
delete mode 100644 notes-to-self/trivial.py
delete mode 100644 notes-to-self/wakeup-fd-racer.py
delete mode 100644 notes-to-self/win-waitable-timer.py
delete mode 100644 notes-to-self/windows-vm-notes.txt
diff --git a/notes-to-self/afd-lab.py b/notes-to-self/afd-lab.py
deleted file mode 100644
index a95eb8bfd8..0000000000
--- a/notes-to-self/afd-lab.py
+++ /dev/null
@@ -1,182 +0,0 @@
-# A little script to experiment with AFD polling.
-#
-# This cheats and uses a bunch of internal APIs. Don't follow its example. The
-# point is just to experiment with random junk that probably won't work, so we
-# can figure out what we actually do want to do internally.
-
-# Currently this demonstrates what seems to be a weird bug in the Windows
-# kernel. If you:
-#
-# 0. Set up a socket so that it's not writable.
-# 1. Submit a SEND poll operation.
-# 2. Submit a RECEIVE poll operation.
-# 3. Send some data through the socket, to trigger the RECEIVE.
-#
-# ...then the SEND poll operation completes with the RECEIVE flag set.
-#
-# (This bug is why our Windows backend jumps through hoops to avoid ever
-# issuing multiple polls simultaneously for the same socket.)
-#
-# This script's output on my machine:
-#
-# -- Iteration start --
-# Starting a poll for
-# Starting a poll for
-# Sending another byte
-# Poll for : got
-# Poll for : Cancelled()
-# -- Iteration start --
-# Starting a poll for
-# Starting a poll for
-# Poll for : got Sending another byte
-# Poll for : got
-#
-# So what we're seeing is:
-#
-# On the first iteration, where there's initially no data in the socket, the
-# SEND completes with the RECEIVE flag set, and the RECEIVE operation doesn't
-# return at all, until we cancel it.
-#
-# On the second iteration, there's already data sitting in the socket from the
-# last loop. This time, the RECEIVE returns immediately with the RECEIVE flag
-# set, which makes sense -- when starting a RECEIVE poll, it does an immediate
-# check to see if there's data already, and if so it does an early exit. But
-# the bizarre thing is, when we then send *another* byte of data, the SEND
-# operation wakes up with the RECEIVE flag set.
-#
-# Why is this bizarre? Let me count the ways:
-#
-# - The SEND operation should never return RECEIVE.
-#
-# - If it does insist on returning RECEIVE, it should do it immediately, since
-# there is already data to receive. But it doesn't.
-#
-# - And then when we send data into a socket that already has data in it, that
-# shouldn't have any effect at all! But instead it wakes up the SEND.
-#
-# - Also, the RECEIVE call did an early check for data and exited out
-# immediately, without going through the whole "register a callback to
-# be notified when data arrives" dance. So even if you do have some bug
-# in tracking which operations should be woken on which state transitions,
-# there's no reason this operation would even touch that tracking data. Yet,
-# if we take out the brief RECEIVE, then the SEND *doesn't* wake up.
-#
-# - Also, if I move the send() call up above the loop, so that there's already
-# data in the socket when we start our first iteration, then you would think
-# that would just make the first iteration act like it was the second
-# iteration. But it doesn't. Instead it makes all the weird behavior
-# disappear entirely.
-#
-# "What do we know … of the world and the universe about us? Our means of
-# receiving impressions are absurdly few, and our notions of surrounding
-# objects infinitely narrow. We see things only as we are constructed to see
-# them, and can gain no idea of their absolute nature. With five feeble senses
-# we pretend to comprehend the boundlessly complex cosmos, yet other beings
-# with wider, stronger, or different range of senses might not only see very
-# differently the things we see, but might see and study whole worlds of
-# matter, energy, and life which lie close at hand yet can never be detected
-# with the senses we have."
-
-import os.path
-import sys
-
-sys.path.insert(0, os.path.abspath(os.path.dirname(__file__) + r"\.."))
-
-import trio
-
-print(trio.__file__)
-import socket
-
-import trio.testing
-from trio._core._io_windows import _afd_helper_handle, _check, _get_base_socket
-from trio._core._windows_cffi import (
- AFDPollFlags,
- ErrorCodes,
- IoControlCodes,
- ffi,
- kernel32,
-)
-
-
-class AFDLab:
- def __init__(self):
- self._afd = _afd_helper_handle()
- trio.lowlevel.register_with_iocp(self._afd)
-
- async def afd_poll(self, sock, flags, *, exclusive=0):
- print(f"Starting a poll for {flags!r}")
- lpOverlapped = ffi.new("LPOVERLAPPED")
- poll_info = ffi.new("AFD_POLL_INFO *")
- poll_info.Timeout = 2**63 - 1 # INT64_MAX
- poll_info.NumberOfHandles = 1
- poll_info.Exclusive = exclusive
- poll_info.Handles[0].Handle = _get_base_socket(sock)
- poll_info.Handles[0].Status = 0
- poll_info.Handles[0].Events = flags
-
- try:
- _check(
- kernel32.DeviceIoControl(
- self._afd,
- IoControlCodes.IOCTL_AFD_POLL,
- poll_info,
- ffi.sizeof("AFD_POLL_INFO"),
- poll_info,
- ffi.sizeof("AFD_POLL_INFO"),
- ffi.NULL,
- lpOverlapped,
- ),
- )
- except OSError as exc:
- if exc.winerror != ErrorCodes.ERROR_IO_PENDING: # pragma: no cover
- raise
-
- try:
- await trio.lowlevel.wait_overlapped(self._afd, lpOverlapped)
- except:
- print(f"Poll for {flags!r}: {sys.exc_info()[1]!r}")
- raise
- out_flags = AFDPollFlags(poll_info.Handles[0].Events)
- print(f"Poll for {flags!r}: got {out_flags!r}")
- return out_flags
-
-
-def fill_socket(sock):
- try:
- while True:
- sock.send(b"x" * 65536)
- except BlockingIOError:
- pass
-
-
-async def main():
- afdlab = AFDLab()
-
- a, b = socket.socketpair()
- a.setblocking(False)
- b.setblocking(False)
-
- fill_socket(a)
-
- while True:
- print("-- Iteration start --")
- async with trio.open_nursery() as nursery:
- nursery.start_soon(
- afdlab.afd_poll,
- a,
- AFDPollFlags.AFD_POLL_SEND,
- )
- await trio.sleep(2)
- nursery.start_soon(
- afdlab.afd_poll,
- a,
- AFDPollFlags.AFD_POLL_RECEIVE,
- )
- await trio.sleep(2)
- print("Sending another byte")
- b.send(b"x")
- await trio.sleep(2)
- nursery.cancel_scope.cancel()
-
-
-trio.run(main)
diff --git a/notes-to-self/aio-guest-test.py b/notes-to-self/aio-guest-test.py
deleted file mode 100644
index 3c607d0281..0000000000
--- a/notes-to-self/aio-guest-test.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import asyncio
-
-import trio
-
-
-async def aio_main():
- loop = asyncio.get_running_loop()
-
- trio_done_fut = loop.create_future()
-
- def trio_done_callback(main_outcome):
- print(f"trio_main finished: {main_outcome!r}")
- trio_done_fut.set_result(main_outcome)
-
- trio.lowlevel.start_guest_run(
- trio_main,
- run_sync_soon_threadsafe=loop.call_soon_threadsafe,
- done_callback=trio_done_callback,
- )
-
- (await trio_done_fut).unwrap()
-
-
-async def trio_main():
- print("trio_main!")
-
- to_trio, from_aio = trio.open_memory_channel(float("inf"))
- from_trio = asyncio.Queue()
-
- _task_ref = asyncio.create_task(aio_pingpong(from_trio, to_trio))
-
- from_trio.put_nowait(0)
-
- async for n in from_aio:
- print(f"trio got: {n}")
- await trio.sleep(1)
- from_trio.put_nowait(n + 1)
- if n >= 10:
- return
- del _task_ref
-
-
-async def aio_pingpong(from_trio, to_trio):
- print("aio_pingpong!")
-
- while True:
- n = await from_trio.get()
- print(f"aio got: {n}")
- await asyncio.sleep(1)
- to_trio.send_nowait(n + 1)
-
-
-asyncio.run(aio_main())
diff --git a/notes-to-self/atomic-local.py b/notes-to-self/atomic-local.py
deleted file mode 100644
index 643bc16c6a..0000000000
--- a/notes-to-self/atomic-local.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from types import CodeType
-
-# Has to be a string :-(
-sentinel = "_unique_name"
-
-
-def f():
- print(locals())
-
-
-# code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,
-# constants, names, varnames, filename, name, firstlineno,
-# lnotab[, freevars[, cellvars]])
-new_code = CodeType(
- f.__code__.co_argcount,
- f.__code__.co_kwonlyargcount + 1,
- f.__code__.co_nlocals + 1,
- f.__code__.co_stacksize,
- f.__code__.co_flags,
- f.__code__.co_code,
- f.__code__.co_consts,
- f.__code__.co_names,
- (*f.__code__.co_varnames, sentinel),
- f.__code__.co_filename,
- f.__code__.co_name,
- f.__code__.co_firstlineno,
- f.__code__.co_lnotab,
- f.__code__.co_freevars,
- f.__code__.co_cellvars,
-)
-
-f.__code__ = new_code
-f.__kwdefaults__ = {sentinel: "fdsa"}
-
-f()
diff --git a/notes-to-self/blocking-read-hack.py b/notes-to-self/blocking-read-hack.py
deleted file mode 100644
index 688f103057..0000000000
--- a/notes-to-self/blocking-read-hack.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import errno
-import os
-import socket
-
-import trio
-
-bad_socket = socket.socket()
-
-
-class BlockingReadTimeoutError(Exception):
- pass
-
-
-async def blocking_read_with_timeout(
- fd,
- count,
- timeout, # noqa: ASYNC109 # manual timeout
-):
- print("reading from fd", fd)
- cancel_requested = False
-
- async def kill_it_after_timeout(new_fd):
- print("sleeping")
- await trio.sleep(timeout)
- print("breaking the fd")
- os.dup2(bad_socket.fileno(), new_fd, inheritable=False)
- # MAGIC
- print("setuid(getuid())")
- os.setuid(os.getuid())
- nonlocal cancel_requested
- cancel_requested = True
-
- new_fd = os.dup(fd)
- print("working fd is", new_fd)
- try:
- async with trio.open_nursery() as nursery:
- nursery.start_soon(kill_it_after_timeout, new_fd)
- try:
- data = await trio.to_thread.run_sync(os.read, new_fd, count)
- except OSError as exc:
- if cancel_requested and exc.errno == errno.ENOTCONN:
- # Call was successfully cancelled. In a real version we'd
- # integrate properly with Trio's cancellation tools; here
- # we'll just raise an arbitrary error.
- raise BlockingReadTimeoutError from None
- print("got", data)
- nursery.cancel_scope.cancel()
- return data
- finally:
- os.close(new_fd)
-
-
-trio.run(blocking_read_with_timeout, 0, 10, 2)
diff --git a/notes-to-self/estimate-task-size.py b/notes-to-self/estimate-task-size.py
deleted file mode 100644
index 0010c7a2b4..0000000000
--- a/notes-to-self/estimate-task-size.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Little script to get a rough estimate of how much memory each task takes
-
-import resource
-
-import trio
-import trio.testing
-
-LOW = 1000
-HIGH = 10000
-
-
-async def tinytask():
- await trio.sleep_forever()
-
-
-async def measure(count):
- async with trio.open_nursery() as nursery:
- for _ in range(count):
- nursery.start_soon(tinytask)
- await trio.testing.wait_all_tasks_blocked()
- nursery.cancel_scope.cancel()
- return resource.getrusage(resource.RUSAGE_SELF)
-
-
-async def main():
- low_usage = await measure(LOW)
- high_usage = await measure(HIGH + LOW)
-
- print("Memory usage per task:", (high_usage.ru_maxrss - low_usage.ru_maxrss) / HIGH)
- print("(kilobytes on Linux, bytes on macOS)")
-
-
-trio.run(main)
diff --git a/notes-to-self/fbsd-pipe-close-notify.py b/notes-to-self/fbsd-pipe-close-notify.py
deleted file mode 100644
index ef60d6900e..0000000000
--- a/notes-to-self/fbsd-pipe-close-notify.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# This script completes correctly on macOS and FreeBSD 13.0-CURRENT, but hangs
-# on FreeBSD 12.1. I'm told the fix will be backported to 12.2 (which is due
-# out in October 2020).
-#
-# Upstream bug: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=246350
-
-import os
-import select
-
-r, w = os.pipe()
-
-os.set_blocking(w, False)
-
-print("filling pipe buffer")
-try:
- while True:
- os.write(w, b"x")
-except BlockingIOError:
- pass
-
-_, wfds, _ = select.select([], [w], [], 0)
-print("select() says the write pipe is", "writable" if w in wfds else "NOT writable")
-
-kq = select.kqueue()
-event = select.kevent(w, select.KQ_FILTER_WRITE, select.KQ_EV_ADD)
-kq.control([event], 0)
-
-print("closing read end of pipe")
-os.close(r)
-
-_, wfds, _ = select.select([], [w], [], 0)
-print("select() says the write pipe is", "writable" if w in wfds else "NOT writable")
-
-print("waiting for kqueue to report the write end is writable")
-got = kq.control([], 1)
-print("done!")
-print(got)
diff --git a/notes-to-self/file-read-latency.py b/notes-to-self/file-read-latency.py
deleted file mode 100644
index c01cd41bf2..0000000000
--- a/notes-to-self/file-read-latency.py
+++ /dev/null
@@ -1,27 +0,0 @@
-import time
-
-# https://bitbucket.org/pypy/pypy/issues/2624/weird-performance-on-pypy3-when-reading
-# COUNT = 100000
-# f = open("/etc/passwd", "rt")
-COUNT = 1000000
-# With default buffering this test never even syscalls, and goes at about ~140
-# ns per call, instead of ~500 ns/call for the syscall and related overhead.
-# That's probably more fair -- the BufferedIOBase code can't service random
-# accesses, even if your working set fits entirely in RAM.
-with open("/etc/passwd", "rb") as f: # , buffering=0)
- while True:
- start = time.perf_counter()
- for _ in range(COUNT):
- f.seek(0)
- f.read(1)
- between = time.perf_counter()
- for _ in range(COUNT):
- f.seek(0)
- end = time.perf_counter()
-
- both = (between - start) / COUNT * 1e9
- seek = (end - between) / COUNT * 1e9
- read = both - seek
- print(
- f"{both:.2f} ns/(seek+read), {seek:.2f} ns/seek, estimate ~{read:.2f} ns/read",
- )
diff --git a/notes-to-self/graceful-shutdown-idea.py b/notes-to-self/graceful-shutdown-idea.py
deleted file mode 100644
index 9497af9724..0000000000
--- a/notes-to-self/graceful-shutdown-idea.py
+++ /dev/null
@@ -1,66 +0,0 @@
-import signal
-
-import gsm
-import trio
-
-
-class GracefulShutdownManager:
- def __init__(self):
- self._shutting_down = False
- self._cancel_scopes = set()
-
- def start_shutdown(self):
- self._shutting_down = True
- for cancel_scope in self._cancel_scopes:
- cancel_scope.cancel()
-
- def cancel_on_graceful_shutdown(self):
- cancel_scope = trio.CancelScope()
- self._cancel_scopes.add(cancel_scope)
- if self._shutting_down:
- cancel_scope.cancel()
- return cancel_scope
-
- @property
- def shutting_down(self):
- return self._shutting_down
-
-
-# Code can check gsm.shutting_down occasionally at appropriate points to see
-# if it should exit.
-#
-# When doing operations that might block for an indefinite about of time and
-# that should be aborted when a graceful shutdown starts, wrap them in 'with
-# gsm.cancel_on_graceful_shutdown()'.
-async def stream_handler(stream):
- while True:
- with gsm.cancel_on_graceful_shutdown():
- data = await stream.receive_some()
- print(f"{data = }")
- if gsm.shutting_down:
- break
-
-
-# To trigger the shutdown:
-async def listen_for_shutdown_signals():
- with trio.open_signal_receiver(signal.SIGINT, signal.SIGTERM) as signal_aiter:
- async for _sig in signal_aiter:
- gsm.start_shutdown()
- break
- # TODO: it'd be nice to have some logic like "if we get another
- # signal, or if 30 seconds pass, then do a hard shutdown".
- # That's easy enough:
- #
- # with trio.move_on_after(30):
- # async for sig in signal_aiter:
- # break
- # sys.exit()
- #
- # The trick is, if we do finish shutting down in (say) 10 seconds,
- # then we want to exit immediately. So I guess you'd need the main
- # part of the program to detect when it's finished shutting down, and
- # then cancel listen_for_shutdown_signals?
- #
- # I guess this would be a good place to use @smurfix's daemon task
- # construct:
- # https://github.com/python-trio/trio/issues/569#issuecomment-408419260
diff --git a/notes-to-self/how-does-windows-so-reuseaddr-work.py b/notes-to-self/how-does-windows-so-reuseaddr-work.py
deleted file mode 100644
index 117b9738e6..0000000000
--- a/notes-to-self/how-does-windows-so-reuseaddr-work.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# There are some tables here:
-# https://web.archive.org/web/20120206195747/https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx
-# They appear to be wrong.
-#
-# See https://github.com/python-trio/trio/issues/928 for details and context
-
-import errno
-import socket
-
-modes = ["default", "SO_REUSEADDR", "SO_EXCLUSIVEADDRUSE"]
-bind_types = ["wildcard", "specific"]
-
-
-def sock(mode):
- s = socket.socket(family=socket.AF_INET)
- if mode == "SO_REUSEADDR":
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- elif mode == "SO_EXCLUSIVEADDRUSE":
- s.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
- return s
-
-
-def bind(sock, bind_type):
- if bind_type == "wildcard":
- sock.bind(("0.0.0.0", 12345))
- elif bind_type == "specific":
- sock.bind(("127.0.0.1", 12345))
- else:
- raise AssertionError()
-
-
-def table_entry(mode1, bind_type1, mode2, bind_type2):
- with sock(mode1) as sock1:
- bind(sock1, bind_type1)
- try:
- with sock(mode2) as sock2:
- bind(sock2, bind_type2)
- except OSError as exc:
- if exc.winerror == errno.WSAEADDRINUSE:
- return "INUSE"
- elif exc.winerror == errno.WSAEACCES:
- return "ACCESS"
- raise
- else:
- return "Success"
-
-
-print(
- """
- second bind
- | """
- + " | ".join(f"{mode:<19}" for mode in modes),
-)
-
-print(""" """, end="")
-for _ in modes:
- print(
- " | " + " | ".join(f"{bind_type:>8}" for bind_type in bind_types),
- end="",
- )
-
-print(
- """
-first bind -----------------------------------------------------------------""",
- # default | wildcard | INUSE | Success | ACCESS | Success | INUSE | Success
-)
-
-for mode1 in modes:
- for bind_type1 in bind_types:
- row = []
- for mode2 in modes:
- for bind_type2 in bind_types:
- entry = table_entry(mode1, bind_type1, mode2, bind_type2)
- row.append(entry)
- # print(mode1, bind_type1, mode2, bind_type2, entry)
- print(
- f"{mode1:>19} | {bind_type1:>8} | "
- + " | ".join(f"{entry:>8}" for entry in row),
- )
diff --git a/notes-to-self/loopy.py b/notes-to-self/loopy.py
deleted file mode 100644
index 99f6e050b9..0000000000
--- a/notes-to-self/loopy.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import time
-
-import trio
-
-
-async def loopy():
- try:
- while True:
- # synchronous sleep to avoid maxing out CPU
- time.sleep(0.01) # noqa: ASYNC251
- await trio.lowlevel.checkpoint()
- except KeyboardInterrupt:
- print("KI!")
-
-
-async def main():
- async with trio.open_nursery() as nursery:
- nursery.start_soon(loopy)
- nursery.start_soon(loopy)
- nursery.start_soon(loopy)
-
-
-trio.run(main)
diff --git a/notes-to-self/lots-of-tasks.py b/notes-to-self/lots-of-tasks.py
deleted file mode 100644
index 048c69a7ec..0000000000
--- a/notes-to-self/lots-of-tasks.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import sys
-
-import trio
-
-(COUNT_STR,) = sys.argv[1:]
-COUNT = int(COUNT_STR)
-
-
-async def main():
- async with trio.open_nursery() as nursery:
- for _ in range(COUNT):
- nursery.start_soon(trio.sleep, 1)
-
-
-trio.run(main)
diff --git a/notes-to-self/manual-signal-handler.py b/notes-to-self/manual-signal-handler.py
deleted file mode 100644
index ead7f84d7d..0000000000
--- a/notes-to-self/manual-signal-handler.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# How to manually call the SIGINT handler on Windows without using raise() or
-# similar.
-import os
-import sys
-
-if os.name == "nt":
- import cffi
-
- ffi = cffi.FFI()
- ffi.cdef(
- """
- void* WINAPI GetProcAddress(void* hModule, char* lpProcName);
- typedef void (*PyOS_sighandler_t)(int);
- """,
- )
- kernel32 = ffi.dlopen("kernel32.dll")
- PyOS_getsig_ptr = kernel32.GetProcAddress(
- ffi.cast("void*", sys.dllhandle),
- b"PyOS_getsig",
- )
- PyOS_getsig = ffi.cast("PyOS_sighandler_t (*)(int)", PyOS_getsig_ptr)
-
- import signal
-
- PyOS_getsig(signal.SIGINT)(signal.SIGINT)
diff --git a/notes-to-self/measure-listen-backlog.py b/notes-to-self/measure-listen-backlog.py
deleted file mode 100644
index b7253b86cc..0000000000
--- a/notes-to-self/measure-listen-backlog.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import trio
-
-
-async def run_test(nominal_backlog):
- print("--\nnominal:", nominal_backlog)
-
- listen_sock = trio.socket.socket()
- await listen_sock.bind(("127.0.0.1", 0))
- listen_sock.listen(nominal_backlog)
- client_socks = []
- while True:
- client_sock = trio.socket.socket()
- # Generally the response to the listen buffer being full is that the
- # SYN gets dropped, and the client retries after 1 second. So we
- # assume that any connect() call to localhost that takes >0.5 seconds
- # indicates a dropped SYN.
- with trio.move_on_after(0.5) as cancel_scope:
- await client_sock.connect(listen_sock.getsockname())
- if cancel_scope.cancelled_caught:
- break
- client_socks.append(client_sock)
- print("actual:", len(client_socks))
- for client_sock in client_socks:
- client_sock.close()
-
-
-for nominal_backlog in [10, trio.socket.SOMAXCONN, 65535]:
- trio.run(run_test, nominal_backlog)
diff --git a/notes-to-self/ntp-example.py b/notes-to-self/ntp-example.py
deleted file mode 100644
index 2bb9f80fb3..0000000000
--- a/notes-to-self/ntp-example.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# If you want to use IPv6, then:
-# - replace AF_INET with AF_INET6 everywhere
-# - use the hostname "2.pool.ntp.org"
-# (see: https://news.ntppool.org/2011/06/continuing-ipv6-deployment/)
-
-import datetime
-import struct
-
-import trio
-
-
-def make_query_packet():
- """Construct a UDP packet suitable for querying an NTP server to ask for
- the current time."""
-
- # The structure of an NTP packet is described here:
- # https://tools.ietf.org/html/rfc5905#page-19
- # They're always 48 bytes long, unless you're using extensions, which we
- # aren't.
- packet = bytearray(48)
-
- # The first byte contains 3 subfields:
- # first 2 bits: 11, leap second status unknown
- # next 3 bits: 100, NTP version indicator, 0b100 == 4 = version 4
- # last 3 bits: 011, NTP mode indicator, 0b011 == 3 == "client"
- packet[0] = 0b11100011
-
- # For an outgoing request, all other fields can be left as zeros.
-
- return packet
-
-
-def extract_transmit_timestamp(ntp_packet):
- """Given an NTP packet, extract the "transmit timestamp" field, as a
- Python datetime."""
-
- # The transmit timestamp is the time that the server sent its response.
- # It's stored in bytes 40-47 of the NTP packet. See:
- # https://tools.ietf.org/html/rfc5905#page-19
- encoded_transmit_timestamp = ntp_packet[40:48]
-
- # The timestamp is stored in the "NTP timestamp format", which is a 32
- # byte count of whole seconds, followed by a 32 byte count of fractions of
- # a second. See:
- # https://tools.ietf.org/html/rfc5905#page-13
- seconds, fraction = struct.unpack("!II", encoded_transmit_timestamp)
-
- # The timestamp is the number of seconds since January 1, 1900 (ignoring
- # leap seconds). To convert it to a datetime object, we do some simple
- # datetime arithmetic:
- base_time = datetime.datetime(1900, 1, 1)
- offset = datetime.timedelta(seconds=seconds + fraction / 2**32)
- return base_time + offset
-
-
-async def main():
- print("Our clock currently reads (in UTC):", datetime.datetime.utcnow())
-
- # Look up some random NTP servers.
- # (See www.pool.ntp.org for information about the NTP pool.)
- servers = await trio.socket.getaddrinfo(
- "pool.ntp.org", # host
- "ntp", # port
- family=trio.socket.AF_INET, # IPv4
- type=trio.socket.SOCK_DGRAM, # UDP
- )
-
- # Construct an NTP query packet.
- query_packet = make_query_packet()
-
- # Create a UDP socket
- udp_sock = trio.socket.socket(
- family=trio.socket.AF_INET, # IPv4
- type=trio.socket.SOCK_DGRAM, # UDP
- )
-
- # Use the socket to send the query packet to each of the servers.
- print("-- Sending queries --")
- for server in servers:
- address = server[-1]
- print("Sending to:", address)
- await udp_sock.sendto(query_packet, address)
-
- # Read responses from the socket.
- print("-- Reading responses (for 10 seconds) --")
- with trio.move_on_after(10):
- while True:
- # We accept packets up to 1024 bytes long (though in practice NTP
- # packets will be much shorter).
- data, address = await udp_sock.recvfrom(1024)
- print("Got response from:", address)
- transmit_timestamp = extract_transmit_timestamp(data)
- print("Their clock read (in UTC):", transmit_timestamp)
-
-
-trio.run(main)
diff --git a/notes-to-self/print-task-tree.py b/notes-to-self/print-task-tree.py
deleted file mode 100644
index 54b97ec014..0000000000
--- a/notes-to-self/print-task-tree.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# NOTE:
-# possibly it would be easier to use https://pypi.org/project/tree-format/
-# instead of formatting by hand like this code does...
-
-"""
-Demo/exploration of how to print a task tree. Outputs:
-
-
-├─ __main__.main
-│ ├─ __main__.child1
-│ │ ├─ trio.sleep_forever
-│ │ ├─ __main__.child2
-│ │ │ ├─ trio.sleep_forever
-│ │ │ └─ trio.sleep_forever
-│ │ └─ __main__.child2
-│ │ ├─ trio.sleep_forever
-│ │ └─ trio.sleep_forever
-│ └─ (nested nursery)
-│ └─ __main__.child1
-│ ├─ trio.sleep_forever
-│ ├─ __main__.child2
-│ │ ├─ trio.sleep_forever
-│ │ └─ trio.sleep_forever
-│ └─ __main__.child2
-│ ├─ trio.sleep_forever
-│ └─ trio.sleep_forever
-└─
-
-"""
-
-import trio
-import trio.testing
-
-MID_PREFIX = "├─ "
-MID_CONTINUE = "│ "
-END_PREFIX = "└─ "
-END_CONTINUE = " " * len(END_PREFIX)
-
-
-def current_root_task():
- task = trio.lowlevel.current_task()
- while task.parent_nursery is not None:
- task = task.parent_nursery.parent_task
- return task
-
-
-def _render_subtree(name, rendered_children):
- lines = []
- lines.append(name)
- for child_lines in rendered_children:
- if child_lines is rendered_children[-1]:
- first_prefix = END_PREFIX
- rest_prefix = END_CONTINUE
- else:
- first_prefix = MID_PREFIX
- rest_prefix = MID_CONTINUE
- lines.append(first_prefix + child_lines[0])
- lines.extend(rest_prefix + child_line for child_line in child_lines[1:])
- return lines
-
-
-def _rendered_nursery_children(nursery):
- return [task_tree_lines(t) for t in nursery.child_tasks]
-
-
-def task_tree_lines(task=None):
- if task is None:
- task = current_root_task()
- rendered_children = []
- nurseries = list(task.child_nurseries)
- while nurseries:
- nursery = nurseries.pop()
- nursery_children = _rendered_nursery_children(nursery)
- if rendered_children:
- nested = _render_subtree("(nested nursery)", rendered_children)
- nursery_children.append(nested)
- rendered_children = nursery_children
- return _render_subtree(task.name, rendered_children)
-
-
-def print_task_tree(task=None):
- for line in task_tree_lines(task):
- print(line)
-
-
-################################################################
-
-
-async def child2():
- async with trio.open_nursery() as nursery:
- nursery.start_soon(trio.sleep_forever)
- nursery.start_soon(trio.sleep_forever)
-
-
-async def child1():
- async with trio.open_nursery() as nursery:
- nursery.start_soon(child2)
- nursery.start_soon(child2)
- nursery.start_soon(trio.sleep_forever)
-
-
-async def main():
- async with trio.open_nursery() as nursery0:
- nursery0.start_soon(child1)
- async with trio.open_nursery() as nursery1:
- nursery1.start_soon(child1)
-
- await trio.testing.wait_all_tasks_blocked()
- print_task_tree()
- nursery0.cancel_scope.cancel()
-
-
-trio.run(main)
diff --git a/notes-to-self/proxy-benchmarks.py b/notes-to-self/proxy-benchmarks.py
deleted file mode 100644
index 830327cf48..0000000000
--- a/notes-to-self/proxy-benchmarks.py
+++ /dev/null
@@ -1,175 +0,0 @@
-import textwrap
-import time
-
-methods = {"fileno"}
-
-
-class Proxy1:
- strategy = "__getattr__"
- works_for = "any attr"
-
- def __init__(self, wrapped):
- self._wrapped = wrapped
-
- def __getattr__(self, name):
- if name in methods:
- return getattr(self._wrapped, name)
- raise AttributeError(name)
-
-
-################################################################
-
-
-class Proxy2:
- strategy = "generated methods (getattr + closure)"
- works_for = "methods"
-
- def __init__(self, wrapped):
- self._wrapped = wrapped
-
-
-def add_wrapper(cls, method):
- def wrapper(self, *args, **kwargs):
- return getattr(self._wrapped, method)(*args, **kwargs)
-
- setattr(cls, method, wrapper)
-
-
-for method in methods:
- add_wrapper(Proxy2, method)
-
-################################################################
-
-
-class Proxy3:
- strategy = "generated methods (exec)"
- works_for = "methods"
-
- def __init__(self, wrapped):
- self._wrapped = wrapped
-
-
-def add_wrapper(cls, method):
- code = textwrap.dedent(
- f"""
- def wrapper(self, *args, **kwargs):
- return self._wrapped.{method}(*args, **kwargs)
- """,
- )
- ns = {}
- exec(code, ns)
- setattr(cls, method, ns["wrapper"])
-
-
-for method in methods:
- add_wrapper(Proxy3, method)
-
-################################################################
-
-
-class Proxy4:
- strategy = "generated properties (getattr + closure)"
- works_for = "any attr"
-
- def __init__(self, wrapped):
- self._wrapped = wrapped
-
-
-def add_wrapper(cls, attr):
- def getter(self):
- return getattr(self._wrapped, attr)
-
- def setter(self, newval):
- setattr(self._wrapped, attr, newval)
-
- def deleter(self):
- delattr(self._wrapped, attr)
-
- setattr(cls, attr, property(getter, setter, deleter))
-
-
-for method in methods:
- add_wrapper(Proxy4, method)
-
-################################################################
-
-
-class Proxy5:
- strategy = "generated properties (exec)"
- works_for = "any attr"
-
- def __init__(self, wrapped):
- self._wrapped = wrapped
-
-
-def add_wrapper(cls, attr):
- code = textwrap.dedent(
- f"""
- def getter(self):
- return self._wrapped.{attr}
-
- def setter(self, newval):
- self._wrapped.{attr} = newval
-
- def deleter(self):
- del self._wrapped.{attr}
- """,
- )
- ns = {}
- exec(code, ns)
- setattr(cls, attr, property(ns["getter"], ns["setter"], ns["deleter"]))
-
-
-for method in methods:
- add_wrapper(Proxy5, method)
-
-################################################################
-
-
-# methods only
-class Proxy6:
- strategy = "copy attrs from wrappee to wrapper"
- works_for = "methods + constant attrs"
-
- def __init__(self, wrapper):
- self._wrapper = wrapper
-
- for method in methods:
- setattr(self, method, getattr(self._wrapper, method))
-
-
-################################################################
-
-classes = [Proxy1, Proxy2, Proxy3, Proxy4, Proxy5, Proxy6]
-
-
-def check(cls):
- with open("/etc/passwd") as f:
- p = cls(f)
- assert p.fileno() == f.fileno()
-
-
-for cls in classes:
- check(cls)
-
-with open("/etc/passwd") as f:
- objs = [c(f) for c in classes]
-
- COUNT = 1000000
- try:
- import __pypy__ # noqa: F401 # __pypy__ imported but unused
- except ImportError:
- pass
- else:
- COUNT *= 10
-
- while True:
- print("-------")
- for obj in objs:
- start = time.perf_counter()
- for _ in range(COUNT):
- obj.fileno()
- # obj.fileno
- end = time.perf_counter()
- per_usec = COUNT / (end - start) / 1e6
- print(f"{per_usec:7.2f} / us: {obj.strategy} ({obj.works_for})")
diff --git a/notes-to-self/reopen-pipe.py b/notes-to-self/reopen-pipe.py
deleted file mode 100644
index dbccd567d7..0000000000
--- a/notes-to-self/reopen-pipe.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import os
-import tempfile
-import threading
-import time
-
-
-def check_reopen(r1, w):
- try:
- print("Reopening read end")
- r2 = os.open(f"/proc/self/fd/{r1}", os.O_RDONLY)
-
- print(f"r1 is {r1}, r2 is {r2}")
-
- print("checking they both can receive from w...")
-
- os.write(w, b"a")
- assert os.read(r1, 1) == b"a"
-
- os.write(w, b"b")
- assert os.read(r2, 1) == b"b"
-
- print("...ok")
-
- print("setting r2 to non-blocking")
- os.set_blocking(r2, False)
-
- print("os.get_blocking(r1) ==", os.get_blocking(r1))
- print("os.get_blocking(r2) ==", os.get_blocking(r2))
-
- # Check r2 is really truly non-blocking
- try:
- os.read(r2, 1)
- except BlockingIOError:
- print("r2 definitely seems to be in non-blocking mode")
-
- # Check that r1 is really truly still in blocking mode
- def sleep_then_write():
- time.sleep(1)
- os.write(w, b"c")
-
- threading.Thread(target=sleep_then_write, daemon=True).start()
- assert os.read(r1, 1) == b"c"
- print("r1 definitely seems to be in blocking mode")
- except Exception as exc:
- print(f"ERROR: {exc!r}")
-
-
-print("-- testing anonymous pipe --")
-check_reopen(*os.pipe())
-
-print("-- testing FIFO --")
-with tempfile.TemporaryDirectory() as tmpdir:
- fifo = tmpdir + "/" + "myfifo"
- os.mkfifo(fifo)
- # "A process can open a FIFO in nonblocking mode. In this case, opening
- # for read-only will succeed even if no-one has opened on the write side
- # yet and opening for write-only will fail with ENXIO (no such device or
- # address) unless the other end has already been opened." -- Linux fifo(7)
- r = os.open(fifo, os.O_RDONLY | os.O_NONBLOCK)
- assert not os.get_blocking(r)
- os.set_blocking(r, True)
- assert os.get_blocking(r)
- w = os.open(fifo, os.O_WRONLY)
- check_reopen(r, w)
-
-print("-- testing socketpair --")
-import socket
-
-rs, ws = socket.socketpair()
-check_reopen(rs.fileno(), ws.fileno())
diff --git a/notes-to-self/schedule-timing.py b/notes-to-self/schedule-timing.py
deleted file mode 100644
index 11594b7cc7..0000000000
--- a/notes-to-self/schedule-timing.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import time
-
-import trio
-
-LOOPS = 0
-RUNNING = True
-
-
-async def reschedule_loop(depth):
- if depth == 0:
- global LOOPS
- while RUNNING:
- LOOPS += 1
- await trio.lowlevel.checkpoint()
- # await trio.lowlevel.cancel_shielded_checkpoint()
- else:
- await reschedule_loop(depth - 1)
-
-
-async def report_loop():
- global RUNNING
- try:
- while True:
- start_count = LOOPS
- start_time = time.perf_counter()
- await trio.sleep(1)
- end_time = time.perf_counter()
- end_count = LOOPS
- loops = end_count - start_count
- duration = end_time - start_time
- print(f"{loops / duration} loops/sec")
- finally:
- RUNNING = False
-
-
-async def main():
- async with trio.open_nursery() as nursery:
- nursery.start_soon(reschedule_loop, 10)
- nursery.start_soon(report_loop)
-
-
-trio.run(main)
diff --git a/notes-to-self/server.crt b/notes-to-self/server.crt
deleted file mode 100644
index 9c58d8e65b..0000000000
--- a/notes-to-self/server.crt
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDBjCCAe4CCQDq+3W9D8C4ejANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
-VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
-cyBQdHkgTHRkMB4XDTE3MDMxOTAzMDk1MVoXDTE4MDMxOTAzMDk1MVowRTELMAkG
-A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
-IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
-AOwDDFVh8pvIrhZtIIX6pb3/PO5SM3rWsfoyyHi73GxemIiEHfYEjMKN8Eo10jUv
-4G0n8VlrrmuhGR+UuHY6jCxjoCuYWszQhwBZBaeGE24ydtO/RE24yhNsJHPQWXMe
-TL4mg1EBjJYXTwNhd7SwgCpkBQ+724ZJg+CmiPuYhVLdvjjUUmwiSbeueyULIPEJ
-G1EWkKdU5pYtyyTZoc0x2YEjes3YNWY563yk+RljvidFBMyAX8N3fF4yrCCHDeY6
-UPdpXry/BJcEJm7PY2lMhbL71T6499qKnmSaWyJjm+KqbXSEYXoWDVBBvg5pR9Ia
-XSoJ1MTfJ8eYnZDs5mETYDkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEApaW8WiKA
-3yDOUVzgwkeX3HxvxfhxtMTPBmO1M8YgX1yi+URkkKakc6bg3XW1saQrxBkXWwBr
-81Atd0tOLwHsC1HPd7Y5Q/1LKZiYFq2Sva6eZfeedRF/0f/SQC+rSvZNI5DIVPS4
-jW/EpyMKIeerIyWeFXz0/NWcYLCDWN6m2iDtR3m98bJcqSdUemLgyR13EAWsaVZ7
-dB6nkwGl9e78SOIHeGYg1Fb0B7IN2Tqw2tO3Xn0mzhvqs65OYuYo4pB0FzxiySAB
-q2nrgu6kGhkQw/RQ8QJ5MYjydYqCU0I4Qve1W7RoUxRnJvxJrMuvcdlMeboASKNl
-L7YQurFGvAAiZQ==
------END CERTIFICATE-----
diff --git a/notes-to-self/server.csr b/notes-to-self/server.csr
deleted file mode 100644
index f0fbc3829d..0000000000
--- a/notes-to-self/server.csr
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN CERTIFICATE REQUEST-----
-MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
-ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
-AQEBBQADggEPADCCAQoCggEBAOwDDFVh8pvIrhZtIIX6pb3/PO5SM3rWsfoyyHi7
-3GxemIiEHfYEjMKN8Eo10jUv4G0n8VlrrmuhGR+UuHY6jCxjoCuYWszQhwBZBaeG
-E24ydtO/RE24yhNsJHPQWXMeTL4mg1EBjJYXTwNhd7SwgCpkBQ+724ZJg+CmiPuY
-hVLdvjjUUmwiSbeueyULIPEJG1EWkKdU5pYtyyTZoc0x2YEjes3YNWY563yk+Rlj
-vidFBMyAX8N3fF4yrCCHDeY6UPdpXry/BJcEJm7PY2lMhbL71T6499qKnmSaWyJj
-m+KqbXSEYXoWDVBBvg5pR9IaXSoJ1MTfJ8eYnZDs5mETYDkCAwEAAaAAMA0GCSqG
-SIb3DQEBCwUAA4IBAQC+LhkPmCjxk5Nzn743u+7D/YzNhjv8Xv4aGUjjNyspVNso
-tlCAWkW2dWo8USvQrMUz5yl6qj6QQlg0QaYfaIiK8pkGz4s+Sh1plz1Eaa7QDK4O
-0wmtP6KkJyQW561ZY8sixS1DevKOmsp2Pa9fWU/vqKfzRv85A975XNadp6hkxXd7
-YOZCrSZjTnakpQvKoItvT9Xk7yKP6BI6h/03XORscbW/HyvLGoVLdE80yIkmjSot
-3JXxHspT27bWNWhz/Slph3UFaVyOVGXFTAqkLDZ3OISMnuC+q/t38EHYkR1aev/l
-4WogCtlWkFZ3bmhmlhJrH/bdTEkM6WopwoC6bczh
------END CERTIFICATE REQUEST-----
diff --git a/notes-to-self/server.key b/notes-to-self/server.key
deleted file mode 100644
index c0ba0b8582..0000000000
--- a/notes-to-self/server.key
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEogIBAAKCAQEA7AMMVWHym8iuFm0ghfqlvf887lIzetax+jLIeLvcbF6YiIQd
-9gSMwo3wSjXSNS/gbSfxWWuua6EZH5S4djqMLGOgK5hazNCHAFkFp4YTbjJ2079E
-TbjKE2wkc9BZcx5MviaDUQGMlhdPA2F3tLCAKmQFD7vbhkmD4KaI+5iFUt2+ONRS
-bCJJt657JQsg8QkbURaQp1Tmli3LJNmhzTHZgSN6zdg1ZjnrfKT5GWO+J0UEzIBf
-w3d8XjKsIIcN5jpQ92levL8ElwQmbs9jaUyFsvvVPrj32oqeZJpbImOb4qptdIRh
-ehYNUEG+DmlH0hpdKgnUxN8nx5idkOzmYRNgOQIDAQABAoIBABuus9ij93fsTvcU
-b7cnUh95+6ScgatL2W5WXItExbL0WYHRtU3w9K2xRlj9/Rz98536DGYHqlq3d6Hr
-qMM9VMm0GcpjQWs6nksdJfujT04inytxCMrw/MrQaWooKwXErQ20qLxsqRfFvh/Q
-Y+EOvsm6F5nj1/jlUJGeFv0jw6eXXxH6bqVUVYIaVCpAMB5Sm8caQ4dAI9UESZJv
-vuucT24iSyV8vp060L1tNKgRUr5e2CMfbucauZh0nLALPAyu1I07Ce62q9wLLw66
-c2FLHcZBkTGvL0bPe89ttJJuK0jttHV6GQ/OneytezZFxLw1DMsG3VxzbXt2X7AN
-noGzrDECgYEA/fnK0xlNir9bTNmOID42GoVUYF6iYWUf//rRlCKsRofSlPjTZDJK
-grl/plTBKDE6qDDEkB1mrEkJufqP3slyq66NfkP0NLoo+PFkGSnsbvUvFNYwcYvH
-7w2NWo/GvM4DJRqHvrETryBQwQtBJFsq9biWd3+hNCXYrhawKGqbzw0CgYEA7eSa
-T6zIdmvszG5x1XzQ3k29GwUA4SLL1YfV2pnLxoMZgW5Q6T+cOCpJdEuL0BXCNelP
-gk0gNXNvCzylwVC0BbpefFaJYsWK6gVg1EwDkiZcGx4FnKd0TWYer6RWrZ9cVohT
-eNwix9kKVef7chf+2006eE1O8D0UYwZMpGifqt0CgYAKjmtjwtV6QuHkm9ZQeMV+
-7LPJHaXaLn3aAe7cHWTTuamDD6SZsY1vSY6It1Uf+ovZmc1RwCcYWiDRXhzEwdLG
-WAcBjImF94bkcgQbF6cAJajDUPPKhGjXAtUxQnCcQGPZEvU5c9rBmLJCk9ktTazH
-cdivNtrYdApBkifYRjYbsQKBgDZl0ctqTSSXJTzR/IG+2twalqV5DWxt0oJvXz1v
-caNhExH/sczEWOqW8NkA9WWNtC0zvpSjIjxWuwuswJJl6+Rra3OvLhdB6LP+qteg
-0ig3UVR6FvptaDDSqy2qvI9TI4A+CChY3jMotC5Ur7C1P/fRvw8HToesz96c8CWg
-LvKZAoGAS4VW2YaYVlN14mkKGtHbZq0XWZGursAwYJn6H5jBCjl+89tsFzfCnTER
-hZFH+zs281v1bVbCFws8lWrZg8AZh55dG8CcLtuCkTyXJ/aAdlan0+FmXV60+RLP
-Z1TyykQG/oDgO1Z+5GrcN2b0FOFaSbH2NRzRlhyOI63yTQi4lT8=
------END RSA PRIVATE KEY-----
diff --git a/notes-to-self/server.orig.key b/notes-to-self/server.orig.key
deleted file mode 100644
index 28fac173ff..0000000000
--- a/notes-to-self/server.orig.key
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,D9C5B2214855387C
-
-gYuCZiXsU74IOErbOGOmc1y/BFP1N7UuRO19tidUrq1O6sreJSAVKRibIynAwmXj
-p5xvaAnBBIZIH6X7I2vduJgtUeeyvy5yxR98pD6liRKDxFaVD+O1m5IZxSbAs2De
-olk4Zlv3YULpbVF6Ud+QuLmgbqfmT+8NVGm4MwRey7Gkj+LEfGrNjpfgLqNRIaUZ
-XDPQh9HLZYCsAbz5OeRHwJwawLO74fvWBkFjsQyoWLgJzqZFmt15SyrRufBeKYP6
-oKKemsiW8/A2+i6Rb1vHYOJJ6c9jeeHPJkZSbfNWf4/Z702DAMIisbHmTCQzsUrX
-178d2Z7sDKcuDCQ1EInnLRb3YET/V83wGDWyHxWepaHLWHd5S7tFbsqZFsXxIuYM
-lcZZVSPsOLnG2SozZK+Tr2RX7jkI4Kmfh0RDgtKBYQQopZjRSFUG2hvvH3EIxVIf
-JyUG8AA5RT1J9tkcSJJ5MS40So7i3eyAuZXuYVSkuDai/mu2IUU8vYnwB8a1psU1
-P2CGUj2AFopvMAfSOYIPGHpcIn+lvxuXUdczR/Yikp/BhGT+diJjP68CUsMBdyq7
-pcVmMVyQPVpcsMag3IXGgIAF1v1GhO3zDMd1uXA1lyrHQa6CEah3z+4WFSWwYZ0I
-OZz5qM9bnfKoAQesp+xmcZhs8cbrblMRVDkWiPUixxKVJk3eBUsMoa1WYq/2u0ly
-EgvNlY39B/3eiLi+k+S6gVGT8a4AP6n4RuxPD4g0A79bI1LpC3xU6vcKV/GyIP69
-t2DHR2q9UDEiRj9DxjuTzxew7eRX8ktD7DhYV06BxrgQIRRiL9MrZRKGuqzXcMP/
-kWY71ioFZJ1ViZkpy7bEsYrpF2XBjGge3We0s2udnrY3r3ogxOjtZiT27e2zEbXD
-T59C3gecuEzCSCZ3eQtdcVC9m3RdHMTNNKvqmTVFPgfGOoM5u2gG+rYjhetbpTDB
-T5drcEEAcV11DHuokU4tlqOdIWdLuBsK3xgO98JasEr1LyYJT1fnjB+6AbhjfSS2
-p5TPekmSwaZbaBzwfP1xmhINJm388GCROXMkc9iLAWN9npHhssfMAA2WMXqDTgSt
-34oUnHgLGmvOm5HzJE/tTR1WP1Rye4nKNLwsbk2x7WXxqcNUPYc+OVmZbsl/R5Gz
-3zRHPts01mT/eaSfqj1wkJgpYtDQLPO+V1fc2pDgJmQMYyr7OCLI6I9GJBlB8gVq
-aemv0TMi3/eUVyJRaAHxAAi7YMsrSkuKUrsbLfIIRgViaEy+1stFa9iWiHJT0DKJ
-0fOqtwcL8OYJURyG/D29yUP5qBJcrFuIYk8uI1wtfDNMeAI4LWoWwMhBLtB6POY+
-a/qmMewFzrGGsR9R0ptwtlplhvJVeArfLYGngnbgBV4vwchjLQTR2RMouZWlwRH9
-NWX6EqsIk/zzYvu+o7sBC2839D3GCPQMmgKqSWwmlf2a76mqZk2duTO9+0v6+e+F
-Qc44ndLFE+mEibXkm9PMHvPsXOUdC4KPpugC/aZbn4OCqVd3eSl7k+PZGKZua6IJ
-ybhosNzQc4lg25K7iMxRXpK5WrOgEXSAA3kUquDRTWHshpz/Avwbgw==
------END RSA PRIVATE KEY-----
diff --git a/notes-to-self/sleep-time.py b/notes-to-self/sleep-time.py
deleted file mode 100644
index 85adf623e1..0000000000
--- a/notes-to-self/sleep-time.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Suppose:
-# - we're blocked until a timeout occurs
-# - our process gets put to sleep for a while (SIGSTOP or whatever)
-# - then it gets woken up again
-# what happens to our timeout?
-#
-# Here we do things that sleep for 6 seconds, and we put the process to sleep
-# for 2 seconds in the middle of that.
-#
-# Results on Linux: everything takes 6 seconds, except for select.select(),
-# and also time.sleep() (which on CPython uses the select() call internally)
-#
-# Results on macOS: everything takes 6 seconds.
-#
-# Why do we care:
-# https://github.com/python-trio/trio/issues/591#issuecomment-498020805
-
-import os
-import select
-import signal
-import subprocess
-import sys
-import time
-
-DUR = 6
-# Can also try SIGTSTP
-STOP_SIGNAL = signal.SIGSTOP
-
-test_progs = [
- f"import threading; ev = threading.Event(); ev.wait({DUR})",
- # Python's time.sleep() calls select() internally
- f"import time; time.sleep({DUR})",
- # This is the real sleep() function
- f"import ctypes; ctypes.CDLL(None).sleep({DUR})",
- f"import select; select.select([], [], [], {DUR})",
- f"import select; p = select.poll(); p.poll({DUR} * 1000)",
-]
-if hasattr(select, "epoll"):
- test_progs += [
- f"import select; ep = select.epoll(); ep.poll({DUR})",
- ]
-if hasattr(select, "kqueue"):
- test_progs += [
- f"import select; kq = select.kqueue(); kq.control([], 1, {DUR})",
- ]
-
-for test_prog in test_progs:
- print("----------------------------------------------------------------")
- start = time.monotonic()
- print(f"Running: {test_prog}")
- print(f"Expected duration: {DUR} seconds")
- p = subprocess.Popen([sys.executable, "-c", test_prog])
- time.sleep(DUR / 3)
- print(f"Putting it to sleep for {DUR / 3} seconds")
- os.kill(p.pid, STOP_SIGNAL)
- time.sleep(DUR / 3)
- print("Waking it up again")
- os.kill(p.pid, signal.SIGCONT)
- p.wait()
- end = time.monotonic()
- print(f"Actual duration: {end - start:.2f}")
diff --git a/notes-to-self/socket-scaling.py b/notes-to-self/socket-scaling.py
deleted file mode 100644
index 3bd074836a..0000000000
--- a/notes-to-self/socket-scaling.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Little script to measure how wait_readable scales with the number of
-# sockets. We look at three key measurements:
-#
-# - cost of issuing wait_readable
-# - cost of running the scheduler, while wait_readables are blocked in the
-# background
-# - cost of cancelling wait_readable
-#
-# On Linux and macOS, these all appear to be ~O(1), as we'd expect.
-#
-# On Windows: with the old 'select'-based loop, the cost of scheduling grew
-# with the number of outstanding sockets, which was bad.
-#
-# To run this on Unix systems, you'll probably first have to run:
-#
-# ulimit -n 31000
-#
-# or similar.
-
-import socket
-import time
-
-import trio
-import trio.testing
-
-
-async def main():
- for total in [10, 100, 500, 1_000, 10_000, 20_000, 30_000]:
-
- def pt(desc, *, count=total, item="socket"):
- nonlocal last_time
- now = time.perf_counter()
- total_ms = (now - last_time) * 1000
- per_us = total_ms * 1000 / count
- print(f"{desc}: {total_ms:.2f} ms total, {per_us:.2f} μs/{item}")
- last_time = now
-
- print(f"\n-- {total} sockets --")
- last_time = time.perf_counter()
- sockets = []
- for _ in range(total // 2):
- a, b = socket.socketpair()
- sockets += [a, b]
- pt("socket creation")
- async with trio.open_nursery() as nursery:
- for s in sockets:
- nursery.start_soon(trio.lowlevel.wait_readable, s)
- await trio.testing.wait_all_tasks_blocked()
- pt("spawning wait tasks")
- for _ in range(1000):
- await trio.lowlevel.cancel_shielded_checkpoint()
- pt("scheduling 1000 times", count=1000, item="schedule")
- nursery.cancel_scope.cancel()
- pt("cancelling wait tasks")
- for sock in sockets:
- sock.close()
- pt("closing sockets")
-
-
-trio.run(main)
diff --git a/notes-to-self/socketpair-buffering.py b/notes-to-self/socketpair-buffering.py
deleted file mode 100644
index e6169c25d3..0000000000
--- a/notes-to-self/socketpair-buffering.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import socket
-
-# Linux:
-# low values get rounded up to ~2-4 KB, so that's predictable
-# with low values, can queue up 6 one-byte sends (!)
-# with default values, can queue up 278 one-byte sends
-#
-# Windows:
-# if SNDBUF = 0 freezes, so that's useless
-# by default, buffers 655121
-# with both set to 1, buffers 525347
-# except sometimes it's less intermittently (?!?)
-#
-# macOS:
-# if bufsize = 1, can queue up 1 one-byte send
-# with default bufsize, can queue up 8192 one-byte sends
-# and bufsize = 0 is invalid (setsockopt errors out)
-
-for bufsize in [1, None, 0]:
- a, b = socket.socketpair()
- a.setblocking(False)
- b.setblocking(False)
-
- a.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
- if bufsize is not None:
- a.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, bufsize)
- b.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, bufsize)
-
- try:
- for _count in range(10000000):
- a.send(b"\x00")
- except BlockingIOError:
- break
-
- print(f"setsockopt bufsize {bufsize}: {_count}")
- a.close()
- b.close()
diff --git a/notes-to-self/ssl-close-notify/ssl-close-notify.py b/notes-to-self/ssl-close-notify/ssl-close-notify.py
deleted file mode 100644
index 7a55b8c99c..0000000000
--- a/notes-to-self/ssl-close-notify/ssl-close-notify.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# Scenario:
-# - TLS connection is set up successfully
-# - client sends close_notify then closes socket
-# - server receives the close_notify then attempts to send close_notify back
-#
-# On CPython, the last step raises BrokenPipeError. On PyPy, it raises
-# SSLEOFError.
-#
-# SSLEOFError seems a bit perverse given that it's supposed to mean "EOF
-# occurred in violation of protocol", and the client's behavior here is
-# explicitly allowed by the RFCs. But maybe openssl is just perverse like
-# that, and it's a coincidence that CPython and PyPy act differently here? I
-# don't know if this is a bug or not.
-#
-# (Using: debian's CPython 3.5 or 3.6, and pypy3 5.8.0-beta)
-
-import socket
-import ssl
-import threading
-
-client_sock, server_sock = socket.socketpair()
-
-client_done = threading.Event()
-
-
-def server_thread_fn():
- server_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
- server_ctx.load_cert_chain("trio-test-1.pem")
- server = server_ctx.wrap_socket(
- server_sock,
- server_side=True,
- suppress_ragged_eofs=False,
- )
- while True:
- data = server.recv(4096)
- print("server got:", data)
- if not data:
- print("server waiting for client to finish everything")
- client_done.wait()
- print("server attempting to send back close-notify")
- server.unwrap()
- print("server ok")
- break
- server.sendall(data)
-
-
-server_thread = threading.Thread(target=server_thread_fn)
-server_thread.start()
-
-client_ctx = ssl.create_default_context(cafile="trio-test-CA.pem")
-client = client_ctx.wrap_socket(client_sock, server_hostname="trio-test-1.example.org")
-
-
-# Now we have two SSLSockets that have established an encrypted connection
-# with each other
-
-assert client.getpeercert() is not None
-client.sendall(b"x")
-assert client.recv(10) == b"x"
-
-# The client sends a close-notify, and then immediately closes the connection
-# (as explicitly permitted by the TLS RFCs).
-
-# This is a slightly odd construction, but if you trace through the ssl module
-# far enough you'll see that it's equivalent to calling SSL_shutdown() once,
-# which generates the close_notify, and then immediately calling it again,
-# which checks for the close_notify and then immediately raises
-# SSLWantReadError because of course it hasn't arrived yet:
-print("client sending close_notify")
-client.setblocking(False)
-try:
- client.unwrap()
-except ssl.SSLWantReadError:
- print("client got SSLWantReadError as expected")
-else:
- raise AssertionError()
-client.close()
-client_done.set()
diff --git a/notes-to-self/ssl-close-notify/ssl2.py b/notes-to-self/ssl-close-notify/ssl2.py
deleted file mode 100644
index 54ee1fb9b6..0000000000
--- a/notes-to-self/ssl-close-notify/ssl2.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# This demonstrates a PyPy bug:
-# https://bitbucket.org/pypy/pypy/issues/2578/
-
-import socket
-import ssl
-import threading
-
-# client_sock, server_sock = socket.socketpair()
-listen_sock = socket.socket()
-listen_sock.bind(("127.0.0.1", 0))
-listen_sock.listen(1)
-client_sock = socket.socket()
-client_sock.connect(listen_sock.getsockname())
-server_sock, _ = listen_sock.accept()
-
-server_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
-server_ctx.load_cert_chain("trio-test-1.pem")
-server = server_ctx.wrap_socket(
- server_sock,
- server_side=True,
- suppress_ragged_eofs=False,
- do_handshake_on_connect=False,
-)
-
-client_ctx = ssl.create_default_context(cafile="trio-test-CA.pem")
-client = client_ctx.wrap_socket(
- client_sock,
- server_hostname="trio-test-1.example.org",
- suppress_ragged_eofs=False,
- do_handshake_on_connect=False,
-)
-
-server_handshake_thread = threading.Thread(target=server.do_handshake)
-server_handshake_thread.start()
-client_handshake_thread = threading.Thread(target=client.do_handshake)
-client_handshake_thread.start()
-
-server_handshake_thread.join()
-client_handshake_thread.join()
-
-# Now we have two SSLSockets that have established an encrypted connection
-# with each other
-
-assert client.getpeercert() is not None
-client.sendall(b"x")
-assert server.recv(10) == b"x"
-
-# A few different ways to make attempts to read/write the socket's fd return
-# weird failures at the operating system level
-
-# Attempting to send on a socket after shutdown should raise EPIPE or similar
-server.shutdown(socket.SHUT_WR)
-
-# Attempting to read/write to the fd after it's closed should raise EBADF
-# os.close(server.fileno())
-
-# Attempting to read/write to an fd opened with O_DIRECT raises EINVAL in most
-# cases (unless you're very careful with alignment etc. which openssl isn't)
-# os.dup2(os.open("/tmp/blah-example-file", os.O_RDWR | os.O_CREAT | os.O_DIRECT), server.fileno())
-
-# Sending or receiving
-server.sendall(b"hello")
-# server.recv(10)
diff --git a/notes-to-self/ssl-close-notify/trio-test-1.pem b/notes-to-self/ssl-close-notify/trio-test-1.pem
deleted file mode 100644
index a0c1b773f9..0000000000
--- a/notes-to-self/ssl-close-notify/trio-test-1.pem
+++ /dev/null
@@ -1,64 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDZ8yz1OrHX7aHp
-Erfa1ds8kmYfqYomjgy5wDsGdb8i1gF4uxhHCRDQtNZANVOVXI7R3TMchA1GMxzA
-ZYDuBDuEUsqTktbTEBNb4GjOhyMu1fF4dX/tMxf7GB+flTx178eE2exTOZLmSmBa
-2laoDVe3CrBAYE7nZtBF630jKKKMsUuIl0CbFRHajpoqM3e3CeCo4KcbBzgujRA3
-AsVV6y5qhMH2zqLkOYaurVUfEkdjqoHFgj1VbjWpkTbrXAxPwW6v/uZK056bHgBg
-go03RyWexaPapsF2oUm2JNdSN3z7MP0umKphO2n9icyGt9Bmkm2AKs3dA45VLPXh
-+NohluqJAgMBAAECggEARlfWCtAG1ko8F52S+W5MdCBMFawCiq8OLGV+p3cZWYT4
-tJ6uFz81ziaPf+m2MF7POazK8kksf5u/i9k245s6GlseRsL90uE9XknvibjUAinK
-5bYGs+fptYDzs+3WtbnOC3LKc5IBd5JJxwjxLwwfY1RvzldHIChu0CJRISfcTsvR
-occ8hXdeft7svNymvTuwQd05u1yjzL4RwF8Be76i17j5+jDsrAaUKdxxwGNAyOU7
-OKrUY6G851T6NUGgC19iXAJ1wN9tVGIR5QOs3J/s6dCctnX5tN8Di7prkXCKvVlm
-vhpC8XWWG+c3LhS90wmEBvKS0AfUeoPDHxMOLyzKgQKBgQD07lZRO0nsc38+PVaI
-NrvlP90Q8OgbwMIC52jmSZK3b5YSh3TrllsbCg6hzUk1SAJsa3qi7B1vq36Fd+rG
-LGDRW9xY0cfShLhzqvZWi45zU/RYnEcWHOuXQshLikx1DWUpg2KbLSVT2/lyvzmn
-QgM1Te8CSxW5vrBRVfluXoJuEwKBgQDjzLAbwk/wdjITKlQtirtsJEzWi3LGuUrg
-Z2kMz+0ztUU5d1oFL9B5xh0CwK8bpK9kYnoVZSy/r5+mGHqyz1eKaDdAXIR13nC0
-g7aZbTZzbt2btvuNZc3NCzRffHF3sCqp8a+oCryHyITjZcA+WYeU8nG0TQ5O8Zgr
-Skbo1JGocwKBgQC4jCx1oFqe0pd5afYdREBnB6ul7B63apHEZmBfw+fMV0OYSoAK
-Uovq37UOrQMQJmXNE16gC5BSZ8E5B5XaI+3/UVvBgK8zK9VfMd3Sb+yxcPyXF4lo
-W/oXSrZoVJgvShyDHv/ZNDb/7KsTjon+QHryWvpPnAuOnON1JXZ/dq6ICQKBgCZF
-AukG8esR0EPL/qxP/ECksIvyjWu5QU0F0m4mmFDxiRmoZWUtrTZoBAOsXz6johuZ
-N61Ue/oQBSAgSKy1jJ1h+LZFVLOAlSqeXhTUditaWryINyaADdz+nuPTwjQ7Uk+O
-nNX8R8P/+eNB+tP+snphaJzDvT2h9NCA//ypiXblAoGAJoLmotPI+P3KIRVzESL0
-DAsVmeijtXE3H+R4nwqUDQbBbFKx0/u2pbON+D5C9llaGiuUp9H+awtwQRYhToeX
-CNguwWrcpuhFOCeXDHDWF/0NIZYD2wBMxjF/eUarvoLaT4Gi0yyWh5ExIKOW4bFk
-EojUPSJ3gomOUp5bIFcSmSU=
------END PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIICrzCCAZcCAQEwDQYJKoZIhvcNAQELBQAwFzEVMBMGA1UECgwMVHJpbyB0ZXN0
-IENBMCAXDTE3MDQwOTEwMDcyMVoYDzIyOTEwMTIyMTAwNzIxWjAiMSAwHgYDVQQD
-DBd0cmlvLXRlc3QtMS5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBANnzLPU6sdftoekSt9rV2zySZh+piiaODLnAOwZ1vyLWAXi7GEcJ
-ENC01kA1U5VcjtHdMxyEDUYzHMBlgO4EO4RSypOS1tMQE1vgaM6HIy7V8Xh1f+0z
-F/sYH5+VPHXvx4TZ7FM5kuZKYFraVqgNV7cKsEBgTudm0EXrfSMoooyxS4iXQJsV
-EdqOmiozd7cJ4KjgpxsHOC6NEDcCxVXrLmqEwfbOouQ5hq6tVR8SR2OqgcWCPVVu
-NamRNutcDE/Bbq/+5krTnpseAGCCjTdHJZ7Fo9qmwXahSbYk11I3fPsw/S6YqmE7
-af2JzIa30GaSbYAqzd0DjlUs9eH42iGW6okCAwEAATANBgkqhkiG9w0BAQsFAAOC
-AQEAlRNA96H88lVnzlpQUYt0pwpoy7B3/CDe8Uvl41thKEfTjb+SIo95F4l+fi+l
-jISWSonAYXRMNqymPMXl2ir0NigxfvvrcjggER3khASIs0l1ICwTNTv2a40NnFY6
-ZjTaBeSZ/lAi7191AkENDYvMl3aGhb6kALVIbos4/5LvJYF/UXvQfrjriLWZq/I3
-WkvduU9oSi0EA4Jt9aAhblsgDHMBL0+LU8Nl1tgzy2/NePcJWjzBRQDlF8uxCQ+2
-LesZongKQ+lebS4eYbNs0s810h8hrOEcn7VWn7FfxZRkjeaKIst2FCHmdr5JJgxj
-8fw+s7l2UkrNURAJ4IRNQvPB+w==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIDBjCCAe6gAwIBAgIJAIUF+wna+nuzMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV
-BAoMDFRyaW8gdGVzdCBDQTAgFw0xNzA0MDkxMDA3MjFaGA8yMjkxMDEyMjEwMDcy
-MVowFzEVMBMGA1UECgwMVHJpbyB0ZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEAyhE82Cbq+2c2f9M+vj2f9v+z+0+bMZDUVPSXhBDiRdKubt+K
-f9vY+ZH3ze1sm0iNgO6xU3OsDTlzO5z0TpsvEEbs0wgsJDUXD7Y8Fb1zH2jaVCro
-Y6KcVfFZvD96zsVCnZy0vMsYJw20iIL0RNCtr17lXWVxd17OoVy91djFD9v/cixu
-LRIr+N7pa8BDLUQUO/g0ui9YSC9Wgf67mr93KXKPGwjTHBGdjeZeex198j5aZjZR
-lkPH/9g5d3hP7EI0EAIMDVd4dvwNJgZzv+AZINbKLAkQyE9AAm+xQ7qBSvdfAvKq
-N/fwaFevmyrxUBcfoQxSpds8njWDb3dQzCn7ywIDAQABo1MwUTAdBgNVHQ4EFgQU
-JiilveozF8Qpyy2fS3wV4foVRCswHwYDVR0jBBgwFoAUJiilveozF8Qpyy2fS3wV
-4foVRCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAkcthPjkJ
-+npvMKyQtUx7CSVcT4Ar0jHrvfPSg17ipyLv+MhSTIbS2VwhSYXxNmu+oBWKuYUs
-BnNxly3+SOcs+dTP3GBMng91SBsz5hhbP4ws8uUtCvYJauzeHbeY67R14RT8Ws/b
-mP6HDiybN7zy6LOKGCiz+sCoJqVZG/yBYO87iQsTTyNttgoG27yUSvzP07EQwUa5
-F9dI9Wn+4b5wP2ofMCu3asTbKXjfFbz3w5OkRgpGYhC4jhDdOw/819+01R9//GrM
-54Gme03yDAAM7nGihr1Xtld3dp2gLuqv0WgxKBqvG5X+nCbr2WamscAP5qz149vo
-y6Hq6P4mm2GmZw==
------END CERTIFICATE-----
diff --git a/notes-to-self/ssl-close-notify/trio-test-CA.pem b/notes-to-self/ssl-close-notify/trio-test-CA.pem
deleted file mode 100644
index 9bf34001b2..0000000000
--- a/notes-to-self/ssl-close-notify/trio-test-CA.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDBjCCAe6gAwIBAgIJAIUF+wna+nuzMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV
-BAoMDFRyaW8gdGVzdCBDQTAgFw0xNzA0MDkxMDA3MjFaGA8yMjkxMDEyMjEwMDcy
-MVowFzEVMBMGA1UECgwMVHJpbyB0ZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEAyhE82Cbq+2c2f9M+vj2f9v+z+0+bMZDUVPSXhBDiRdKubt+K
-f9vY+ZH3ze1sm0iNgO6xU3OsDTlzO5z0TpsvEEbs0wgsJDUXD7Y8Fb1zH2jaVCro
-Y6KcVfFZvD96zsVCnZy0vMsYJw20iIL0RNCtr17lXWVxd17OoVy91djFD9v/cixu
-LRIr+N7pa8BDLUQUO/g0ui9YSC9Wgf67mr93KXKPGwjTHBGdjeZeex198j5aZjZR
-lkPH/9g5d3hP7EI0EAIMDVd4dvwNJgZzv+AZINbKLAkQyE9AAm+xQ7qBSvdfAvKq
-N/fwaFevmyrxUBcfoQxSpds8njWDb3dQzCn7ywIDAQABo1MwUTAdBgNVHQ4EFgQU
-JiilveozF8Qpyy2fS3wV4foVRCswHwYDVR0jBBgwFoAUJiilveozF8Qpyy2fS3wV
-4foVRCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAkcthPjkJ
-+npvMKyQtUx7CSVcT4Ar0jHrvfPSg17ipyLv+MhSTIbS2VwhSYXxNmu+oBWKuYUs
-BnNxly3+SOcs+dTP3GBMng91SBsz5hhbP4ws8uUtCvYJauzeHbeY67R14RT8Ws/b
-mP6HDiybN7zy6LOKGCiz+sCoJqVZG/yBYO87iQsTTyNttgoG27yUSvzP07EQwUa5
-F9dI9Wn+4b5wP2ofMCu3asTbKXjfFbz3w5OkRgpGYhC4jhDdOw/819+01R9//GrM
-54Gme03yDAAM7nGihr1Xtld3dp2gLuqv0WgxKBqvG5X+nCbr2WamscAP5qz149vo
-y6Hq6P4mm2GmZw==
------END CERTIFICATE-----
diff --git a/notes-to-self/ssl-handshake/ssl-handshake.py b/notes-to-self/ssl-handshake/ssl-handshake.py
deleted file mode 100644
index 1665ce3331..0000000000
--- a/notes-to-self/ssl-handshake/ssl-handshake.py
+++ /dev/null
@@ -1,147 +0,0 @@
-import socket
-import ssl
-import threading
-from contextlib import contextmanager
-
-BUFSIZE = 4096
-
-server_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
-server_ctx.load_cert_chain("trio-test-1.pem")
-
-
-def _ssl_echo_serve_sync(sock):
- try:
- wrapped = server_ctx.wrap_socket(sock, server_side=True)
- while True:
- data = wrapped.recv(BUFSIZE)
- if not data:
- wrapped.unwrap()
- return
- wrapped.sendall(data)
- except BrokenPipeError:
- pass
-
-
-@contextmanager
-def echo_server_connection():
- client_sock, server_sock = socket.socketpair()
- with client_sock, server_sock:
- t = threading.Thread(
- target=_ssl_echo_serve_sync,
- args=(server_sock,),
- daemon=True,
- )
- t.start()
-
- yield client_sock
-
-
-class ManuallyWrappedSocket:
- def __init__(self, ctx, sock, **kwargs):
- self.incoming = ssl.MemoryBIO()
- self.outgoing = ssl.MemoryBIO()
- self.obj = ctx.wrap_bio(self.incoming, self.outgoing, **kwargs)
- self.sock = sock
-
- def _retry(self, fn, *args):
- finished = False
- while not finished:
- want_read = False
- try:
- ret = fn(*args)
- except ssl.SSLWantReadError:
- want_read = True
- except ssl.SSLWantWriteError:
- # can't happen, but if it did this would be the right way to
- # handle it anyway
- pass
- else:
- finished = True
- # do any sending
- data = self.outgoing.read()
- if data:
- self.sock.sendall(data)
- # do any receiving
- if want_read:
- data = self.sock.recv(BUFSIZE)
- if not data:
- self.incoming.write_eof()
- else:
- self.incoming.write(data)
- # then retry if necessary
- return ret
-
- def do_handshake(self):
- self._retry(self.obj.do_handshake)
-
- def recv(self, bufsize):
- return self._retry(self.obj.read, bufsize)
-
- def sendall(self, data):
- self._retry(self.obj.write, data)
-
- def unwrap(self):
- self._retry(self.obj.unwrap)
- return self.sock
-
-
-def wrap_socket_via_wrap_socket(ctx, sock, **kwargs):
- return ctx.wrap_socket(sock, do_handshake_on_connect=False, **kwargs)
-
-
-def wrap_socket_via_wrap_bio(ctx, sock, **kwargs):
- return ManuallyWrappedSocket(ctx, sock, **kwargs)
-
-
-for wrap_socket in [
- wrap_socket_via_wrap_socket,
- wrap_socket_via_wrap_bio,
-]:
- print(f"\n--- checking {wrap_socket.__name__} ---\n")
-
- print("checking with do_handshake + correct hostname...")
- with echo_server_connection() as client_sock:
- client_ctx = ssl.create_default_context(cafile="trio-test-CA.pem")
- wrapped = wrap_socket(
- client_ctx,
- client_sock,
- server_hostname="trio-test-1.example.org",
- )
- wrapped.do_handshake()
- wrapped.sendall(b"x")
- assert wrapped.recv(1) == b"x"
- wrapped.unwrap()
- print("...success")
-
- print("checking with do_handshake + wrong hostname...")
- with echo_server_connection() as client_sock:
- client_ctx = ssl.create_default_context(cafile="trio-test-CA.pem")
- wrapped = wrap_socket(
- client_ctx,
- client_sock,
- server_hostname="trio-test-2.example.org",
- )
- try:
- wrapped.do_handshake()
- except Exception:
- print("...got error as expected")
- else:
- print("??? no error ???")
-
- print("checking withOUT do_handshake + wrong hostname...")
- with echo_server_connection() as client_sock:
- client_ctx = ssl.create_default_context(cafile="trio-test-CA.pem")
- wrapped = wrap_socket(
- client_ctx,
- client_sock,
- server_hostname="trio-test-2.example.org",
- )
- # We forgot to call do_handshake
- # But the hostname is wrong so something had better error out...
- sent = b"x"
- print("sending", sent)
- wrapped.sendall(sent)
- got = wrapped.recv(1)
- print("got:", got)
- assert got == sent
- print("!!!! successful chat with invalid host! we have been haxored!")
diff --git a/notes-to-self/ssl-handshake/trio-test-1.pem b/notes-to-self/ssl-handshake/trio-test-1.pem
deleted file mode 100644
index a0c1b773f9..0000000000
--- a/notes-to-self/ssl-handshake/trio-test-1.pem
+++ /dev/null
@@ -1,64 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDZ8yz1OrHX7aHp
-Erfa1ds8kmYfqYomjgy5wDsGdb8i1gF4uxhHCRDQtNZANVOVXI7R3TMchA1GMxzA
-ZYDuBDuEUsqTktbTEBNb4GjOhyMu1fF4dX/tMxf7GB+flTx178eE2exTOZLmSmBa
-2laoDVe3CrBAYE7nZtBF630jKKKMsUuIl0CbFRHajpoqM3e3CeCo4KcbBzgujRA3
-AsVV6y5qhMH2zqLkOYaurVUfEkdjqoHFgj1VbjWpkTbrXAxPwW6v/uZK056bHgBg
-go03RyWexaPapsF2oUm2JNdSN3z7MP0umKphO2n9icyGt9Bmkm2AKs3dA45VLPXh
-+NohluqJAgMBAAECggEARlfWCtAG1ko8F52S+W5MdCBMFawCiq8OLGV+p3cZWYT4
-tJ6uFz81ziaPf+m2MF7POazK8kksf5u/i9k245s6GlseRsL90uE9XknvibjUAinK
-5bYGs+fptYDzs+3WtbnOC3LKc5IBd5JJxwjxLwwfY1RvzldHIChu0CJRISfcTsvR
-occ8hXdeft7svNymvTuwQd05u1yjzL4RwF8Be76i17j5+jDsrAaUKdxxwGNAyOU7
-OKrUY6G851T6NUGgC19iXAJ1wN9tVGIR5QOs3J/s6dCctnX5tN8Di7prkXCKvVlm
-vhpC8XWWG+c3LhS90wmEBvKS0AfUeoPDHxMOLyzKgQKBgQD07lZRO0nsc38+PVaI
-NrvlP90Q8OgbwMIC52jmSZK3b5YSh3TrllsbCg6hzUk1SAJsa3qi7B1vq36Fd+rG
-LGDRW9xY0cfShLhzqvZWi45zU/RYnEcWHOuXQshLikx1DWUpg2KbLSVT2/lyvzmn
-QgM1Te8CSxW5vrBRVfluXoJuEwKBgQDjzLAbwk/wdjITKlQtirtsJEzWi3LGuUrg
-Z2kMz+0ztUU5d1oFL9B5xh0CwK8bpK9kYnoVZSy/r5+mGHqyz1eKaDdAXIR13nC0
-g7aZbTZzbt2btvuNZc3NCzRffHF3sCqp8a+oCryHyITjZcA+WYeU8nG0TQ5O8Zgr
-Skbo1JGocwKBgQC4jCx1oFqe0pd5afYdREBnB6ul7B63apHEZmBfw+fMV0OYSoAK
-Uovq37UOrQMQJmXNE16gC5BSZ8E5B5XaI+3/UVvBgK8zK9VfMd3Sb+yxcPyXF4lo
-W/oXSrZoVJgvShyDHv/ZNDb/7KsTjon+QHryWvpPnAuOnON1JXZ/dq6ICQKBgCZF
-AukG8esR0EPL/qxP/ECksIvyjWu5QU0F0m4mmFDxiRmoZWUtrTZoBAOsXz6johuZ
-N61Ue/oQBSAgSKy1jJ1h+LZFVLOAlSqeXhTUditaWryINyaADdz+nuPTwjQ7Uk+O
-nNX8R8P/+eNB+tP+snphaJzDvT2h9NCA//ypiXblAoGAJoLmotPI+P3KIRVzESL0
-DAsVmeijtXE3H+R4nwqUDQbBbFKx0/u2pbON+D5C9llaGiuUp9H+awtwQRYhToeX
-CNguwWrcpuhFOCeXDHDWF/0NIZYD2wBMxjF/eUarvoLaT4Gi0yyWh5ExIKOW4bFk
-EojUPSJ3gomOUp5bIFcSmSU=
------END PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIICrzCCAZcCAQEwDQYJKoZIhvcNAQELBQAwFzEVMBMGA1UECgwMVHJpbyB0ZXN0
-IENBMCAXDTE3MDQwOTEwMDcyMVoYDzIyOTEwMTIyMTAwNzIxWjAiMSAwHgYDVQQD
-DBd0cmlvLXRlc3QtMS5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBANnzLPU6sdftoekSt9rV2zySZh+piiaODLnAOwZ1vyLWAXi7GEcJ
-ENC01kA1U5VcjtHdMxyEDUYzHMBlgO4EO4RSypOS1tMQE1vgaM6HIy7V8Xh1f+0z
-F/sYH5+VPHXvx4TZ7FM5kuZKYFraVqgNV7cKsEBgTudm0EXrfSMoooyxS4iXQJsV
-EdqOmiozd7cJ4KjgpxsHOC6NEDcCxVXrLmqEwfbOouQ5hq6tVR8SR2OqgcWCPVVu
-NamRNutcDE/Bbq/+5krTnpseAGCCjTdHJZ7Fo9qmwXahSbYk11I3fPsw/S6YqmE7
-af2JzIa30GaSbYAqzd0DjlUs9eH42iGW6okCAwEAATANBgkqhkiG9w0BAQsFAAOC
-AQEAlRNA96H88lVnzlpQUYt0pwpoy7B3/CDe8Uvl41thKEfTjb+SIo95F4l+fi+l
-jISWSonAYXRMNqymPMXl2ir0NigxfvvrcjggER3khASIs0l1ICwTNTv2a40NnFY6
-ZjTaBeSZ/lAi7191AkENDYvMl3aGhb6kALVIbos4/5LvJYF/UXvQfrjriLWZq/I3
-WkvduU9oSi0EA4Jt9aAhblsgDHMBL0+LU8Nl1tgzy2/NePcJWjzBRQDlF8uxCQ+2
-LesZongKQ+lebS4eYbNs0s810h8hrOEcn7VWn7FfxZRkjeaKIst2FCHmdr5JJgxj
-8fw+s7l2UkrNURAJ4IRNQvPB+w==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIDBjCCAe6gAwIBAgIJAIUF+wna+nuzMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV
-BAoMDFRyaW8gdGVzdCBDQTAgFw0xNzA0MDkxMDA3MjFaGA8yMjkxMDEyMjEwMDcy
-MVowFzEVMBMGA1UECgwMVHJpbyB0ZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEAyhE82Cbq+2c2f9M+vj2f9v+z+0+bMZDUVPSXhBDiRdKubt+K
-f9vY+ZH3ze1sm0iNgO6xU3OsDTlzO5z0TpsvEEbs0wgsJDUXD7Y8Fb1zH2jaVCro
-Y6KcVfFZvD96zsVCnZy0vMsYJw20iIL0RNCtr17lXWVxd17OoVy91djFD9v/cixu
-LRIr+N7pa8BDLUQUO/g0ui9YSC9Wgf67mr93KXKPGwjTHBGdjeZeex198j5aZjZR
-lkPH/9g5d3hP7EI0EAIMDVd4dvwNJgZzv+AZINbKLAkQyE9AAm+xQ7qBSvdfAvKq
-N/fwaFevmyrxUBcfoQxSpds8njWDb3dQzCn7ywIDAQABo1MwUTAdBgNVHQ4EFgQU
-JiilveozF8Qpyy2fS3wV4foVRCswHwYDVR0jBBgwFoAUJiilveozF8Qpyy2fS3wV
-4foVRCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAkcthPjkJ
-+npvMKyQtUx7CSVcT4Ar0jHrvfPSg17ipyLv+MhSTIbS2VwhSYXxNmu+oBWKuYUs
-BnNxly3+SOcs+dTP3GBMng91SBsz5hhbP4ws8uUtCvYJauzeHbeY67R14RT8Ws/b
-mP6HDiybN7zy6LOKGCiz+sCoJqVZG/yBYO87iQsTTyNttgoG27yUSvzP07EQwUa5
-F9dI9Wn+4b5wP2ofMCu3asTbKXjfFbz3w5OkRgpGYhC4jhDdOw/819+01R9//GrM
-54Gme03yDAAM7nGihr1Xtld3dp2gLuqv0WgxKBqvG5X+nCbr2WamscAP5qz149vo
-y6Hq6P4mm2GmZw==
------END CERTIFICATE-----
diff --git a/notes-to-self/ssl-handshake/trio-test-CA.pem b/notes-to-self/ssl-handshake/trio-test-CA.pem
deleted file mode 100644
index 9bf34001b2..0000000000
--- a/notes-to-self/ssl-handshake/trio-test-CA.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDBjCCAe6gAwIBAgIJAIUF+wna+nuzMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV
-BAoMDFRyaW8gdGVzdCBDQTAgFw0xNzA0MDkxMDA3MjFaGA8yMjkxMDEyMjEwMDcy
-MVowFzEVMBMGA1UECgwMVHJpbyB0ZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEAyhE82Cbq+2c2f9M+vj2f9v+z+0+bMZDUVPSXhBDiRdKubt+K
-f9vY+ZH3ze1sm0iNgO6xU3OsDTlzO5z0TpsvEEbs0wgsJDUXD7Y8Fb1zH2jaVCro
-Y6KcVfFZvD96zsVCnZy0vMsYJw20iIL0RNCtr17lXWVxd17OoVy91djFD9v/cixu
-LRIr+N7pa8BDLUQUO/g0ui9YSC9Wgf67mr93KXKPGwjTHBGdjeZeex198j5aZjZR
-lkPH/9g5d3hP7EI0EAIMDVd4dvwNJgZzv+AZINbKLAkQyE9AAm+xQ7qBSvdfAvKq
-N/fwaFevmyrxUBcfoQxSpds8njWDb3dQzCn7ywIDAQABo1MwUTAdBgNVHQ4EFgQU
-JiilveozF8Qpyy2fS3wV4foVRCswHwYDVR0jBBgwFoAUJiilveozF8Qpyy2fS3wV
-4foVRCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAkcthPjkJ
-+npvMKyQtUx7CSVcT4Ar0jHrvfPSg17ipyLv+MhSTIbS2VwhSYXxNmu+oBWKuYUs
-BnNxly3+SOcs+dTP3GBMng91SBsz5hhbP4ws8uUtCvYJauzeHbeY67R14RT8Ws/b
-mP6HDiybN7zy6LOKGCiz+sCoJqVZG/yBYO87iQsTTyNttgoG27yUSvzP07EQwUa5
-F9dI9Wn+4b5wP2ofMCu3asTbKXjfFbz3w5OkRgpGYhC4jhDdOw/819+01R9//GrM
-54Gme03yDAAM7nGihr1Xtld3dp2gLuqv0WgxKBqvG5X+nCbr2WamscAP5qz149vo
-y6Hq6P4mm2GmZw==
------END CERTIFICATE-----
diff --git a/notes-to-self/sslobject.py b/notes-to-self/sslobject.py
deleted file mode 100644
index a6e7b07a08..0000000000
--- a/notes-to-self/sslobject.py
+++ /dev/null
@@ -1,78 +0,0 @@
-import ssl
-from contextlib import contextmanager
-
-client_ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
-client_ctx.check_hostname = False
-client_ctx.verify_mode = ssl.CERT_NONE
-
-cinb = ssl.MemoryBIO()
-coutb = ssl.MemoryBIO()
-cso = client_ctx.wrap_bio(cinb, coutb)
-
-server_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
-server_ctx.load_cert_chain("server.crt", "server.key", "xxxx")
-sinb = ssl.MemoryBIO()
-soutb = ssl.MemoryBIO()
-sso = server_ctx.wrap_bio(sinb, soutb, server_side=True)
-
-
-@contextmanager
-def expect(etype):
- try:
- yield
- except etype:
- pass
- else:
- raise AssertionError(f"expected {etype}")
-
-
-with expect(ssl.SSLWantReadError):
- cso.do_handshake()
-assert not cinb.pending
-assert coutb.pending
-
-with expect(ssl.SSLWantReadError):
- sso.do_handshake()
-assert not sinb.pending
-assert not soutb.pending
-
-# A trickle is not enough
-# sinb.write(coutb.read(1))
-# with expect(ssl.SSLWantReadError):
-# cso.do_handshake()
-# with expect(ssl.SSLWantReadError):
-# sso.do_handshake()
-
-sinb.write(coutb.read())
-# Now it should be able to respond
-with expect(ssl.SSLWantReadError):
- sso.do_handshake()
-assert soutb.pending
-
-cinb.write(soutb.read())
-with expect(ssl.SSLWantReadError):
- cso.do_handshake()
-
-sinb.write(coutb.read())
-# server done!
-sso.do_handshake()
-assert soutb.pending
-
-# client done!
-cinb.write(soutb.read())
-cso.do_handshake()
-
-cso.write(b"hello")
-sinb.write(coutb.read())
-assert sso.read(10) == b"hello"
-with expect(ssl.SSLWantReadError):
- sso.read(10)
-
-# cso.write(b"x" * 2 ** 30)
-# print(coutb.pending)
-
-assert not coutb.pending
-assert not cinb.pending
-sso.do_handshake()
-assert not coutb.pending
-assert not cinb.pending
diff --git a/notes-to-self/subprocess-notes.txt b/notes-to-self/subprocess-notes.txt
deleted file mode 100644
index d3a5c1096c..0000000000
--- a/notes-to-self/subprocess-notes.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-# subprocesses are a huge hassle
-# on Linux there is simply no way to async wait for a child to exit except by
-# messing with SIGCHLD and that is ... *such* a mess. Not really
-# tenable. We're better off trying os.waitpid(..., os.WNOHANG), and if that
-# says the process is still going then spawn a thread to sit in waitpid.
-# ......though that waitpid is non-cancellable so ugh. this is a problem,
-# because it's also mutating -- you only get to waitpid() once, and you have
-# to do it, because zombies. I guess we could make sure the waitpid thread is
-# daemonic and either it gets back to us eventually (even if our first call to
-# 'await wait()' is cancelled, maybe another one won't be), or else we go away
-# and don't care anymore.
-# I guess simplest is just to spawn a thread at the same time as we spawn the
-# process, with more reasonable notification semantics.
-# or we can poll every 100 ms or something, sigh.
-
-# on Mac/*BSD then kqueue works, go them. (maybe have WNOHANG after turning it
-# on to avoid a race condition I guess)
-
-# on Windows, you can either do the thread thing, or something involving
-# WaitForMultipleObjects, or the Job Object API:
-# https://stackoverflow.com/questions/17724859/detecting-exit-failure-of-child-processes-using-iocp-c-windows
-# (see also the comments here about using the Job Object API:
-# https://stackoverflow.com/questions/23434842/python-how-to-kill-child-processes-when-parent-dies/23587108#23587108)
-# however the docs say:
-# "Note that, with the exception of limits set with the
-# JobObjectNotificationLimitInformation information class, delivery of
-# messages to the completion port is not guaranteed; failure of a message to
-# arrive does not necessarily mean that the event did not occur"
-#
-# oh windows wtf
-
-# We'll probably want to mess with the job API anyway for worker processes
-# (b/c that's the reliable way to make sure we never leave residual worker
-# processes around after exiting, see that stackoverflow question again), so
-# maybe this isn't too big a hassle? waitpid is probably easiest for the
-# first-pass implementation though.
-
-# the handle version has the same issues as waitpid on Linux, except I guess
-# that on windows the waitpid equivalent doesn't consume the handle.
-# -- wait no, the windows equivalent takes a timeout! and we know our
-# cancellation deadline going in, so that's actually okay. (Still need to use
-# a thread but whatever.)
-
-# asyncio does RegisterWaitForSingleObject with a callback that does
-# PostQueuedCompletionStatus.
-# this is just a thread pool in disguise (and in principle could have weird
-# problems if you have enough children and run out of threads)
-# it's possible we could do something with a thread that just sits in
-# an alertable state and handle callbacks...? though hmm, maybe the set of
-# events that can notify via callbacks is equivalent to the set that can
-# notify via IOCP.
-# there's WaitForMultipleObjects to let multiple waits share a thread I
-# guess.
-# you can wake up a WaitForMultipleObjectsEx on-demand by using QueueUserAPC
-# to send a no-op APC to its thread.
-# this is also a way to cancel a WaitForSingleObjectEx, actually. So it
-# actually is possible to cancel the equivalent of a waitpid on Windows.
-
-# Potentially useful observation: you *can* use a socket as the
-# stdin/stdout/stderr for a child, iff you create that socket *without*
-# WSA_FLAG_OVERLAPPED:
-# http://stackoverflow.com/a/5725609
-# Here's ncm's Windows implementation of socketpair, which has a flag to
-# control whether one of the sockets has WSA_FLAG_OVERLAPPED set:
-# https://github.com/ncm/selectable-socketpair/blob/master/socketpair.c
-# (it also uses listen(1) so it's robust against someone intercepting things,
-# unlike the version in socket.py... not sure anyone really cares, but
-# hey. OTOH it only supports AF_INET, while socket.py supports AF_INET6,
-# fancy.)
-# (or it would be trivial to (re)implement in python, using either
-# socket.socketpair or ncm's version as a model, given a cffi function to
-# create the non-overlapped socket in the first place then just pass it into
-# the socket.socket constructor (avoiding the dup() that fromfd does).)
diff --git a/notes-to-self/thread-closure-bug-demo.py b/notes-to-self/thread-closure-bug-demo.py
deleted file mode 100644
index b5da68c334..0000000000
--- a/notes-to-self/thread-closure-bug-demo.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# This is a reproducer for:
-# https://bugs.python.org/issue30744
-# https://bitbucket.org/pypy/pypy/issues/2591/
-
-import sys
-import threading
-import time
-
-COUNT = 100
-
-
-def slow_tracefunc(frame, event, arg):
- # A no-op trace function that sleeps briefly to make us more likely to hit
- # the race condition.
- time.sleep(0.01)
- return slow_tracefunc
-
-
-def run_with_slow_tracefunc(fn):
- # settrace() only takes effect when you enter a new frame, so we need this
- # little dance:
- sys.settrace(slow_tracefunc)
- return fn()
-
-
-def outer():
- x = 0
- # We hide the done variable inside a list, because we want to use it to
- # communicate between the main thread and the looper thread, and the bug
- # here is that variable assignments made in the main thread disappear
- # before the child thread can see them...
- done = [False]
-
- def traced_looper():
- # Force w_locals to be instantiated (only matters on PyPy; on CPython
- # you can comment this line out and everything stays the same)
- print(locals())
- nonlocal x # Force x to be closed over
- # Random nonsense whose only purpose is to trigger lots of calls to
- # the trace func
- count = 0
- while not done[0]:
- count += 1
- return count
-
- t = threading.Thread(target=run_with_slow_tracefunc, args=(traced_looper,))
- t.start()
-
- for i in range(COUNT):
- print(f"after {i} increments, x is {x}")
- x += 1
- time.sleep(0.01)
-
- done[0] = True
- t.join()
-
- print(f"Final discrepancy: {COUNT - x} (should be 0)")
-
-
-outer()
diff --git a/notes-to-self/thread-dispatch-bench.py b/notes-to-self/thread-dispatch-bench.py
deleted file mode 100644
index e752c27e04..0000000000
--- a/notes-to-self/thread-dispatch-bench.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Estimate the cost of simply passing some data into a thread and back, in as
-# minimal a fashion as possible.
-#
-# This is useful to get a sense of the *lower-bound* cost of
-# trio.to_thread.run_sync
-
-import threading
-import time
-from queue import Queue
-
-COUNT = 10000
-
-
-def worker(in_q, out_q):
- while True:
- job = in_q.get()
- out_q.put(job())
-
-
-def main():
- in_q = Queue()
- out_q = Queue()
-
- t = threading.Thread(target=worker, args=(in_q, out_q))
- t.start()
-
- while True:
- start = time.monotonic()
- for _ in range(COUNT):
- in_q.put(lambda: None)
- out_q.get()
- end = time.monotonic()
- print(f"{(end - start) / COUNT * 1e6:.2f} μs/job")
-
-
-main()
diff --git a/notes-to-self/time-wait-windows-exclusiveaddruse.py b/notes-to-self/time-wait-windows-exclusiveaddruse.py
deleted file mode 100644
index dcb4a27dd0..0000000000
--- a/notes-to-self/time-wait-windows-exclusiveaddruse.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# On windows, what does SO_EXCLUSIVEADDRUSE actually do? Apparently not what
-# the documentation says!
-# See: https://stackoverflow.com/questions/45624916/
-#
-# Specifically, this script seems to demonstrate that it only creates
-# conflicts between listening sockets, *not* lingering connected sockets.
-
-import socket
-from contextlib import contextmanager
-
-
-@contextmanager
-def report_outcome(tagline):
- try:
- yield
- except OSError as exc:
- print(f"{tagline}: failed")
- print(f" details: {exc!r}")
- else:
- print(f"{tagline}: succeeded")
-
-
-# Set up initial listening socket
-lsock = socket.socket()
-lsock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
-lsock.bind(("127.0.0.1", 0))
-sockaddr = lsock.getsockname()
-lsock.listen(10)
-
-# Make connected client and server sockets
-csock = socket.socket()
-csock.connect(sockaddr)
-ssock, _ = lsock.accept()
-
-print("lsock", lsock.getsockname())
-print("ssock", ssock.getsockname())
-
-# Can't make a second listener while the first exists
-probe = socket.socket()
-probe.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
-with report_outcome("rebind with existing listening socket"):
- probe.bind(sockaddr)
-
-# Now we close the first listen socket, while leaving the connected sockets
-# open:
-lsock.close()
-# This time binding succeeds (contra MSDN!)
-probe = socket.socket()
-probe.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
-with report_outcome("rebind with live connected sockets"):
- probe.bind(sockaddr)
- probe.listen(10)
- print("probe", probe.getsockname())
- print("ssock", ssock.getsockname())
-probe.close()
-
-# Server-initiated close to trigger TIME_WAIT status
-ssock.send(b"x")
-assert csock.recv(1) == b"x"
-ssock.close()
-assert csock.recv(1) == b""
-
-# And does the TIME_WAIT sock prevent binding?
-probe = socket.socket()
-probe.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
-with report_outcome("rebind with TIME_WAIT socket"):
- probe.bind(sockaddr)
- probe.listen(10)
-probe.close()
diff --git a/notes-to-self/time-wait.py b/notes-to-self/time-wait.py
deleted file mode 100644
index edc1b39172..0000000000
--- a/notes-to-self/time-wait.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# what does SO_REUSEADDR do, exactly?
-
-# Theory:
-#
-# - listen1 is bound to port P
-# - listen1.accept() returns a connected socket server1, which is also bound
-# to port P
-# - listen1 is closed
-# - we attempt to bind listen2 to port P
-# - this fails because server1 is still open, or still in TIME_WAIT, and you
-# can't use bind() to bind to a port that still has sockets on it, unless
-# both those sockets and the socket being bound have SO_REUSEADDR
-#
-# The standard way to avoid this is to set SO_REUSEADDR on all listening
-# sockets before binding them. And this works, but for somewhat more
-# complicated reasons than are often appreciated.
-#
-# In our scenario above it doesn't really matter for listen1 (assuming the
-# port is initially unused).
-#
-# What is important is that it's set on *server1*. Setting it on listen1
-# before calling bind() automatically accomplishes this, because SO_REUSEADDR
-# is inherited by accept()ed sockets. But it also works to set it on listen1
-# any time before calling accept(), or to set it on server1 directly.
-#
-# Also, it must be set on listen2 before calling bind(), or it will conflict
-# with the lingering server1 socket.
-
-import errno
-import socket
-
-import attrs
-
-
-@attrs.define(repr=False, slots=False)
-class Options:
- listen1_early = None
- listen1_middle = None
- listen1_late = None
- server = None
- listen2 = None
-
- def set(self, which, sock):
- value = getattr(self, which)
- if value is not None:
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, value)
-
- def describe(self):
- info = []
- for f in attrs.fields(self.__class__):
- value = getattr(self, f.name)
- if value is not None:
- info.append(f"{f.name}={value}")
- return "Set/unset: {}".format(", ".join(info))
-
-
-def time_wait(options):
- print(options.describe())
-
- # Find a pristine port (one we can definitely bind to without
- # SO_REUSEADDR)
- listen0 = socket.socket()
- listen0.bind(("127.0.0.1", 0))
- sockaddr = listen0.getsockname()
- # print(" ", sockaddr)
- listen0.close()
-
- listen1 = socket.socket()
- options.set("listen1_early", listen1)
- listen1.bind(sockaddr)
- listen1.listen(1)
-
- options.set("listen1_middle", listen1)
-
- client = socket.socket()
- client.connect(sockaddr)
-
- options.set("listen1_late", listen1)
-
- server, _ = listen1.accept()
-
- options.set("server", server)
-
- # Server initiated close to trigger TIME_WAIT status
- server.close()
- assert client.recv(10) == b""
- client.close()
-
- listen1.close()
-
- listen2 = socket.socket()
- options.set("listen2", listen2)
- try:
- listen2.bind(sockaddr)
- except OSError as exc:
- if exc.errno == errno.EADDRINUSE:
- print(" -> EADDRINUSE")
- else:
- raise
- else:
- print(" -> ok")
-
-
-time_wait(Options())
-time_wait(Options(listen1_early=True, server=True, listen2=True))
-time_wait(Options(listen1_early=True))
-time_wait(Options(server=True))
-time_wait(Options(listen2=True))
-time_wait(Options(listen1_early=True, listen2=True))
-time_wait(Options(server=True, listen2=True))
-time_wait(Options(listen1_middle=True, listen2=True))
-time_wait(Options(listen1_late=True, listen2=True))
-time_wait(Options(listen1_middle=True, server=False, listen2=True))
diff --git a/notes-to-self/trace.py b/notes-to-self/trace.py
deleted file mode 100644
index 046412d3ae..0000000000
--- a/notes-to-self/trace.py
+++ /dev/null
@@ -1,154 +0,0 @@
-import json
-import os
-from itertools import count
-
-import trio
-
-# Experiment with generating Chrome Event Trace format, which can be browsed
-# through chrome://tracing or other mechanisms.
-#
-# Screenshot: https://files.gitter.im/python-trio/general/fp6w/image.png
-#
-# Trace format docs: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#
-#
-# Things learned so far:
-# - I don't understand how the ph="s"/ph="f" flow events work – I think
-# they're supposed to show up as arrows, and I'm emitting them between tasks
-# that wake each other up, but they're not showing up.
-# - I think writing out json synchronously from each event is creating gaps in
-# the trace; maybe better to batch them up to write up all at once at the
-# end
-# - including tracebacks would be cool
-# - there doesn't seem to be any good way to group together tasks based on
-# nurseries. this really limits the value of this particular trace
-# format+viewer for us. (also maybe we should have an instrumentation event
-# when a nursery is opened/closed?)
-# - task._counter should maybe be public
-# - I don't know how to best show task lifetime, scheduling times, and what
-# the task is actually doing on the same plot. if we want to show particular
-# events like "called stream.send_all", then the chrome trace format won't
-# let us also show "task is running", because neither kind of event is
-# strictly nested inside the other
-
-
-class Trace(trio.abc.Instrument):
- def __init__(self, out):
- self.out = out
- self.out.write("[\n")
- self.ids = count()
- self._task_metadata(-1, "I/O manager")
-
- def _write(self, **ev):
- ev.setdefault("pid", os.getpid())
- if ev["ph"] != "M":
- ev.setdefault("ts", trio.current_time() * 1e6)
- self.out.write(json.dumps(ev))
- self.out.write(",\n")
-
- def _task_metadata(self, tid, name):
- self._write(
- name="thread_name",
- ph="M",
- tid=tid,
- args={"name": name},
- )
- self._write(
- name="thread_sort_index",
- ph="M",
- tid=tid,
- args={"sort_index": tid},
- )
-
- def task_spawned(self, task):
- self._task_metadata(task._counter, task.name)
- self._write(
- name="task lifetime",
- ph="B",
- tid=task._counter,
- )
-
- def task_exited(self, task):
- self._write(
- name="task lifetime",
- ph="E",
- tid=task._counter,
- )
-
- def before_task_step(self, task):
- self._write(
- name="running",
- ph="B",
- tid=task._counter,
- )
-
- def after_task_step(self, task):
- self._write(
- name="running",
- ph="E",
- tid=task._counter,
- )
-
- def task_scheduled(self, task):
- try:
- waker = trio.lowlevel.current_task()
- except RuntimeError:
- pass
- else:
- id_ = next(self.ids)
- self._write(
- ph="s",
- cat="wakeup",
- id=id_,
- tid=waker._counter,
- )
- self._write(
- cat="wakeup",
- ph="f",
- id=id_,
- tid=task._counter,
- )
-
- def before_io_wait(self, timeout):
- self._write(
- name="I/O wait",
- ph="B",
- tid=-1,
- )
-
- def after_io_wait(self, timeout):
- self._write(
- name="I/O wait",
- ph="E",
- tid=-1,
- )
-
-
-async def child1():
- print(" child1: started! sleeping now...")
- await trio.sleep(1)
- print(" child1: exiting!")
-
-
-async def child2():
- print(" child2: started! sleeping now...")
- await trio.sleep(1)
- print(" child2: exiting!")
-
-
-async def parent():
- print("parent: started!")
- async with trio.open_nursery() as nursery:
- print("parent: spawning child1...")
- nursery.start_soon(child1)
-
- print("parent: spawning child2...")
- nursery.start_soon(child2)
-
- print("parent: waiting for children to finish...")
- # -- we exit the nursery block here --
- print("parent: all done!")
-
-
-with open("/tmp/t.json", "w") as t_json:
- t = Trace(t_json)
- trio.run(parent, instruments=[t])
diff --git a/notes-to-self/trivial-err.py b/notes-to-self/trivial-err.py
deleted file mode 100644
index 1e7b7d9e10..0000000000
--- a/notes-to-self/trivial-err.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import sys
-
-import trio
-
-sys.stderr = sys.stdout
-
-
-async def child1(): # noqa: RUF029 # async not required
- raise ValueError
-
-
-async def child2():
- async with trio.open_nursery() as nursery:
- nursery.start_soon(grandchild1)
- nursery.start_soon(grandchild2)
-
-
-async def grandchild1(): # noqa: RUF029 # async not required
- raise KeyError
-
-
-async def grandchild2(): # noqa: RUF029 # async not required
- raise NameError("Bob")
-
-
-async def main():
- async with trio.open_nursery() as nursery:
- nursery.start_soon(child1)
- nursery.start_soon(child2)
- # nursery.start_soon(grandchild1)
-
-
-trio.run(main)
diff --git a/notes-to-self/trivial.py b/notes-to-self/trivial.py
deleted file mode 100644
index a7e0d17d8b..0000000000
--- a/notes-to-self/trivial.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import trio
-
-
-async def foo(): # noqa: RUF029 # await not used
- print("in foo!")
- return 3
-
-
-print("running!")
-print(trio.run(foo))
diff --git a/notes-to-self/wakeup-fd-racer.py b/notes-to-self/wakeup-fd-racer.py
deleted file mode 100644
index 97faa0dfdf..0000000000
--- a/notes-to-self/wakeup-fd-racer.py
+++ /dev/null
@@ -1,105 +0,0 @@
-import itertools
-import os
-import select
-import signal
-import socket
-import threading
-import time
-
-# Equivalent to the C function raise(), which Python doesn't wrap
-if os.name == "nt":
- import cffi
-
- _ffi = cffi.FFI()
- _ffi.cdef("int raise(int);")
- _lib = _ffi.dlopen("api-ms-win-crt-runtime-l1-1-0.dll")
- signal_raise = getattr(_lib, "raise")
-else:
-
- def signal_raise(signum):
- # Use pthread_kill to make sure we're actually using the wakeup fd on
- # Unix
- signal.pthread_kill(threading.get_ident(), signum)
-
-
-def raise_SIGINT_soon():
- time.sleep(1)
- signal_raise(signal.SIGINT)
- # Sending 2 signals becomes reliable, as we'd expect (because we need
- # set-flags -> write-to-fd, and doing it twice does
- # write-to-fd -> set-flags -> write-to-fd -> set-flags)
- # signal_raise(signal.SIGINT)
-
-
-def drain(sock):
- total = 0
- try:
- while True:
- total += len(sock.recv(1024))
- except BlockingIOError:
- pass
- return total
-
-
-def main():
- writer, reader = socket.socketpair()
- writer.setblocking(False)
- reader.setblocking(False)
-
- signal.set_wakeup_fd(writer.fileno())
-
- # Keep trying until we lose the race...
- for attempt in itertools.count():
- print(f"Attempt {attempt}: start")
-
- # Make sure the socket is empty
- drained = drain(reader)
- if drained:
- print(f"Attempt {attempt}: ({drained} residual bytes discarded)")
-
- # Arrange for SIGINT to be delivered 1 second from now
- thread = threading.Thread(target=raise_SIGINT_soon)
- thread.start()
-
- # Fake an IO loop that's trying to sleep for 10 seconds (but will
- # hopefully get interrupted after just 1 second)
- start = time.perf_counter()
- target = start + 10
- try:
- select_calls = 0
- drained = 0
- while True:
- now = time.perf_counter()
- if now > target:
- break
- select_calls += 1
- r, _, _ = select.select([reader], [], [], target - now)
- if r:
- # In theory we should loop to fully drain the socket but
- # honestly there's 1 byte in there at most and it'll be
- # fine.
- drained += drain(reader)
- except KeyboardInterrupt:
- pass
- else:
- print(f"Attempt {attempt}: no KeyboardInterrupt?!")
-
- # We expect a successful run to take 1 second, and a failed run to
- # take 10 seconds, so 2 seconds is a reasonable cutoff to distinguish
- # them.
- duration = time.perf_counter() - start
- if duration < 2:
- print(
- f"Attempt {attempt}: OK, trying again "
- f"(select_calls = {select_calls}, drained = {drained})",
- )
- else:
- print(f"Attempt {attempt}: FAILED, took {duration} seconds")
- print(f"select_calls = {select_calls}, drained = {drained}")
- break
-
- thread.join()
-
-
-if __name__ == "__main__":
- main()
diff --git a/notes-to-self/win-waitable-timer.py b/notes-to-self/win-waitable-timer.py
deleted file mode 100644
index b8d9af6cad..0000000000
--- a/notes-to-self/win-waitable-timer.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# Sandbox for exploring the Windows "waitable timer" API.
-# Cf https://github.com/python-trio/trio/issues/173
-#
-# Observations:
-# - if you set a timer in the far future, then block in
-# WaitForMultipleObjects, then set the computer's clock forward by a few
-# years (past the target sleep time), then the timer immediately wakes up
-# (which is good!)
-# - if you set a timer in the past, then it wakes up immediately
-
-# Random thoughts:
-# - top-level API sleep_until_datetime
-# - portable manages the heap of outstanding sleeps, runs a system task to
-# wait for the next one, wakes up tasks when their deadline arrives, etc.
-# - non-portable code: async def sleep_until_datetime_raw, which simply blocks
-# until the given time using system-specific methods. Can assume that there
-# is only one call to this method at a time.
-# Actually, this should be a method, so it can hold persistent state (e.g.
-# timerfd).
-# Can assume that the datetime passed in has tzinfo=timezone.utc
-# Need a way to override this object for testing.
-#
-# should we expose wake-system-on-alarm functionality? windows and linux both
-# make this fairly straightforward, but you obviously need to use a separate
-# time source
-
-import contextlib
-from datetime import datetime, timedelta, timezone
-
-import cffi
-import trio
-from trio._core._windows_cffi import ffi, kernel32, raise_winerror
-
-with contextlib.suppress(cffi.CDefError):
- ffi.cdef(
- """
-typedef struct _PROCESS_LEAP_SECOND_INFO {
- ULONG Flags;
- ULONG Reserved;
-} PROCESS_LEAP_SECOND_INFO, *PPROCESS_LEAP_SECOND_INFO;
-
-typedef struct _SYSTEMTIME {
- WORD wYear;
- WORD wMonth;
- WORD wDayOfWeek;
- WORD wDay;
- WORD wHour;
- WORD wMinute;
- WORD wSecond;
- WORD wMilliseconds;
-} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
-""",
- )
-
-ffi.cdef(
- """
-typedef LARGE_INTEGER FILETIME;
-typedef FILETIME* LPFILETIME;
-
-HANDLE CreateWaitableTimerW(
- LPSECURITY_ATTRIBUTES lpTimerAttributes,
- BOOL bManualReset,
- LPCWSTR lpTimerName
-);
-
-BOOL SetWaitableTimer(
- HANDLE hTimer,
- const LPFILETIME lpDueTime,
- LONG lPeriod,
- void* pfnCompletionRoutine,
- LPVOID lpArgToCompletionRoutine,
- BOOL fResume
-);
-
-BOOL SetProcessInformation(
- HANDLE hProcess,
- /* Really an enum, PROCESS_INFORMATION_CLASS */
- int32_t ProcessInformationClass,
- LPVOID ProcessInformation,
- DWORD ProcessInformationSize
-);
-
-void GetSystemTimeAsFileTime(
- LPFILETIME lpSystemTimeAsFileTime
-);
-
-BOOL SystemTimeToFileTime(
- const SYSTEMTIME *lpSystemTime,
- LPFILETIME lpFileTime
-);
-""",
- override=True,
-)
-
-ProcessLeapSecondInfo = 8
-PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND = 1
-
-
-def set_leap_seconds_enabled(enabled):
- plsi = ffi.new("PROCESS_LEAP_SECOND_INFO*")
- if enabled:
- plsi.Flags = PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND
- else:
- plsi.Flags = 0
- plsi.Reserved = 0
- if not kernel32.SetProcessInformation(
- ffi.cast("HANDLE", -1), # current process
- ProcessLeapSecondInfo,
- plsi,
- ffi.sizeof("PROCESS_LEAP_SECOND_INFO"),
- ):
- raise_winerror()
-
-
-def now_as_filetime():
- ft = ffi.new("LARGE_INTEGER*")
- kernel32.GetSystemTimeAsFileTime(ft)
- return ft[0]
-
-
-# "FILETIME" is a specific Windows time representation, that I guess was used
-# for files originally but now gets used in all kinds of non-file-related
-# places. Essentially: integer count of "ticks" since an epoch in 1601, where
-# each tick is 100 nanoseconds, in UTC but pretending that leap seconds don't
-# exist. (Fortunately, the Python datetime module also pretends that
-# leapseconds don't exist, so we can use datetime arithmetic to compute
-# FILETIME values.)
-#
-# https://docs.microsoft.com/en-us/windows/win32/sysinfo/file-times
-#
-# This page has FILETIME converters and can be useful for debugging:
-#
-# https://www.epochconverter.com/ldap
-#
-FILETIME_TICKS_PER_SECOND = 10**7
-FILETIME_EPOCH = datetime.strptime("1601-01-01 00:00:00 Z", "%Y-%m-%d %H:%M:%S %z")
-# XXX THE ABOVE IS WRONG:
-#
-# https://techcommunity.microsoft.com/t5/networking-blog/leap-seconds-for-the-appdev-what-you-should-know/ba-p/339813#
-#
-# Sometimes Windows FILETIME does include leap seconds! It depends on Windows
-# version, process-global state, environment state, registry settings, and who
-# knows what else!
-#
-# So actually the only correct way to convert a YMDhms-style representation of
-# a time into a FILETIME is to use SystemTimeToFileTime
-#
-# ...also I can't even run this test on my VM, because it's running an ancient
-# version of Win10 that doesn't have leap second support. Also also, Windows
-# only tracks leap seconds since they added leap second support, and there
-# haven't been any, so right now things work correctly either way.
-#
-# It is possible to insert some fake leap seconds for testing, if you want.
-
-
-def py_datetime_to_win_filetime(dt):
- # We'll want to call this on every datetime as it comes in
- # dt = dt.astimezone(timezone.utc)
- assert dt.tzinfo is timezone.utc
- return round((dt - FILETIME_EPOCH).total_seconds() * FILETIME_TICKS_PER_SECOND)
-
-
-async def main():
- h = kernel32.CreateWaitableTimerW(ffi.NULL, True, ffi.NULL)
- if not h:
- raise_winerror()
- print(h)
-
- SECONDS = 2
-
- wakeup = datetime.now(timezone.utc) + timedelta(seconds=SECONDS)
- wakeup_filetime = py_datetime_to_win_filetime(wakeup)
- wakeup_cffi = ffi.new("LARGE_INTEGER *")
- wakeup_cffi[0] = wakeup_filetime
-
- print(wakeup_filetime, wakeup_cffi)
-
- print(f"Sleeping for {SECONDS} seconds (until {wakeup})")
-
- if not kernel32.SetWaitableTimer(
- h,
- wakeup_cffi,
- 0,
- ffi.NULL,
- ffi.NULL,
- False,
- ):
- raise_winerror()
-
- await trio.hazmat.WaitForSingleObject(h)
-
- print(f"Current FILETIME: {now_as_filetime()}")
- set_leap_seconds_enabled(False)
- print(f"Current FILETIME: {now_as_filetime()}")
- set_leap_seconds_enabled(True)
- print(f"Current FILETIME: {now_as_filetime()}")
- set_leap_seconds_enabled(False)
- print(f"Current FILETIME: {now_as_filetime()}")
-
-
-trio.run(main)
diff --git a/notes-to-self/windows-vm-notes.txt b/notes-to-self/windows-vm-notes.txt
deleted file mode 100644
index 804069f108..0000000000
--- a/notes-to-self/windows-vm-notes.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-Have a VM in virtualbox
-
-activate winenv here, or use py, py -m pip, etc.; regular python is
-not in the path
-
-virtualbox is set to map my home dir to \\vboxsrv\njs, which can be
-mapped to a drive with: net use x: \\vboxsrv\njs
-
-if switching back and forth between windows and linux in the same
-directory and using the same version of python, .pyc files are a problem.
- find -name __pycache__ | xargs rm -rf
-export PYTHONDONTWRITEBYTECODE=1
-
-if things freeze, control-C doesn't seem reliable... possibly this is
-a bug in my code :-(. but can get to task manager via vbox menu Input
-Keyboard -> Insert ctrl-alt-del.
From c87f318ee142d3fe80ff600df0c10b4659864858 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Sun, 20 Oct 2024 14:01:59 +0200
Subject: [PATCH 02/66] redirect all references to notes-to-self to static url
---
docs/source/reference-io.rst | 3 ++-
pyproject.toml | 2 +-
src/trio/_core/_io_windows.py | 3 ++-
src/trio/_highlevel_open_tcp_listeners.py | 2 +-
4 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/docs/source/reference-io.rst b/docs/source/reference-io.rst
index e8a967bf17..0d8c8cab1b 100644
--- a/docs/source/reference-io.rst
+++ b/docs/source/reference-io.rst
@@ -555,7 +555,8 @@ there are 1,000,000 µs in a second. Note that all the numbers here are
going to be rough orders of magnitude to give you a sense of scale; if
you need precise numbers for your environment, measure!)
-.. file.read benchmark is notes-to-self/file-read-latency.py
+.. file.read benchmark is
+ https://github.com/python-trio/trio/blob/v0.27.0/notes-to-self/file-read-latency.py
.. Numbers for spinning disks and SSDs are from taking a few random
recent reviews from http://www.storagereview.com/best_drives and
looking at their "4K Write Latency" test results for "Average MS"
diff --git a/pyproject.toml b/pyproject.toml
index f773d4c703..b0a134dd56 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -92,7 +92,7 @@ fix = true
# The directories to consider when resolving first vs. third-party imports.
# Does not control what files to include/exclude!
-src = ["src/trio", "notes-to-self"]
+src = ["src/trio"]
include = ["*.py", "*.pyi", "**/pyproject.toml"]
diff --git a/src/trio/_core/_io_windows.py b/src/trio/_core/_io_windows.py
index 0bc3b33378..b2bfc09a86 100644
--- a/src/trio/_core/_io_windows.py
+++ b/src/trio/_core/_io_windows.py
@@ -152,7 +152,8 @@
#
# Unfortunately, the Windows kernel seems to have bugs if you try to issue
# multiple simultaneous IOCTL_AFD_POLL operations on the same socket (see
-# notes-to-self/afd-lab.py). So if a user calls wait_readable and
+# https://github.com/python-trio/trio/blob/v0.27.0/notes-to-self/afd-lab.py).
+# So if a user calls wait_readable and
# wait_writable at the same time, we have to combine those into a single
# IOCTL_AFD_POLL. This means we can't just use the wait_overlapped machinery.
# Instead we have some dedicated code to handle these operations, and a
diff --git a/src/trio/_highlevel_open_tcp_listeners.py b/src/trio/_highlevel_open_tcp_listeners.py
index 2e71ca5543..92f29128b3 100644
--- a/src/trio/_highlevel_open_tcp_listeners.py
+++ b/src/trio/_highlevel_open_tcp_listeners.py
@@ -42,7 +42,7 @@
# backlog just causes it to be silently truncated to the configured maximum,
# so this is unnecessary -- we can just pass in "infinity" and get the maximum
# that way. (Verified on Windows, Linux, macOS using
-# notes-to-self/measure-listen-backlog.py)
+# https://github.com/python-trio/trio/blob/v0.27.0/notes-to-self/measure-listen-backlog.py
def _compute_backlog(backlog: int | None) -> int:
# Many systems (Linux, BSDs, ...) store the backlog in a uint16 and are
# missing overflow protection, so we apply our own overflow protection.
From 64035c288bb1c6f6f80bee61369030dcf8a0a08a Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Mon, 25 Nov 2024 12:43:15 +0100
Subject: [PATCH 03/66] refer to wiki instead of tag
---
docs/source/reference-io.rst | 2 +-
src/trio/_core/_io_windows.py | 2 +-
src/trio/_highlevel_open_tcp_listeners.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/source/reference-io.rst b/docs/source/reference-io.rst
index 0d8c8cab1b..665f62dd0b 100644
--- a/docs/source/reference-io.rst
+++ b/docs/source/reference-io.rst
@@ -556,7 +556,7 @@ going to be rough orders of magnitude to give you a sense of scale; if
you need precise numbers for your environment, measure!)
.. file.read benchmark is
- https://github.com/python-trio/trio/blob/v0.27.0/notes-to-self/file-read-latency.py
+ https://github.com/python-trio/trio/wiki/notes-to-self#file-read-latencypy
.. Numbers for spinning disks and SSDs are from taking a few random
recent reviews from http://www.storagereview.com/best_drives and
looking at their "4K Write Latency" test results for "Average MS"
diff --git a/src/trio/_core/_io_windows.py b/src/trio/_core/_io_windows.py
index b2bfc09a86..b39ee63197 100644
--- a/src/trio/_core/_io_windows.py
+++ b/src/trio/_core/_io_windows.py
@@ -152,7 +152,7 @@
#
# Unfortunately, the Windows kernel seems to have bugs if you try to issue
# multiple simultaneous IOCTL_AFD_POLL operations on the same socket (see
-# https://github.com/python-trio/trio/blob/v0.27.0/notes-to-self/afd-lab.py).
+# https://github.com/python-trio/trio/wiki/notes-to-self#afd-labpy).
# So if a user calls wait_readable and
# wait_writable at the same time, we have to combine those into a single
# IOCTL_AFD_POLL. This means we can't just use the wait_overlapped machinery.
diff --git a/src/trio/_highlevel_open_tcp_listeners.py b/src/trio/_highlevel_open_tcp_listeners.py
index 92f29128b3..023b2b240f 100644
--- a/src/trio/_highlevel_open_tcp_listeners.py
+++ b/src/trio/_highlevel_open_tcp_listeners.py
@@ -42,7 +42,7 @@
# backlog just causes it to be silently truncated to the configured maximum,
# so this is unnecessary -- we can just pass in "infinity" and get the maximum
# that way. (Verified on Windows, Linux, macOS using
-# https://github.com/python-trio/trio/blob/v0.27.0/notes-to-self/measure-listen-backlog.py
+# https://github.com/python-trio/trio/wiki/notes-to-self#measure-listen-backlogpy
def _compute_backlog(backlog: int | None) -> int:
# Many systems (Linux, BSDs, ...) store the backlog in a uint16 and are
# missing overflow protection, so we apply our own overflow protection.
From ea973115c39b02e4ab867b15eb13f12287282718 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Mon, 25 Nov 2024 12:44:34 +0100
Subject: [PATCH 04/66] don't need to config ruff to ignore notes-to-self
anymore
---
pyproject.toml | 2 --
1 file changed, 2 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 8c38ed90ed..3d23eb7380 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -162,8 +162,6 @@ extend-ignore = [
'src/trio/_abc.py' = ['A005']
'src/trio/_socket.py' = ['A005']
'src/trio/_ssl.py' = ['A005']
-# Don't check annotations in notes-to-self
-'notes-to-self/*.py' = ['ANN001', 'ANN002', 'ANN003', 'ANN201', 'ANN202', 'ANN204']
[tool.ruff.lint.isort]
combine-as-imports = true
From dfcb6908be51c493dffda326c0ca815d722d0e0d Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Mon, 9 Dec 2024 13:13:11 +0100
Subject: [PATCH 05/66] add notes-to-self/ to gitignore
---
.gitignore | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.gitignore b/.gitignore
index 388f2dfbda..cad76cb460 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+# In case somebody wants to restore the directory for local testing
+notes-to-self/
+
# Project-specific generated files
docs/build/
From 8a3ebf8c44802562dbed3c43cce1b2cf95bd2af1 Mon Sep 17 00:00:00 2001
From: A5rocks
Date: Tue, 10 Dec 2024 13:22:54 +0900
Subject: [PATCH 06/66] Get rid of using `__` for positional only arguments
---
src/trio/_socket.py | 110 ++++++++++++++++++++++++--------------------
1 file changed, 60 insertions(+), 50 deletions(-)
diff --git a/src/trio/_socket.py b/src/trio/_socket.py
index d92add290c..612be7007d 100644
--- a/src/trio/_socket.py
+++ b/src/trio/_socket.py
@@ -1,6 +1,3 @@
-# ruff: noqa: PYI063 # Several cases throughout file where
-# argument names with __ used because of typeshed, see comment for recv in _SocketType
-
from __future__ import annotations
import os
@@ -677,11 +674,12 @@ async def accept(self) -> tuple[SocketType, AddressFormat]:
async def connect(self, address: AddressFormat) -> None:
raise NotImplementedError
- def recv(__self, __buflen: int, __flags: int = 0) -> Awaitable[bytes]:
+ def recv(self, buflen: int, flags: int = 0, /) -> Awaitable[bytes]:
raise NotImplementedError
def recv_into(
- __self,
+ self,
+ /,
buffer: Buffer,
nbytes: int = 0,
flags: int = 0,
@@ -690,15 +688,17 @@ def recv_into(
# return type of socket.socket.recvfrom in typeshed is tuple[bytes, Any]
def recvfrom(
- __self,
- __bufsize: int,
- __flags: int = 0,
+ self,
+ bufsize: int,
+ flags: int = 0,
+ /,
) -> Awaitable[tuple[bytes, AddressFormat]]:
raise NotImplementedError
# return type of socket.socket.recvfrom_into in typeshed is tuple[bytes, Any]
def recvfrom_into(
- __self,
+ self,
+ /,
buffer: Buffer,
nbytes: int = 0,
flags: int = 0,
@@ -710,10 +710,11 @@ def recvfrom_into(
):
def recvmsg(
- __self,
- __bufsize: int,
- __ancbufsize: int = 0,
- __flags: int = 0,
+ self,
+ bufsize: int,
+ ancbufsize: int = 0,
+ flags: int = 0,
+ /,
) -> Awaitable[tuple[bytes, list[tuple[int, int, bytes]], int, object]]:
raise NotImplementedError
@@ -722,29 +723,32 @@ def recvmsg(
):
def recvmsg_into(
- __self,
- __buffers: Iterable[Buffer],
- __ancbufsize: int = 0,
- __flags: int = 0,
+ self,
+ buffers: Iterable[Buffer],
+ ancbufsize: int = 0,
+ flags: int = 0,
+ /,
) -> Awaitable[tuple[int, list[tuple[int, int, bytes]], int, object]]:
raise NotImplementedError
- def send(__self, __bytes: Buffer, __flags: int = 0) -> Awaitable[int]:
+ def send(self, bytes: Buffer, flags: int = 0, /) -> Awaitable[int]:
raise NotImplementedError
@overload
async def sendto(
self,
- __data: Buffer,
- __address: tuple[object, ...] | str | Buffer,
+ data: Buffer,
+ address: tuple[object, ...] | str | Buffer,
+ /,
) -> int: ...
@overload
async def sendto(
self,
- __data: Buffer,
- __flags: int,
- __address: tuple[object, ...] | str | Buffer,
+ data: Buffer,
+ flags: int,
+ address: tuple[object, ...] | str | Buffer,
+ /,
) -> int: ...
async def sendto(self, *args: object) -> int:
@@ -757,10 +761,11 @@ async def sendto(self, *args: object) -> int:
@_wraps(_stdlib_socket.socket.sendmsg, assigned=(), updated=())
async def sendmsg(
self,
- __buffers: Iterable[Buffer],
- __ancdata: Iterable[tuple[int, int, Buffer]] = (),
- __flags: int = 0,
- __address: AddressFormat | None = None,
+ buffers: Iterable[Buffer],
+ ancdata: Iterable[tuple[int, int, Buffer]] = (),
+ flags: int = 0,
+ address: AddressFormat | None = None,
+ /,
) -> int:
raise NotImplementedError
@@ -1118,11 +1123,8 @@ async def connect(self, address: AddressFormat) -> None:
# complain about AmbiguousType
if TYPE_CHECKING:
- def recv(__self, __buflen: int, __flags: int = 0) -> Awaitable[bytes]: ...
+ def recv(self, buflen: int, flags: int = 0, /) -> Awaitable[bytes]: ...
- # _make_simple_sock_method_wrapper is typed, so this checks that the above is correct
- # this requires that we refrain from using `/` to specify pos-only
- # args, or mypy thinks the signature differs from typeshed.
recv = _make_simple_sock_method_wrapper(
_stdlib_socket.socket.recv,
_core.wait_readable,
@@ -1135,7 +1137,8 @@ def recv(__self, __buflen: int, __flags: int = 0) -> Awaitable[bytes]: ...
if TYPE_CHECKING:
def recv_into(
- __self,
+ self,
+ /,
buffer: Buffer,
nbytes: int = 0,
flags: int = 0,
@@ -1153,9 +1156,10 @@ def recv_into(
if TYPE_CHECKING:
# return type of socket.socket.recvfrom in typeshed is tuple[bytes, Any]
def recvfrom(
- __self,
- __bufsize: int,
- __flags: int = 0,
+ self,
+ bufsize: int,
+ flags: int = 0,
+ /,
) -> Awaitable[tuple[bytes, AddressFormat]]: ...
recvfrom = _make_simple_sock_method_wrapper(
@@ -1170,7 +1174,8 @@ def recvfrom(
if TYPE_CHECKING:
# return type of socket.socket.recvfrom_into in typeshed is tuple[bytes, Any]
def recvfrom_into(
- __self,
+ self,
+ /,
buffer: Buffer,
nbytes: int = 0,
flags: int = 0,
@@ -1191,10 +1196,11 @@ def recvfrom_into(
if TYPE_CHECKING:
def recvmsg(
- __self,
- __bufsize: int,
- __ancbufsize: int = 0,
- __flags: int = 0,
+ self,
+ bufsize: int,
+ ancbufsize: int = 0,
+ flags: int = 0,
+ /,
) -> Awaitable[tuple[bytes, list[tuple[int, int, bytes]], int, object]]: ...
recvmsg = _make_simple_sock_method_wrapper(
@@ -1213,10 +1219,11 @@ def recvmsg(
if TYPE_CHECKING:
def recvmsg_into(
- __self,
- __buffers: Iterable[Buffer],
- __ancbufsize: int = 0,
- __flags: int = 0,
+ self,
+ buffers: Iterable[Buffer],
+ ancbufsize: int = 0,
+ flags: int = 0,
+ /,
) -> Awaitable[tuple[int, list[tuple[int, int, bytes]], int, object]]: ...
recvmsg_into = _make_simple_sock_method_wrapper(
@@ -1231,7 +1238,7 @@ def recvmsg_into(
if TYPE_CHECKING:
- def send(__self, __bytes: Buffer, __flags: int = 0) -> Awaitable[int]: ...
+ def send(self, bytes: Buffer, flags: int = 0, /) -> Awaitable[int]: ...
send = _make_simple_sock_method_wrapper(
_stdlib_socket.socket.send,
@@ -1245,16 +1252,18 @@ def send(__self, __bytes: Buffer, __flags: int = 0) -> Awaitable[int]: ...
@overload
async def sendto(
self,
- __data: Buffer,
- __address: tuple[object, ...] | str | Buffer,
+ data: Buffer,
+ address: tuple[object, ...] | str | Buffer,
+ /,
) -> int: ...
@overload
async def sendto(
self,
- __data: Buffer,
- __flags: int,
- __address: tuple[object, ...] | str | Buffer,
+ data: Buffer,
+ flags: int,
+ address: tuple[object, ...] | str | Buffer,
+ /,
) -> int: ...
@_wraps(_stdlib_socket.socket.sendto, assigned=(), updated=())
@@ -1287,6 +1296,7 @@ async def sendmsg(
ancdata: Iterable[tuple[int, int, Buffer]] = (),
flags: int = 0,
address: AddressFormat | None = None,
+ /,
) -> int:
"""Similar to :meth:`socket.socket.sendmsg`, but async.
From 3637b3d1fc30c31427259be25da21fe45dc58313 Mon Sep 17 00:00:00 2001
From: A5rocks
Date: Tue, 10 Dec 2024 13:29:46 +0900
Subject: [PATCH 07/66] Add a changelog in case someone depends on the old
argument names
---
newsfragments/3094.misc.rst | 1 +
1 file changed, 1 insertion(+)
create mode 100644 newsfragments/3094.misc.rst
diff --git a/newsfragments/3094.misc.rst b/newsfragments/3094.misc.rst
new file mode 100644
index 0000000000..c35b7802e8
--- /dev/null
+++ b/newsfragments/3094.misc.rst
@@ -0,0 +1 @@
+Switch to using PEP570 for positional-only arguments for `~trio.socket.SocketType`'s methods.
From 1fa8e53a05464896dbe69c82d190eac885d91e1a Mon Sep 17 00:00:00 2001
From: A5rocks
Date: Tue, 10 Dec 2024 15:50:48 +0900
Subject: [PATCH 08/66] Remove unnecessary positional-only on `self`
---
src/trio/_socket.py | 28 ++++++++++------------------
1 file changed, 10 insertions(+), 18 deletions(-)
diff --git a/src/trio/_socket.py b/src/trio/_socket.py
index 612be7007d..4dde512985 100644
--- a/src/trio/_socket.py
+++ b/src/trio/_socket.py
@@ -564,14 +564,13 @@ def getsockname(self) -> AddressFormat:
raise NotImplementedError
@overload
- def getsockopt(self, /, level: int, optname: int) -> int: ...
+ def getsockopt(self, level: int, optname: int) -> int: ...
@overload
- def getsockopt(self, /, level: int, optname: int, buflen: int) -> bytes: ...
+ def getsockopt(self, level: int, optname: int, buflen: int) -> bytes: ...
def getsockopt(
self,
- /,
level: int,
optname: int,
buflen: int | None = None,
@@ -579,12 +578,11 @@ def getsockopt(
raise NotImplementedError
@overload
- def setsockopt(self, /, level: int, optname: int, value: int | Buffer) -> None: ...
+ def setsockopt(self, level: int, optname: int, value: int | Buffer) -> None: ...
@overload
def setsockopt(
self,
- /,
level: int,
optname: int,
value: None,
@@ -593,7 +591,6 @@ def setsockopt(
def setsockopt(
self,
- /,
level: int,
optname: int,
value: int | Buffer | None,
@@ -601,7 +598,7 @@ def setsockopt(
) -> None:
raise NotImplementedError
- def listen(self, /, backlog: int = min(_stdlib_socket.SOMAXCONN, 128)) -> None:
+ def listen(self, backlog: int = min(_stdlib_socket.SOMAXCONN, 128)) -> None:
raise NotImplementedError
def get_inheritable(self) -> bool:
@@ -614,7 +611,7 @@ def set_inheritable(self, inheritable: bool) -> None:
not TYPE_CHECKING and hasattr(_stdlib_socket.socket, "share")
):
- def share(self, /, process_id: int) -> bytes:
+ def share(self, process_id: int) -> bytes:
raise NotImplementedError
def __enter__(self) -> Self:
@@ -679,7 +676,6 @@ def recv(self, buflen: int, flags: int = 0, /) -> Awaitable[bytes]:
def recv_into(
self,
- /,
buffer: Buffer,
nbytes: int = 0,
flags: int = 0,
@@ -698,7 +694,6 @@ def recvfrom(
# return type of socket.socket.recvfrom_into in typeshed is tuple[bytes, Any]
def recvfrom_into(
self,
- /,
buffer: Buffer,
nbytes: int = 0,
flags: int = 0,
@@ -815,14 +810,13 @@ def getsockname(self) -> AddressFormat:
return self._sock.getsockname()
@overload
- def getsockopt(self, /, level: int, optname: int) -> int: ...
+ def getsockopt(self, level: int, optname: int) -> int: ...
@overload
- def getsockopt(self, /, level: int, optname: int, buflen: int) -> bytes: ...
+ def getsockopt(self, level: int, optname: int, buflen: int) -> bytes: ...
def getsockopt(
self,
- /,
level: int,
optname: int,
buflen: int | None = None,
@@ -832,12 +826,11 @@ def getsockopt(
return self._sock.getsockopt(level, optname, buflen)
@overload
- def setsockopt(self, /, level: int, optname: int, value: int | Buffer) -> None: ...
+ def setsockopt(self, level: int, optname: int, value: int | Buffer) -> None: ...
@overload
def setsockopt(
self,
- /,
level: int,
optname: int,
value: None,
@@ -846,7 +839,6 @@ def setsockopt(
def setsockopt(
self,
- /,
level: int,
optname: int,
value: int | Buffer | None,
@@ -867,7 +859,7 @@ def setsockopt(
# four parameters.
return self._sock.setsockopt(level, optname, value, optlen)
- def listen(self, /, backlog: int = min(_stdlib_socket.SOMAXCONN, 128)) -> None:
+ def listen(self, backlog: int = min(_stdlib_socket.SOMAXCONN, 128)) -> None:
return self._sock.listen(backlog)
def get_inheritable(self) -> bool:
@@ -880,7 +872,7 @@ def set_inheritable(self, inheritable: bool) -> None:
not TYPE_CHECKING and hasattr(_stdlib_socket.socket, "share")
):
- def share(self, /, process_id: int) -> bytes:
+ def share(self, process_id: int) -> bytes:
return self._sock.share(process_id)
def __enter__(self) -> Self:
From 51334ffd8c414f53c4b8f0250ef35da8dedc65f3 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 16 Dec 2024 23:33:11 +0000
Subject: [PATCH 09/66] [pre-commit.ci] pre-commit autoupdate
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.8.2 → v0.8.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.2...v0.8.3)
---
.pre-commit-config.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e06d1c7ac5..415a6d2364 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -22,7 +22,7 @@ repos:
hooks:
- id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.8.2
+ rev: v0.8.3
hooks:
- id: ruff
types: [file]
From 17af03b4c3d9efcd65daa0e369c470cf27f5d825 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Tue, 17 Dec 2024 10:59:42 +0100
Subject: [PATCH 10/66] remove erronously configured codecov flags
---
.github/workflows/ci.yml | 4 ----
1 file changed, 4 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f302cbaa2e..ca13c6e33d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -87,7 +87,6 @@ jobs:
directory: empty
token: 87cefb17-c44b-4f2f-8b30-1fff5769ce46
name: Windows (${{ matrix.python }}, ${{ matrix.arch }}${{ matrix.extra_name }})
- flags: Windows,${{ matrix.python }}
Ubuntu:
name: 'Ubuntu (${{ matrix.python }}${{ matrix.extra_name }})'
@@ -137,7 +136,6 @@ jobs:
directory: empty
token: 87cefb17-c44b-4f2f-8b30-1fff5769ce46
name: Ubuntu (${{ matrix.python }}${{ matrix.extra_name }})
- flags: Ubuntu,${{ matrix.python }}
macOS:
name: 'macOS (${{ matrix.python }})'
@@ -173,7 +171,6 @@ jobs:
directory: empty
token: 87cefb17-c44b-4f2f-8b30-1fff5769ce46
name: macOS (${{ matrix.python }})
- flags: macOS,${{ matrix.python }}
# run CI on a musl linux
Alpine:
@@ -199,7 +196,6 @@ jobs:
directory: empty
token: 87cefb17-c44b-4f2f-8b30-1fff5769ce46
name: Alpine
- flags: Alpine,3.12
Cython:
name: "Cython"
From 02a84063923f4700cb7f68b487412ab4a456d9bb Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Tue, 17 Dec 2024 11:10:51 +0100
Subject: [PATCH 11/66] add .codecov.yml
---
.codecov.yml | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 .codecov.yml
diff --git a/.codecov.yml b/.codecov.yml
new file mode 100644
index 0000000000..43cfc24209
--- /dev/null
+++ b/.codecov.yml
@@ -0,0 +1,25 @@
+# -- repository yaml --
+
+# Explicitly wait for all jobs to finish, as wait_for_ci prematurely triggers.
+# See https://github.com/python-trio/trio/issues/2689
+codecov:
+ notify:
+ # This number needs to be changed whenever the number of runs in CI is changed.
+ # Another option is codecov-cli: https://github.com/codecov/codecov-cli#send-notifications
+ after_n_builds: 78
+ wait_for_ci: false
+ notify_error: true # if uploads fail, replace cov comment with a comment with errors.
+ require_ci_to_pass: false
+
+# only post PR comment if coverage changes
+comment:
+ require_changes: true
+
+coverage:
+ # required range
+ range: 99.6..100
+ status:
+ # require patches to be 100%
+ patch:
+ default:
+ target: 100%
From d418dc2d61fd21003536f082edeec34692fb4bf7 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Tue, 17 Dec 2024 11:57:03 +0100
Subject: [PATCH 12/66] move token to yaml file, set fail_ci_if_error
---
.codecov.yml | 4 ++++
.github/workflows/ci.yml | 9 +++++----
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/.codecov.yml b/.codecov.yml
index 43cfc24209..350707e08a 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -11,6 +11,10 @@ codecov:
notify_error: true # if uploads fail, replace cov comment with a comment with errors.
require_ci_to_pass: false
+ # Publicly exposing the token has some small risks from mistakes or malicious actors.
+ # See https://docs.codecov.com/docs/codecov-tokens for correctly configuring it.
+ token: 87cefb17-c44b-4f2f-8b30-1fff5769ce46
+
# only post PR comment if coverage changes
comment:
require_changes: true
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ca13c6e33d..40425d5c7f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -85,8 +85,9 @@ jobs:
uses: codecov/codecov-action@v3
with:
directory: empty
- token: 87cefb17-c44b-4f2f-8b30-1fff5769ce46
name: Windows (${{ matrix.python }}, ${{ matrix.arch }}${{ matrix.extra_name }})
+ # this option cannot be set in .codecov.yml
+ fail_ci_if_error: true
Ubuntu:
name: 'Ubuntu (${{ matrix.python }}${{ matrix.extra_name }})'
@@ -134,8 +135,8 @@ jobs:
uses: codecov/codecov-action@v3
with:
directory: empty
- token: 87cefb17-c44b-4f2f-8b30-1fff5769ce46
name: Ubuntu (${{ matrix.python }}${{ matrix.extra_name }})
+ fail_ci_if_error: true
macOS:
name: 'macOS (${{ matrix.python }})'
@@ -169,8 +170,8 @@ jobs:
uses: codecov/codecov-action@v3
with:
directory: empty
- token: 87cefb17-c44b-4f2f-8b30-1fff5769ce46
name: macOS (${{ matrix.python }})
+ fail_ci_if_error: true
# run CI on a musl linux
Alpine:
@@ -194,8 +195,8 @@ jobs:
uses: codecov/codecov-action@v3
with:
directory: empty
- token: 87cefb17-c44b-4f2f-8b30-1fff5769ce46
name: Alpine
+ fail_ci_if_error: true
Cython:
name: "Cython"
From decfb73fdbba72e7cc704aa089965788f14b6b36 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Tue, 17 Dec 2024 14:44:20 +0100
Subject: [PATCH 13/66] restore flags, make alpine python flag dynamic
---
.github/workflows/ci.yml | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 40425d5c7f..56c7e4c11a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -86,6 +86,9 @@ jobs:
with:
directory: empty
name: Windows (${{ matrix.python }}, ${{ matrix.arch }}${{ matrix.extra_name }})
+ # multiple flags is marked as an error in codecov UI, but is actually fine
+ # https://github.com/codecov/feedback/issues/567
+ flags: Windows,${{ matrix.python }}
# this option cannot be set in .codecov.yml
fail_ci_if_error: true
@@ -136,6 +139,7 @@ jobs:
with:
directory: empty
name: Ubuntu (${{ matrix.python }}${{ matrix.extra_name }})
+ flags: Ubuntu,${{ matrix.python }}
fail_ci_if_error: true
macOS:
@@ -171,6 +175,7 @@ jobs:
with:
directory: empty
name: macOS (${{ matrix.python }})
+ flags: macOS,${{ matrix.python }}
fail_ci_if_error: true
# run CI on a musl linux
@@ -191,11 +196,15 @@ jobs:
run: python -m venv .venv
- name: Run tests
run: source .venv/bin/activate && ./ci.sh
+ - name: Get Python version for codecov flag
+ id: get-version
+ run: echo "version=$(python -V | cut -d' ' -f2 | cut -d'.' -f1,2)" > $GITHUB_OUTPUT
- if: always()
uses: codecov/codecov-action@v3
with:
directory: empty
name: Alpine
+ flags: Alpine,${{ steps.get-version.outputs.version }}
fail_ci_if_error: true
Cython:
From 2fd7fcfacc361c1e5b331a2a31e5d6aec959dbd0 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Tue, 17 Dec 2024 14:55:00 +0100
Subject: [PATCH 14/66] skip codecov if check-formatting is set
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 56c7e4c11a..93f7be9e32 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -134,7 +134,7 @@ jobs:
env:
CHECK_FORMATTING: '${{ matrix.check_formatting }}'
NO_TEST_REQUIREMENTS: '${{ matrix.no_test_requirements }}'
- - if: always()
+ - if: ${{ always() && matrix.check_formatting != '1' }}
uses: codecov/codecov-action@v3
with:
directory: empty
From a7a2ff9c6e6ea2fc0168e0ada2174bf8fd065f77 Mon Sep 17 00:00:00 2001
From: John Litborn <11260241+jakkdl@users.noreply.github.com>
Date: Tue, 17 Dec 2024 19:58:43 +0100
Subject: [PATCH 15/66] Apply suggestions from code review
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко)
---
.codecov.yml | 2 +-
.github/workflows/ci.yml | 6 ++++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/.codecov.yml b/.codecov.yml
index 350707e08a..c4b9b096ca 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -6,7 +6,7 @@ codecov:
notify:
# This number needs to be changed whenever the number of runs in CI is changed.
# Another option is codecov-cli: https://github.com/codecov/codecov-cli#send-notifications
- after_n_builds: 78
+ after_n_builds: 27
wait_for_ci: false
notify_error: true # if uploads fail, replace cov comment with a comment with errors.
require_ci_to_pass: false
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 93f7be9e32..cb00ff85bb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -134,7 +134,9 @@ jobs:
env:
CHECK_FORMATTING: '${{ matrix.check_formatting }}'
NO_TEST_REQUIREMENTS: '${{ matrix.no_test_requirements }}'
- - if: ${{ always() && matrix.check_formatting != '1' }}
+ - if: >-
+ always()
+ && matrix.check_formatting != '1'
uses: codecov/codecov-action@v3
with:
directory: empty
@@ -198,7 +200,7 @@ jobs:
run: source .venv/bin/activate && ./ci.sh
- name: Get Python version for codecov flag
id: get-version
- run: echo "version=$(python -V | cut -d' ' -f2 | cut -d'.' -f1,2)" > $GITHUB_OUTPUT
+ run: echo "version=$(python -V | cut -d' ' -f2 | cut -d'.' -f1,2)" >> "${GITHUB_OUTPUT}"
- if: always()
uses: codecov/codecov-action@v3
with:
From 1bd004a97ac05f63f60b8747f354fb05a630cc27 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Wed, 18 Dec 2024 11:31:25 +0100
Subject: [PATCH 16/66] xfail macos 3.13.1 temporarily
---
src/trio/_tests/test_highlevel_socket.py | 6 ++++++
src/trio/_tests/test_socket.py | 6 ++++++
2 files changed, 12 insertions(+)
diff --git a/src/trio/_tests/test_highlevel_socket.py b/src/trio/_tests/test_highlevel_socket.py
index 7e9d352450..a03efb0180 100644
--- a/src/trio/_tests/test_highlevel_socket.py
+++ b/src/trio/_tests/test_highlevel_socket.py
@@ -20,6 +20,12 @@
from collections.abc import Sequence
+@pytest.mark.xfail(
+ sys.platform == "darwin" and sys.version_info[:3] == (3, 13, 1),
+ reason="TODO: This started failing in CI after 3.13.1",
+ raises=OSError,
+ strict=True,
+)
async def test_SocketStream_basics() -> None:
# stdlib socket bad (even if connected)
stdlib_a, stdlib_b = stdlib_socket.socketpair()
diff --git a/src/trio/_tests/test_socket.py b/src/trio/_tests/test_socket.py
index 8226d2e385..ebe94a6ca6 100644
--- a/src/trio/_tests/test_socket.py
+++ b/src/trio/_tests/test_socket.py
@@ -458,6 +458,12 @@ async def test_SocketType_basics() -> None:
sock.close()
+@pytest.mark.xfail(
+ sys.platform == "darwin" and sys.version_info[:3] == (3, 13, 1),
+ reason="TODO: This started failing in CI after 3.13.1",
+ raises=OSError,
+ strict=True,
+)
async def test_SocketType_setsockopt() -> None:
sock = tsocket.socket()
with sock as _:
From 13359c027dc23b8e89c83e2529aab1dd859dd7f6 Mon Sep 17 00:00:00 2001
From: Sviatoslav Sydorenko
Date: Thu, 19 Dec 2024 02:38:27 +0100
Subject: [PATCH 17/66] =?UTF-8?q?=F0=9F=92=84=20Make=20coveragepy=20HTML?=
=?UTF-8?q?=20output=20display=20contexts?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pyproject.toml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/pyproject.toml b/pyproject.toml
index 0785651e53..076d317079 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -268,6 +268,10 @@ directory = "misc"
name = "Miscellaneous internal changes"
showcontent = true
+[tool.coverage.html]
+show_contexts = true
+skip_covered = false
+
[tool.coverage.run]
branch = true
source_pkgs = ["trio"]
From 92e39d19178e00794cd79ef0ae4d320f06f3eeee Mon Sep 17 00:00:00 2001
From: Sviatoslav Sydorenko
Date: Thu, 19 Dec 2024 02:39:47 +0100
Subject: [PATCH 18/66] =?UTF-8?q?=F0=9F=A7=AA=20Record=20relative=20paths?=
=?UTF-8?q?=20in=20the=20coveragepy=20DB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pyproject.toml | 1 +
1 file changed, 1 insertion(+)
diff --git a/pyproject.toml b/pyproject.toml
index 076d317079..b4efcae953 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -286,6 +286,7 @@ omit = [
# The test suite spawns subprocesses to test some stuff, so make sure
# this doesn't corrupt the coverage files
parallel = true
+relative_files = true
[tool.coverage.report]
precision = 1
From e70d040825b37ab0b0690afcc6b12cc80325ec27 Mon Sep 17 00:00:00 2001
From: Sviatoslav Sydorenko
Date: Thu, 19 Dec 2024 02:40:43 +0100
Subject: [PATCH 19/66] =?UTF-8?q?=F0=9F=A7=AA=20Hide=20irrelevant=20covera?=
=?UTF-8?q?ge=20from=20term=20output?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pyproject.toml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/pyproject.toml b/pyproject.toml
index b4efcae953..c1900dd0b9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -291,6 +291,8 @@ relative_files = true
[tool.coverage.report]
precision = 1
skip_covered = true
+skip_empty = true
+show_missing = true
exclude_lines = [
"pragma: no cover",
"abc.abstractmethod",
From 9b23d4720886e652431d22407ac9da5bb051d9d6 Mon Sep 17 00:00:00 2001
From: Sviatoslav Sydorenko
Date: Thu, 19 Dec 2024 02:41:35 +0100
Subject: [PATCH 20/66] =?UTF-8?q?=F0=9F=A7=AA=20Exclude=20xfailing=20tests?=
=?UTF-8?q?=20from=20coverage?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pyproject.toml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/pyproject.toml b/pyproject.toml
index c1900dd0b9..a7444d00ab 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -303,6 +303,9 @@ exclude_lines = [
'class .*\bProtocol\b.*\):',
"raise NotImplementedError",
]
+exclude_also = [
+ '^\s*@pytest\.mark\.xfail',
+]
partial_branches = [
"pragma: no branch",
"if not TYPE_CHECKING:",
From e07911cb477eb5479c4e48e611cb71a55ed5837f Mon Sep 17 00:00:00 2001
From: Sviatoslav Sydorenko
Date: Thu, 19 Dec 2024 02:42:24 +0100
Subject: [PATCH 21/66] =?UTF-8?q?=F0=9F=A7=AA=20Collect=20entire=20repo=20?=
=?UTF-8?q?coverage?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pyproject.toml | 1 +
1 file changed, 1 insertion(+)
diff --git a/pyproject.toml b/pyproject.toml
index a7444d00ab..766757321e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -287,6 +287,7 @@ omit = [
# this doesn't corrupt the coverage files
parallel = true
relative_files = true
+source = ["."]
[tool.coverage.report]
precision = 1
From aeaa980b385a8e862a786229d7d045641ff40e0f Mon Sep 17 00:00:00 2001
From: Sviatoslav Sydorenko
Date: Thu, 19 Dec 2024 02:42:51 +0100
Subject: [PATCH 22/66] =?UTF-8?q?=F0=9F=A7=AA=20Map=20site-packages=20cove?=
=?UTF-8?q?rage=20to=20actual=20source?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pyproject.toml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/pyproject.toml b/pyproject.toml
index 766757321e..f0e1609876 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -272,6 +272,16 @@ showcontent = true
show_contexts = true
skip_covered = false
+[tool.coverage.paths]
+_site-packages-to-src-mapping = [
+ "src",
+ "*/src",
+ '*\src',
+ "*/lib/pypy*/site-packages",
+ "*/lib/python*/site-packages",
+ '*\Lib\site-packages',
+]
+
[tool.coverage.run]
branch = true
source_pkgs = ["trio"]
From 2aa577107075e4d664faf02c6ab5baeef7aff58c Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Thu, 19 Dec 2024 07:47:52 +0000
Subject: [PATCH 23/66] test waitid on linux even with pidfd support
---
src/trio/_tests/test_subprocess.py | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/src/trio/_tests/test_subprocess.py b/src/trio/_tests/test_subprocess.py
index 88623a4304..a23d47bd32 100644
--- a/src/trio/_tests/test_subprocess.py
+++ b/src/trio/_tests/test_subprocess.py
@@ -16,6 +16,7 @@
Any,
NoReturn,
)
+from unittest import mock
import pytest
@@ -146,6 +147,26 @@ async def test_basic(background_process: BackgroundProcessType) -> None:
)
+@background_process_param
+async def test_basic_no_pidfd(background_process: BackgroundProcessType) -> None:
+ with mock.patch("trio._subprocess.can_try_pidfd_open", new=False):
+ async with background_process(EXIT_TRUE) as proc:
+ assert proc._pidfd is None
+ await proc.wait()
+ assert isinstance(proc, Process)
+ assert proc._pidfd is None
+ assert proc.returncode == 0
+ assert repr(proc) == f""
+
+ async with background_process(EXIT_FALSE) as proc:
+ await proc.wait()
+ assert proc.returncode == 1
+ assert repr(proc) == "".format(
+ EXIT_FALSE,
+ "exited with status 1",
+ )
+
+
@background_process_param
async def test_auto_update_returncode(
background_process: BackgroundProcessType,
From 8501b0c5d94890474fa50d2707b1526d3a1a577b Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Thu, 19 Dec 2024 07:57:39 +0000
Subject: [PATCH 24/66] refactor duplicate code in gen_exports
---
src/trio/_tools/gen_exports.py | 23 +++++++----------------
1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/src/trio/_tools/gen_exports.py b/src/trio/_tools/gen_exports.py
index b4db597b63..d5d25a32a9 100755
--- a/src/trio/_tools/gen_exports.py
+++ b/src/trio/_tools/gen_exports.py
@@ -180,22 +180,13 @@ def run_linters(file: File, source: str) -> str:
SystemExit: If either failed.
"""
- success, response = run_black(file, source)
- if not success:
- print(response)
- sys.exit(1)
-
- success, response = run_ruff(file, response)
- if not success: # pragma: no cover # Test for run_ruff should catch
- print(response)
- sys.exit(1)
-
- success, response = run_black(file, response)
- if not success:
- print(response)
- sys.exit(1)
-
- return response
+ for fn in (run_black, run_ruff, run_black):
+ success, source = fn(file, source)
+ if not success:
+ print(source)
+ sys.exit(1)
+
+ return source
def gen_public_wrappers_source(file: File) -> str:
From be0846009bbecbd17a536b3779c2700f43e4331a Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Thu, 19 Dec 2024 08:17:01 +0000
Subject: [PATCH 25/66] more coverage in test_dtls
---
src/trio/_tests/test_dtls.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/trio/_tests/test_dtls.py b/src/trio/_tests/test_dtls.py
index 3f8ee2f05c..83d2fe3d20 100644
--- a/src/trio/_tests/test_dtls.py
+++ b/src/trio/_tests/test_dtls.py
@@ -117,6 +117,18 @@ async def test_smoke(ipv6: bool) -> None:
assert client_channel.get_cleartext_mtu() == cleartext_mtu_1234
+@parametrize_ipv6
+async def test_smoke_close_immediately(ipv6: bool) -> None:
+ # used to get 100% branch coverage in this file itself
+ async with dtls_echo_server(ipv6=ipv6) as (_server_endpoint, address):
+ with endpoint(ipv6=ipv6) as client_endpoint:
+ client_channel = client_endpoint.connect(address, client_ctx)
+ with pytest.raises(trio.NeedHandshakeError):
+ client_channel.get_cleartext_mtu()
+
+ await client_channel.do_handshake()
+
+
@slow
async def test_handshake_over_terrible_network(
autojump_clock: trio.testing.MockClock,
From 2722418b3824df343bea00fa60f3c69be4991832 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Thu, 19 Dec 2024 08:49:46 +0000
Subject: [PATCH 26/66] cover monitors
---
src/trio/_core/_tests/test_io.py | 35 ++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index 48b99d387d..375e171b16 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -1,7 +1,9 @@
from __future__ import annotations
import random
+import select
import socket as stdlib_socket
+import sys
from collections.abc import Awaitable, Callable
from contextlib import suppress
from typing import TYPE_CHECKING, TypeVar
@@ -343,6 +345,7 @@ def check(*, expected_readers: int, expected_writers: int) -> None:
assert iostats.tasks_waiting_write == expected_writers
else:
assert iostats.backend == "kqueue"
+ assert iostats.monitors == 0
assert iostats.tasks_waiting == expected_readers + expected_writers
a1, b1 = stdlib_socket.socketpair()
@@ -381,6 +384,38 @@ def check(*, expected_readers: int, expected_writers: int) -> None:
check(expected_readers=1, expected_writers=0)
+@pytest.mark.skipif(sys.platform in {"win32", "linux"})
+async def test_io_manager_kqueue_monitors_statistics() -> None:
+ def check(
+ *,
+ expected_monitors: int,
+ expected_readers: int,
+ expected_writers: int,
+ ) -> None:
+ statistics = _core.current_statistics()
+ print(statistics)
+ iostats = statistics.io_statistics
+ assert iostats.backend == "kqueue"
+ assert iostats.monitors == expected_monitors
+ assert iostats.tasks_waiting == expected_readers + expected_writers
+
+ a1, b1 = stdlib_socket.socketpair()
+ for sock in [a1, b1]:
+ sock.setblocking(False)
+
+ with a1, b1:
+ # let the call_soon_task settle down
+ await wait_all_tasks_blocked()
+
+ # 1 for call_soon_task
+ check(expected_monitors=0, expected_readers=1, expected_writers=0)
+
+ with trio.lowlevel.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ):
+ check(expected_monitors=1, expected_readers=1, expected_writers=0)
+
+ check(expected_monitors=0, expected_readers=1, expected_writers=0)
+
+
async def test_can_survive_unnotified_close() -> None:
# An "unnotified" close is when the user closes an fd/socket/handle
# directly, without calling notify_closing first. This should never happen
From 88583983b5067ef01e98833185e936d979f2ff04 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Thu, 19 Dec 2024 09:02:32 +0000
Subject: [PATCH 27/66] test cancel wait without pidfd
---
src/trio/_tests/test_subprocess.py | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/src/trio/_tests/test_subprocess.py b/src/trio/_tests/test_subprocess.py
index a23d47bd32..1272eef113 100644
--- a/src/trio/_tests/test_subprocess.py
+++ b/src/trio/_tests/test_subprocess.py
@@ -202,6 +202,27 @@ async def test_multi_wait(background_process: BackgroundProcessType) -> None:
proc.kill()
+@background_process_param
+async def test_multi_wait_no_pidfd(background_process: BackgroundProcessType) -> None:
+ with mock.patch("trio._subprocess.can_try_pidfd_open", new=False):
+ async with background_process(SLEEP(10)) as proc:
+ # Check that wait (including multi-wait) tolerates being cancelled
+ async with _core.open_nursery() as nursery:
+ nursery.start_soon(proc.wait)
+ nursery.start_soon(proc.wait)
+ nursery.start_soon(proc.wait)
+ await wait_all_tasks_blocked()
+ nursery.cancel_scope.cancel()
+
+ # Now try waiting for real
+ async with _core.open_nursery() as nursery:
+ nursery.start_soon(proc.wait)
+ nursery.start_soon(proc.wait)
+ nursery.start_soon(proc.wait)
+ await wait_all_tasks_blocked()
+ proc.kill()
+
+
COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR = python(
"data = sys.stdin.buffer.read(); "
"sys.stdout.buffer.write(data); "
From e5fa8cdd6007d863b7266f2fd28897948f72efe6 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Thu, 19 Dec 2024 09:12:01 +0000
Subject: [PATCH 28/66] test OSError in waitid
---
src/trio/_tests/test_subprocess.py | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/src/trio/_tests/test_subprocess.py b/src/trio/_tests/test_subprocess.py
index 1272eef113..93e1e6a425 100644
--- a/src/trio/_tests/test_subprocess.py
+++ b/src/trio/_tests/test_subprocess.py
@@ -566,6 +566,31 @@ async def test_wait_reapable_fails(background_process: BackgroundProcessType) ->
signal.signal(signal.SIGCHLD, old_sigchld)
+@pytest.mark.skipif(not posix, reason="POSIX specific")
+@background_process_param
+async def test_wait_reapable_fails_no_pidfd(
+ background_process: BackgroundProcessType,
+) -> None:
+ if TYPE_CHECKING and sys.platform == "win32":
+ return
+ with mock.patch("trio._subprocess.can_try_pidfd_open", new=False):
+ old_sigchld = signal.signal(signal.SIGCHLD, signal.SIG_IGN)
+ try:
+ # With SIGCHLD disabled, the wait() syscall will wait for the
+ # process to exit but then fail with ECHILD. Make sure we
+ # support this case as the stdlib subprocess module does.
+ async with background_process(SLEEP(3600)) as proc:
+ async with _core.open_nursery() as nursery:
+ nursery.start_soon(proc.wait)
+ await wait_all_tasks_blocked()
+ proc.kill()
+ nursery.cancel_scope.deadline = _core.current_time() + 1.0
+ assert not nursery.cancel_scope.cancelled_caught
+ assert proc.returncode == 0 # exit status unknowable, so...
+ finally:
+ signal.signal(signal.SIGCHLD, old_sigchld)
+
+
@slow
def test_waitid_eintr() -> None:
# This only matters on PyPy (where we're coding EINTR handling
From 340373df62682e49f0e45e56b1d4c7299ee808f2 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Thu, 19 Dec 2024 09:17:45 +0000
Subject: [PATCH 29/66] Update src/trio/_core/_tests/test_io.py
---
src/trio/_core/_tests/test_io.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index 375e171b16..2c929efe88 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -384,7 +384,7 @@ def check(*, expected_readers: int, expected_writers: int) -> None:
check(expected_readers=1, expected_writers=0)
-@pytest.mark.skipif(sys.platform in {"win32", "linux"})
+@pytest.mark.skipif(sys.platform in {"win32", "linux"}, reason="no kqueue")
async def test_io_manager_kqueue_monitors_statistics() -> None:
def check(
*,
From 6212cdfb6436e4e9403e062cb78b634579eaec0c Mon Sep 17 00:00:00 2001
From: Sviatoslav Sydorenko
Date: Thu, 19 Dec 2024 02:49:20 +0100
Subject: [PATCH 30/66] =?UTF-8?q?=F0=9F=A7=AA=20Collect=20coverage=20of=20?=
=?UTF-8?q?the=20Cython=20smoke=20test?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.codecov.yml | 2 +-
.github/workflows/ci.yml | 25 ++++++++++++++++++++++---
pyproject.toml | 1 +
tests/cython/run_test_cython.py | 3 +++
tests/cython/test_cython.pyx | 3 ++-
5 files changed, 29 insertions(+), 5 deletions(-)
create mode 100644 tests/cython/run_test_cython.py
diff --git a/.codecov.yml b/.codecov.yml
index c4b9b096ca..b66d33a41d 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -6,7 +6,7 @@ codecov:
notify:
# This number needs to be changed whenever the number of runs in CI is changed.
# Another option is codecov-cli: https://github.com/codecov/codecov-cli#send-notifications
- after_n_builds: 27
+ after_n_builds: 31
wait_for_ci: false
notify_error: true # if uploads fail, replace cov comment with a comment with errors.
require_ci_to_pass: false
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cb00ff85bb..4704462f05 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -234,15 +234,34 @@ jobs:
cache: pip
# setuptools is needed to get distutils on 3.12, which cythonize requires
- name: install trio and setuptools
- run: python -m pip install --upgrade pip . setuptools
+ run: python -m pip install --upgrade pip . setuptools 'coverage[toml]'
+
+ - name: add cython plugin to the coveragepy config
+ run: >-
+ sed -i 's#plugins\s=\s\[\]#plugins = ["Cython.Coverage"]#'
+ pyproject.toml
- name: install cython & compile pyx file
+ env:
+ CFLAGS: ${{ env.CFLAGS }} -DCYTHON_TRACE_NOGIL=1
run: |
python -m pip install "cython${{ matrix.cython }}"
- cythonize --inplace tests/cython/test_cython.pyx
+ cythonize --inplace -X linetrace=True tests/cython/test_cython.pyx
- name: import & run module
- run: python -c 'import tests.cython.test_cython'
+ run: coverage run -m tests.cython.run_test_cython
+
+ - name: get Python version for codecov flag
+ id: get-version
+ run: >-
+ echo "version=$(python -V | cut -d' ' -f2 | cut -d'.' -f1,2)"
+ >> "${GITHUB_OUTPUT}"
+ - if: always()
+ uses: codecov/codecov-action@v5
+ with:
+ name: Cython
+ flags: Cython,${{ steps.get-version.outputs.version }}
+ fail_ci_if_error: true
# https://github.com/marketplace/actions/alls-green#why
check: # This job does nothing and is only used for the branch protection
diff --git a/pyproject.toml b/pyproject.toml
index f0e1609876..be5ac58ea4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -296,6 +296,7 @@ omit = [
# The test suite spawns subprocesses to test some stuff, so make sure
# this doesn't corrupt the coverage files
parallel = true
+plugins = []
relative_files = true
source = ["."]
diff --git a/tests/cython/run_test_cython.py b/tests/cython/run_test_cython.py
new file mode 100644
index 0000000000..0c4e043b59
--- /dev/null
+++ b/tests/cython/run_test_cython.py
@@ -0,0 +1,3 @@
+from .test_cython import invoke_main_entry_point
+
+invoke_main_entry_point()
diff --git a/tests/cython/test_cython.pyx b/tests/cython/test_cython.pyx
index b836caf90c..77857eec4b 100644
--- a/tests/cython/test_cython.pyx
+++ b/tests/cython/test_cython.pyx
@@ -19,4 +19,5 @@ async def trio_main() -> None:
nursery.start_soon(foo)
nursery.start_soon(foo)
-trio.run(trio_main)
+def invoke_main_entry_point():
+ trio.run(trio_main)
From fce08120459dc6d7da4ff01ec3047a726a19ba6c Mon Sep 17 00:00:00 2001
From: Sviatoslav Sydorenko
Date: Thu, 19 Jan 2023 03:14:18 +0100
Subject: [PATCH 31/66] Test sdist contents instead of Git checkout in CI
This patch helps make sure that sdist contents is enough to run tests
downstream.
It also includes a smoke test for whether all the tests present in Git have
been included into the sdist. Suggested by Kyle Altendorf [[1]] [[2]].
[1]: https://github.com/python-trio/trio/pull/2541#issuecomment-2552729786
[2]: https://github.com/Chia-Network/chia-blockchain/blob/6dfcd51/.github/workflows/test-single.yml#L208-L243
---
.github/workflows/ci.yml | 174 +++++++++++++++++++++++++++++++++++++--
MANIFEST.in | 8 +-
2 files changed, 172 insertions(+), 10 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4704462f05..7a30132613 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,9 +14,133 @@ concurrency:
group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && format('-{0}', github.sha) || '' }}
cancel-in-progress: true
+env:
+ dists-artifact-name: python-package-distributions
+ dist-name: trio
+
jobs:
+ build:
+ name: 👷 dists
+
+ runs-on: ubuntu-latest
+
+ outputs:
+ dist-version: ${{ steps.dist-version.outputs.version }}
+ sdist-artifact-name: ${{ steps.artifact-name.outputs.sdist }}
+ wheel-artifact-name: ${{ steps.artifact-name.outputs.wheel }}
+
+ steps:
+ - name: Switch to using Python 3.11
+ uses: actions/setup-python@v5
+ with:
+ python-version: 3.11
+
+ - name: Grab the source from Git
+ uses: actions/checkout@v4
+
+ - name: Get the dist version
+ id: dist-version
+ run: >-
+ echo "version=$(
+ grep ^__version__ src/trio/_version.py
+ | sed 's#__version__ = "\([^"]\+\)"#\1#'
+ )"
+ >> "${GITHUB_OUTPUT}"
+
+ - name: Set the expected dist artifact names
+ id: artifact-name
+ run: |
+ echo 'sdist=${{ env.dist-name }}-*.tar.gz' >> "${GITHUB_OUTPUT}"
+ echo 'wheel=${{
+ env.dist-name
+ }}-*-py3-none-any.whl' >> "${GITHUB_OUTPUT}"
+
+ - name: Install build
+ run: python -Im pip install build
+
+ - name: Build dists
+ run: python -Im build
+ - name: Verify that the artifacts with expected names got created
+ run: >-
+ ls -1
+ dist/${{ steps.artifact-name.outputs.sdist }}
+ dist/${{ steps.artifact-name.outputs.wheel }}
+ - name: Store the distribution packages
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ env.dists-artifact-name }}
+ # NOTE: Exact expected file names are specified here
+ # NOTE: as a safety measure — if anything weird ends
+ # NOTE: up being in this dir or not all dists will be
+ # NOTE: produced, this will fail the workflow.
+ path: |
+ dist/${{ steps.artifact-name.outputs.sdist }}
+ dist/${{ steps.artifact-name.outputs.wheel }}
+ retention-days: 5
+
+ - name: >-
+ Smoke-test:
+ retrieve the project source from an sdist inside the GHA artifact
+ uses: re-actors/checkout-python-sdist@release/v2
+ with:
+ source-tarball-name: ${{ steps.artifact-name.outputs.sdist }}
+ workflow-artifact-name: ${{ env.dists-artifact-name }}
+
+ - name: >-
+ Smoke-test: move the sdist-retrieved dir into sdist-src
+ run: |
+ mv -v '${{ github.workspace }}' '${{ runner.temp }}/sdist-src'
+ mkdir -pv '${{ github.workspace }}'
+ mv -v '${{ runner.temp }}/sdist-src' '${{ github.workspace }}/sdist-src'
+ shell: bash -eEuo pipefail {0}
+
+ - name: >-
+ Smoke-test: grab the source from Git into git-src
+ uses: actions/checkout@v4
+ with:
+ path: git-src
+
+ - name: >-
+ Smoke-test: install test requirements from the Git repo
+ run: >-
+ python -Im
+ pip install -c test-requirements.txt -r test-requirements.txt
+ shell: bash -eEuo pipefail {0}
+ working-directory: git-src
+
+ - name: >-
+ Smoke-test: collect tests from the Git repo
+ env:
+ PYTHONPATH: src/
+ run: >-
+ pytest --collect-only -qq .
+ | sort
+ | tee collected-tests
+ shell: bash -eEuo pipefail {0}
+ working-directory: git-src
+
+ - name: >-
+ Smoke-test: collect tests from the sdist tarball
+ env:
+ PYTHONPATH: src/
+ run: >-
+ pytest --collect-only -qq .
+ | sort
+ | tee collected-tests
+ shell: bash -eEuo pipefail {0}
+ working-directory: sdist-src
+
+ - name: >-
+ Smoke-test:
+ verify that all the tests from Git are included in the sdist
+ run: diff --unified sdist-src/collected-tests git-src/collected-tests
+ shell: bash -eEuo pipefail {0}
+
Windows:
name: 'Windows (${{ matrix.python }}, ${{ matrix.arch }}${{ matrix.extra_name }})'
+ needs:
+ - build
+
timeout-minutes: 20
runs-on: 'windows-latest'
strategy:
@@ -58,8 +182,11 @@ jobs:
|| false
}}
steps:
- - name: Checkout
- uses: actions/checkout@v4
+ - name: Retrieve the project source from an sdist inside the GHA artifact
+ uses: re-actors/checkout-python-sdist@release/v2
+ with:
+ source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }}
+ workflow-artifact-name: ${{ env.dists-artifact-name }}
- name: Setup python
uses: actions/setup-python@v5
with:
@@ -94,6 +221,9 @@ jobs:
Ubuntu:
name: 'Ubuntu (${{ matrix.python }}${{ matrix.extra_name }})'
+ needs:
+ - build
+
timeout-minutes: 10
runs-on: 'ubuntu-latest'
strategy:
@@ -121,7 +251,14 @@ jobs:
|| false
}}
steps:
- - name: Checkout
+ - name: Retrieve the project source from an sdist inside the GHA artifact
+ if: matrix.check_formatting != '1'
+ uses: re-actors/checkout-python-sdist@release/v2
+ with:
+ source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }}
+ workflow-artifact-name: ${{ env.dists-artifact-name }}
+ - name: Grab the source from Git
+ if: matrix.check_formatting == '1'
uses: actions/checkout@v4
- name: Setup python
uses: actions/setup-python@v5
@@ -146,6 +283,9 @@ jobs:
macOS:
name: 'macOS (${{ matrix.python }})'
+ needs:
+ - build
+
timeout-minutes: 15
runs-on: 'macos-latest'
strategy:
@@ -162,8 +302,11 @@ jobs:
|| false
}}
steps:
- - name: Checkout
- uses: actions/checkout@v4
+ - name: Retrieve the project source from an sdist inside the GHA artifact
+ uses: re-actors/checkout-python-sdist@release/v2
+ with:
+ source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }}
+ workflow-artifact-name: ${{ env.dists-artifact-name }}
- name: Setup python
uses: actions/setup-python@v5
with:
@@ -183,17 +326,24 @@ jobs:
# run CI on a musl linux
Alpine:
name: "Alpine"
+ needs:
+ - build
+
runs-on: ubuntu-latest
container: alpine
steps:
- - name: Checkout
- uses: actions/checkout@v4
- name: Install necessary packages
# 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).
# `perl` for a platform independent `sed -i` alternative
run: apk update && apk add python3-dev bash nodejs perl
+ - name: Retrieve the project source from an sdist inside the GHA artifact
+ # must be after `apk add` because it relies on `bash` existing
+ uses: re-actors/checkout-python-sdist@release/v2
+ with:
+ source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }}
+ workflow-artifact-name: ${{ env.dists-artifact-name }}
- name: Enter virtual environment
run: python -m venv .venv
- name: Run tests
@@ -211,6 +361,9 @@ jobs:
Cython:
name: "Cython"
+ needs:
+ - build
+
runs-on: ubuntu-latest
strategy:
fail-fast: false
@@ -225,8 +378,11 @@ jobs:
- python: '3.13' # We support running cython3 on 3.13
cython: '>=3' # cython 3 (or greater)
steps:
- - name: Checkout
- uses: actions/checkout@v4
+ - name: Retrieve the project source from an sdist inside the GHA artifact
+ uses: re-actors/checkout-python-sdist@release/v2
+ with:
+ source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }}
+ workflow-artifact-name: ${{ env.dists-artifact-name }}
- name: Setup python
uses: actions/setup-python@v5
with:
diff --git a/MANIFEST.in b/MANIFEST.in
index 440994e43a..5ab28eabbd 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,8 +1,14 @@
+include .codecov.yml
+include check.sh
+include ci.sh
include LICENSE LICENSE.MIT LICENSE.APACHE2
include README.rst
include CODE_OF_CONDUCT.md CONTRIBUTING.md
-include test-requirements.txt
+include *-requirements.in
+include *-requirements.txt
include src/trio/py.typed
+include src/trio/_tests/astrill-codesigning-cert.cer
recursive-include src/trio/_tests/test_ssl_certs *.pem
recursive-include docs *
+recursive-include tests *
prune docs/build
From d436b069ac59d9c95bb6be03b3a69a7a2e124402 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 07:51:44 +0000
Subject: [PATCH 32/66] Update src/trio/_core/_tests/test_io.py
---
src/trio/_core/_tests/test_io.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index 2c929efe88..18d4e389a1 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -386,6 +386,7 @@ def check(*, expected_readers: int, expected_writers: int) -> None:
@pytest.mark.skipif(sys.platform in {"win32", "linux"}, reason="no kqueue")
async def test_io_manager_kqueue_monitors_statistics() -> None:
+ assert sys.platform not in {"win32", "linux"}
def check(
*,
expected_monitors: int,
From 0ac717d0d9f3b69b91309da49e9082233e05d845 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Fri, 20 Dec 2024 07:52:13 +0000
Subject: [PATCH 33/66] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
src/trio/_core/_tests/test_io.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index 18d4e389a1..8e87b3d417 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -387,6 +387,7 @@ def check(*, expected_readers: int, expected_writers: int) -> None:
@pytest.mark.skipif(sys.platform in {"win32", "linux"}, reason="no kqueue")
async def test_io_manager_kqueue_monitors_statistics() -> None:
assert sys.platform not in {"win32", "linux"}
+
def check(
*,
expected_monitors: int,
From 34d6399570e15a2023f87fbcd82c4586a5a748d4 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 07:57:54 +0000
Subject: [PATCH 34/66] filterwarning
---
src/trio/_core/_tests/test_io.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index 8e87b3d417..9f12f07f63 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -383,7 +383,7 @@ def check(*, expected_readers: int, expected_writers: int) -> None:
# 1 for call_soon_task
check(expected_readers=1, expected_writers=0)
-
+@pytest.mark.filterwarnings("ignore:.*UnboundedQueue:trio.TrioDeprecationWarning")
@pytest.mark.skipif(sys.platform in {"win32", "linux"}, reason="no kqueue")
async def test_io_manager_kqueue_monitors_statistics() -> None:
assert sys.platform not in {"win32", "linux"}
From 818094a2950d8b9baa16cb855999b18cf64fc981 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Fri, 20 Dec 2024 07:58:18 +0000
Subject: [PATCH 35/66] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
src/trio/_core/_tests/test_io.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index 9f12f07f63..d88024f23f 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -383,6 +383,7 @@ def check(*, expected_readers: int, expected_writers: int) -> None:
# 1 for call_soon_task
check(expected_readers=1, expected_writers=0)
+
@pytest.mark.filterwarnings("ignore:.*UnboundedQueue:trio.TrioDeprecationWarning")
@pytest.mark.skipif(sys.platform in {"win32", "linux"}, reason="no kqueue")
async def test_io_manager_kqueue_monitors_statistics() -> None:
From 3dac42d7ea4c0bec4d55ee146a18810527c68b86 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 08:05:51 +0000
Subject: [PATCH 36/66] try two assertions
---
src/trio/_core/_tests/test_io.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index d88024f23f..d870100020 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -387,7 +387,8 @@ def check(*, expected_readers: int, expected_writers: int) -> None:
@pytest.mark.filterwarnings("ignore:.*UnboundedQueue:trio.TrioDeprecationWarning")
@pytest.mark.skipif(sys.platform in {"win32", "linux"}, reason="no kqueue")
async def test_io_manager_kqueue_monitors_statistics() -> None:
- assert sys.platform not in {"win32", "linux"}
+ assert sys.platform != "win32"
+ assert sys.platform != "linux"
def check(
*,
From dda9a27f8efa7d9d3865da8f8815eb4ecc7f4ed2 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 08:22:03 +0000
Subject: [PATCH 37/66] ok what, this is very strange, but works.
---
src/trio/_core/_tests/test_io.py | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index d870100020..bd6c23b80b 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -385,11 +385,7 @@ def check(*, expected_readers: int, expected_writers: int) -> None:
@pytest.mark.filterwarnings("ignore:.*UnboundedQueue:trio.TrioDeprecationWarning")
-@pytest.mark.skipif(sys.platform in {"win32", "linux"}, reason="no kqueue")
async def test_io_manager_kqueue_monitors_statistics() -> None:
- assert sys.platform != "win32"
- assert sys.platform != "linux"
-
def check(
*,
expected_monitors: int,
@@ -411,13 +407,14 @@ def check(
# let the call_soon_task settle down
await wait_all_tasks_blocked()
- # 1 for call_soon_task
- check(expected_monitors=0, expected_readers=1, expected_writers=0)
+ if sys.platform != "win32" and sys.platform != "linux":
+ # 1 for call_soon_task
+ check(expected_monitors=0, expected_readers=1, expected_writers=0)
- with trio.lowlevel.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ):
- check(expected_monitors=1, expected_readers=1, expected_writers=0)
+ with trio.lowlevel.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ):
+ check(expected_monitors=1, expected_readers=1, expected_writers=0)
- check(expected_monitors=0, expected_readers=1, expected_writers=0)
+ check(expected_monitors=0, expected_readers=1, expected_writers=0)
async def test_can_survive_unnotified_close() -> None:
From bd81624468df7564b0c14ecde7372841da970851 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 08:44:04 +0000
Subject: [PATCH 38/66] no cover some unreachable pytest.fail code
---
src/trio/_tests/test_timeouts.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/trio/_tests/test_timeouts.py b/src/trio/_tests/test_timeouts.py
index bad439530c..052520b2d9 100644
--- a/src/trio/_tests/test_timeouts.py
+++ b/src/trio/_tests/test_timeouts.py
@@ -115,7 +115,7 @@ async def test_context_shields_from_outer(scope: TimeoutScope) -> None:
outer.cancel()
try:
await trio.lowlevel.checkpoint()
- except trio.Cancelled:
+ except trio.Cancelled: # pragma: no cover
pytest.fail("shield didn't work")
inner.shield = False
with pytest.raises(trio.Cancelled):
From 640f58163b20e04b9e87adc0d4fa422d724bf398 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 08:45:19 +0000
Subject: [PATCH 39/66] remove some unused code from test_subprocess.py
---
src/trio/_tests/test_subprocess.py | 7 -------
1 file changed, 7 deletions(-)
diff --git a/src/trio/_tests/test_subprocess.py b/src/trio/_tests/test_subprocess.py
index 93e1e6a425..bf6742064d 100644
--- a/src/trio/_tests/test_subprocess.py
+++ b/src/trio/_tests/test_subprocess.py
@@ -82,13 +82,6 @@ def SLEEP(seconds: int) -> list[str]:
return python(f"import time; time.sleep({seconds})")
-def got_signal(proc: Process, sig: SignalType) -> bool:
- if (not TYPE_CHECKING and posix) or sys.platform != "win32":
- return proc.returncode == -sig
- else:
- return proc.returncode != 0
-
-
@asynccontextmanager # type: ignore[misc] # Any in decorated
async def open_process_then_kill(
*args: Any,
From 50d848d4f3162bccf57d965f5b3776f9d93bd3fc Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 08:50:10 +0000
Subject: [PATCH 40/66] always use SSL_OP_NO_TLSv1_3 - now that it's available
---
src/trio/_tests/test_ssl.py | 20 ++------------------
1 file changed, 2 insertions(+), 18 deletions(-)
diff --git a/src/trio/_tests/test_ssl.py b/src/trio/_tests/test_ssl.py
index 2a16a0cd13..d271743c7a 100644
--- a/src/trio/_tests/test_ssl.py
+++ b/src/trio/_tests/test_ssl.py
@@ -210,27 +210,11 @@ def __init__(
# we still have to support versions before that, and that means we
# need to test renegotiation support, which means we need to force this
# to use a lower version where this test server can trigger
- # renegotiations. Of course TLS 1.3 support isn't released yet, but
- # I'm told that this will work once it is. (And once it is we can
- # remove the pragma: no cover too.) Alternatively, we could switch to
- # using TLSv1_2_METHOD.
- #
- # Discussion: https://github.com/pyca/pyopenssl/issues/624
-
- # This is the right way, but we can't use it until this PR is in a
- # released:
- # https://github.com/pyca/pyopenssl/pull/861
- #
- # if hasattr(SSL, "OP_NO_TLSv1_3"):
- # ctx.set_options(SSL.OP_NO_TLSv1_3)
- #
- # Fortunately pyopenssl uses cryptography under the hood, so we can be
- # confident that they're using the same version of openssl
+ # renegotiations.
from cryptography.hazmat.bindings.openssl.binding import Binding
b = Binding()
- if hasattr(b.lib, "SSL_OP_NO_TLSv1_3"):
- ctx.set_options(b.lib.SSL_OP_NO_TLSv1_3)
+ ctx.set_options(b.lib.SSL_OP_NO_TLSv1_3)
# Unfortunately there's currently no way to say "use 1.3 or worse", we
# can only disable specific versions. And if the two sides start
From 28ee44a7f7b490b87cd4c41946747846e317fc10 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 08:52:20 +0000
Subject: [PATCH 41/66] cowardly hide from the coverage check using a ternery
---
src/trio/_tests/test_socket.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/trio/_tests/test_socket.py b/src/trio/_tests/test_socket.py
index ebe94a6ca6..3e960bd9a4 100644
--- a/src/trio/_tests/test_socket.py
+++ b/src/trio/_tests/test_socket.py
@@ -376,9 +376,7 @@ async def test_sniff_sockopts() -> None:
from socket import AF_INET, AF_INET6, SOCK_DGRAM, SOCK_STREAM
# generate the combinations of families/types we're testing:
- families = [AF_INET]
- if can_create_ipv6:
- families.append(AF_INET6)
+ families = (AF_INET, AF_INET6) if can_create_ipv6 else (AF_INET,)
sockets = [
stdlib_socket.socket(family, type_)
for family in families
From ac30b6b1272d1addff15763bbda832beef14d5ed Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 09:09:25 +0000
Subject: [PATCH 42/66] Revert "more coverage in test_dtls"
This reverts commit be0846009bbecbd17a536b3779c2700f43e4331a.
---
src/trio/_tests/test_dtls.py | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/src/trio/_tests/test_dtls.py b/src/trio/_tests/test_dtls.py
index 83d2fe3d20..3f8ee2f05c 100644
--- a/src/trio/_tests/test_dtls.py
+++ b/src/trio/_tests/test_dtls.py
@@ -117,18 +117,6 @@ async def test_smoke(ipv6: bool) -> None:
assert client_channel.get_cleartext_mtu() == cleartext_mtu_1234
-@parametrize_ipv6
-async def test_smoke_close_immediately(ipv6: bool) -> None:
- # used to get 100% branch coverage in this file itself
- async with dtls_echo_server(ipv6=ipv6) as (_server_endpoint, address):
- with endpoint(ipv6=ipv6) as client_endpoint:
- client_channel = client_endpoint.connect(address, client_ctx)
- with pytest.raises(trio.NeedHandshakeError):
- client_channel.get_cleartext_mtu()
-
- await client_channel.do_handshake()
-
-
@slow
async def test_handshake_over_terrible_network(
autojump_clock: trio.testing.MockClock,
From f312a703bd9581ad27340bf3b76192630c308428 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 09:12:33 +0000
Subject: [PATCH 43/66] an empty string joined with '' is a noop, so this
branch is redundant
---
src/trio/_tools/gen_exports.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/trio/_tools/gen_exports.py b/src/trio/_tools/gen_exports.py
index d5d25a32a9..fe3431531f 100755
--- a/src/trio/_tools/gen_exports.py
+++ b/src/trio/_tools/gen_exports.py
@@ -195,9 +195,7 @@ def gen_public_wrappers_source(file: File) -> str:
"""
header = [HEADER]
-
- if file.imports:
- header.append(file.imports)
+ header.append(file.imports)
if file.platform:
# Simple checks to avoid repeating imports. If this messes up, type checkers/tests will
# just give errors.
From badc91006e2ca4ea73acb0e71f565d7105604ce3 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 09:30:46 +0000
Subject: [PATCH 44/66] test errors in CancelScope ctor
---
src/trio/_core/_tests/test_run.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/trio/_core/_tests/test_run.py b/src/trio/_core/_tests/test_run.py
index f7ac155ba9..e6d0a99ff6 100644
--- a/src/trio/_core/_tests/test_run.py
+++ b/src/trio/_core/_tests/test_run.py
@@ -371,6 +371,15 @@ async def test_cancel_scope_validation() -> None:
match="^Cannot specify both a deadline and a relative deadline$",
):
_core.CancelScope(deadline=7, relative_deadline=3)
+
+ with pytest.raises(ValueError, match="^deadline must not be NaN$"):
+ _core.CancelScope(deadline=nan)
+ with pytest.raises(ValueError, match="^relative deadline must not be NaN$"):
+ _core.CancelScope(relative_deadline=nan)
+
+ with pytest.raises(ValueError, match="^timeout must be non-negative$"):
+ _core.CancelScope(relative_deadline=-3)
+
scope = _core.CancelScope()
with pytest.raises(ValueError, match="^deadline must not be NaN$"):
From a57247aacef9cc56fe2093522a210458ea742a5c Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 09:31:13 +0000
Subject: [PATCH 45/66] cover code in the contextvars counter
---
src/trio/_core/_tests/test_run.py | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/trio/_core/_tests/test_run.py b/src/trio/_core/_tests/test_run.py
index e6d0a99ff6..0d1cf46722 100644
--- a/src/trio/_core/_tests/test_run.py
+++ b/src/trio/_core/_tests/test_run.py
@@ -11,6 +11,7 @@
from contextlib import ExitStack, contextmanager, suppress
from math import inf, nan
from typing import TYPE_CHECKING, NoReturn, TypeVar
+from unittest import mock
import outcome
import pytest
@@ -26,7 +27,7 @@
assert_checkpoints,
wait_all_tasks_blocked,
)
-from .._run import DEADLINE_HEAP_MIN_PRUNE_THRESHOLD
+from .._run import DEADLINE_HEAP_MIN_PRUNE_THRESHOLD, _count_context_run_tb_frames
from .tutil import (
check_sequence_matches,
create_asyncio_future_in_new_loop,
@@ -2845,3 +2846,12 @@ async def handle_error() -> None:
assert isinstance(exc, MyException)
assert gc.get_referrers(exc) == no_other_refs()
+
+
+def test_context_run_tb_frames() -> None:
+ class Context:
+ def run(self, fn: Callable[[], object]) -> object:
+ return fn()
+
+ with mock.patch("trio._core._run.copy_context", return_value=Context()):
+ assert _count_context_run_tb_frames() == 1
From 5344e71e5b64977f4fac3b30fdb46e4733ef8138 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 10:28:14 +0000
Subject: [PATCH 46/66] test more of kqueue
---
src/trio/_core/_tests/test_io.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index bd6c23b80b..881a34effb 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -411,8 +411,12 @@ def check(
# 1 for call_soon_task
check(expected_monitors=0, expected_readers=1, expected_writers=0)
- with trio.lowlevel.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ):
+ with _core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ) as q:
+ with pytest.raises(_core.BusyResourceError):
+ _core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ)
check(expected_monitors=1, expected_readers=1, expected_writers=0)
+ b1.send(b"\x00")
+ assert len(await q.get_batch()) == 1
check(expected_monitors=0, expected_readers=1, expected_writers=0)
From 8f05dbffa47f75afc31d7e1762328ac49cd5babf Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 10:35:47 +0000
Subject: [PATCH 47/66] actually enter the cmgr
---
src/trio/_core/_tests/test_io.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index 881a34effb..526763cf60 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -412,8 +412,11 @@ def check(
check(expected_monitors=0, expected_readers=1, expected_writers=0)
with _core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ) as q:
- with pytest.raises(_core.BusyResourceError):
- _core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ)
+ with (
+ pytest.raises(_core.BusyResourceError),
+ _core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ),
+ ):
+ pass
check(expected_monitors=1, expected_readers=1, expected_writers=0)
b1.send(b"\x00")
assert len(await q.get_batch()) == 1
From 1ec824c827ea7e69845063f2ec5c8d58ac37d42b Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 10:51:59 +0000
Subject: [PATCH 48/66] no cover unreachable cmgr inside
---
src/trio/_core/_tests/test_io.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index 526763cf60..7232219192 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -416,7 +416,7 @@ def check(
pytest.raises(_core.BusyResourceError),
_core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ),
):
- pass
+ pass # pragma: no cover
check(expected_monitors=1, expected_readers=1, expected_writers=0)
b1.send(b"\x00")
assert len(await q.get_batch()) == 1
From a0f33ee3b49854d5db9ffe709b772950d22a797b Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 10:52:47 +0000
Subject: [PATCH 49/66] remove await - it seems to timeout
---
src/trio/_core/_tests/test_io.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py
index 7232219192..379daa025e 100644
--- a/src/trio/_core/_tests/test_io.py
+++ b/src/trio/_core/_tests/test_io.py
@@ -411,15 +411,13 @@ def check(
# 1 for call_soon_task
check(expected_monitors=0, expected_readers=1, expected_writers=0)
- with _core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ) as q:
+ with _core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ):
with (
pytest.raises(_core.BusyResourceError),
_core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ),
):
pass # pragma: no cover
check(expected_monitors=1, expected_readers=1, expected_writers=0)
- b1.send(b"\x00")
- assert len(await q.get_batch()) == 1
check(expected_monitors=0, expected_readers=1, expected_writers=0)
From cc4b2179f54cc35836ddb7d4ed6ac2cca8ee7db1 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 10:56:36 +0000
Subject: [PATCH 50/66] add newsfragment
---
newsfragments/3159.misc.rst | 1 +
1 file changed, 1 insertion(+)
create mode 100644 newsfragments/3159.misc.rst
diff --git a/newsfragments/3159.misc.rst b/newsfragments/3159.misc.rst
new file mode 100644
index 0000000000..88f1d4cb89
--- /dev/null
+++ b/newsfragments/3159.misc.rst
@@ -0,0 +1 @@
+Get more coverage
From 24917b7bd74711535a779e28da143fb9d6e63a8f Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 11:01:33 +0000
Subject: [PATCH 51/66] bump the lower end of the coverage range
---
.codecov.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.codecov.yml b/.codecov.yml
index b66d33a41d..6e83a239a2 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -21,7 +21,7 @@ comment:
coverage:
# required range
- range: 99.6..100
+ range: 99.8..100
status:
# require patches to be 100%
patch:
From 68de0447a503aa1615ee528932b5f3a8a6c70215 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 10:34:11 +0000
Subject: [PATCH 52/66] add todo to nocov
---
pyproject.toml | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index be5ac58ea4..986570e0a7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -305,18 +305,16 @@ precision = 1
skip_covered = true
skip_empty = true
show_missing = true
-exclude_lines = [
- "pragma: no cover",
- "abc.abstractmethod",
- "if TYPE_CHECKING.*:",
- "if _t.TYPE_CHECKING:",
- "if t.TYPE_CHECKING:",
- "@overload",
- 'class .*\bProtocol\b.*\):',
- "raise NotImplementedError",
-]
exclude_also = [
'^\s*@pytest\.mark\.xfail',
+ "abc.abstractmethod",
+ "if TYPE_CHECKING.*:",
+ "if _t.TYPE_CHECKING:",
+ "if t.TYPE_CHECKING:",
+ "@overload",
+ 'class .*\bProtocol\b.*\):',
+ "raise NotImplementedError",
+ 'TODO: test this line'
]
partial_branches = [
"pragma: no branch",
@@ -326,4 +324,5 @@ partial_branches = [
"if .* or not TYPE_CHECKING:",
"if .* or not _t.TYPE_CHECKING:",
"if .* or not t.TYPE_CHECKING:",
+ 'TODO: test this branch',
]
From 95b1f75723d5c9c6d96618c156ccec0b9ff71a56 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 11:18:27 +0000
Subject: [PATCH 53/66] ignore some coverage with TODOs
---
src/trio/_core/_io_kqueue.py | 4 ++--
src/trio/_core/_io_windows.py | 4 ++--
src/trio/_core/_run.py | 3 ++-
src/trio/_dtls.py | 13 ++++++-------
src/trio/_tests/test_dtls.py | 4 +++-
src/trio/_tools/gen_exports.py | 2 +-
6 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/src/trio/_core/_io_kqueue.py b/src/trio/_core/_io_kqueue.py
index 6c440920d3..a7d90e56a7 100644
--- a/src/trio/_core/_io_kqueue.py
+++ b/src/trio/_core/_io_kqueue.py
@@ -98,7 +98,7 @@ def process_events(self, events: EventResult) -> None:
if isinstance(receiver, _core.Task):
_core.reschedule(receiver, outcome.Value(event))
else:
- receiver.put_nowait(event)
+ receiver.put_nowait(event) # TODO: test this line
# kevent registration is complicated -- e.g. aio submission can
# implicitly perform a EV_ADD, and EVFILT_PROC with NOTE_TRACK will
@@ -162,7 +162,7 @@ async def wait_kevent(
def abort(raise_cancel: RaiseCancelT) -> Abort:
r = abort_func(raise_cancel)
- if r is _core.Abort.SUCCEEDED:
+ if r is _core.Abort.SUCCEEDED: # TODO: test this branch
del self._registered[key]
return r
diff --git a/src/trio/_core/_io_windows.py b/src/trio/_core/_io_windows.py
index 55c4e49e7d..148253ab88 100644
--- a/src/trio/_core/_io_windows.py
+++ b/src/trio/_core/_io_windows.py
@@ -856,9 +856,9 @@ async def wait_overlapped(
`__.
"""
handle = _handle(handle_)
- if isinstance(lpOverlapped, int):
+ if isinstance(lpOverlapped, int): # TODO: test this line
lpOverlapped = ffi.cast("LPOVERLAPPED", lpOverlapped)
- if lpOverlapped in self._overlapped_waiters:
+ if lpOverlapped in self._overlapped_waiters: # TODO: test this line
raise _core.BusyResourceError(
"another task is already waiting on that lpOverlapped",
)
diff --git a/src/trio/_core/_run.py b/src/trio/_core/_run.py
index bfb38f480f..5dbaa18cab 100644
--- a/src/trio/_core/_run.py
+++ b/src/trio/_core/_run.py
@@ -115,7 +115,8 @@ def _public(fn: RetT) -> RetT:
_r = random.Random()
-def _hypothesis_plugin_setup() -> None:
+# no cover because we don't check the hypothesis plugin works with hypothesis
+def _hypothesis_plugin_setup() -> None: # pragma: no cover
from hypothesis import register_random
global _ALLOW_DETERMINISTIC_SCHEDULING
diff --git a/src/trio/_dtls.py b/src/trio/_dtls.py
index 7f4bccc9ed..52367af5fb 100644
--- a/src/trio/_dtls.py
+++ b/src/trio/_dtls.py
@@ -58,7 +58,7 @@ def worst_case_mtu(sock: SocketType) -> int:
if sock.family == trio.socket.AF_INET:
return 576 - packet_header_overhead(sock)
else:
- return 1280 - packet_header_overhead(sock)
+ return 1280 - packet_header_overhead(sock) # TODO: test this line
def best_guess_mtu(sock: SocketType) -> int:
@@ -222,7 +222,7 @@ def decode_handshake_fragment_untrusted(payload: bytes) -> HandshakeFragment:
frag_offset_bytes,
frag_len_bytes,
) = HANDSHAKE_MESSAGE_HEADER.unpack_from(payload)
- except struct.error as exc:
+ except struct.error as exc: # TODO: test this line
raise BadPacket("bad handshake message header") from exc
# 'struct' doesn't have built-in support for 24-bit integers, so we
# have to do it by hand. These can't fail.
@@ -425,14 +425,14 @@ def encode_volley(
for message in messages:
if isinstance(message, OpaqueHandshakeMessage):
encoded = encode_record(message.record)
- if mtu - len(packet) - len(encoded) <= 0:
+ if mtu - len(packet) - len(encoded) <= 0: # TODO: test this line
packets.append(packet)
packet = bytearray()
packet += encoded
assert len(packet) <= mtu
elif isinstance(message, PseudoHandshakeMessage):
space = mtu - len(packet) - RECORD_HEADER.size - len(message.payload)
- if space <= 0:
+ if space <= 0: # TODO: test this line
packets.append(packet)
packet = bytearray()
packet += RECORD_HEADER.pack(
@@ -1326,9 +1326,8 @@ async def handler(dtls_channel):
raise trio.BusyResourceError("another task is already listening")
try:
self.socket.getsockname()
- except OSError:
- # TODO: Write test that triggers this
- raise RuntimeError( # pragma: no cover
+ except OSError: # TODO: test this line
+ raise RuntimeError(
"DTLS socket must be bound before it can serve",
) from None
self._ensure_receive_loop()
diff --git a/src/trio/_tests/test_dtls.py b/src/trio/_tests/test_dtls.py
index 3f8ee2f05c..141e891586 100644
--- a/src/trio/_tests/test_dtls.py
+++ b/src/trio/_tests/test_dtls.py
@@ -75,7 +75,9 @@ async def echo_handler(dtls_channel: DTLSChannel) -> None:
print("server starting do_handshake")
await dtls_channel.do_handshake()
print("server finished do_handshake")
- async for packet in dtls_channel:
+ # no branch for leaving this for loop because we only leave
+ # a channel by cancellation.
+ async for packet in dtls_channel: # pragma: no branch
print(f"echoing {packet!r} -> {dtls_channel.peer_address!r}")
await dtls_channel.send(packet)
except trio.BrokenResourceError: # pragma: no cover
diff --git a/src/trio/_tools/gen_exports.py b/src/trio/_tools/gen_exports.py
index fe3431531f..452d857790 100755
--- a/src/trio/_tools/gen_exports.py
+++ b/src/trio/_tools/gen_exports.py
@@ -293,7 +293,7 @@ def process(files: Iterable[File], *, do_test: bool) -> None:
with open(new_path, "w", encoding="utf-8", newline="\n") as fp:
fp.write(new_source)
print("Regenerated sources successfully.")
- if not matches_disk:
+ if not matches_disk: # TODO: test this branch
# With pre-commit integration, show that we edited files.
sys.exit(1)
From becdf2a4c5dca6a634ee2114cfa367c39e8c59e4 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 11:18:58 +0000
Subject: [PATCH 54/66] assert there's no node in cached_type_info
---
src/trio/_tests/test_exports.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/trio/_tests/test_exports.py b/src/trio/_tests/test_exports.py
index 7d8a7e3c0b..830fd78d80 100644
--- a/src/trio/_tests/test_exports.py
+++ b/src/trio/_tests/test_exports.py
@@ -384,8 +384,11 @@ def lookup_symbol(symbol: str) -> dict[str, str]:
elif tool == "mypy":
# load the cached type information
cached_type_info = cache_json["names"][class_name]
- if "node" not in cached_type_info:
- cached_type_info = lookup_symbol(cached_type_info["cross_ref"])
+ # previously this was an 'if' but it seems it's no longer possible
+ # for this cache to contain 'node', if this assert raises for you
+ # please let us know!
+ assert "node" not in cached_type_info
+ cached_type_info = lookup_symbol(cached_type_info["cross_ref"])
assert "node" in cached_type_info
node = cached_type_info["node"]
From 3f347318074ad82fd0be865a57c5a50c68aed482 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 11:20:09 +0000
Subject: [PATCH 55/66] require 100% coverage
---
.codecov.yml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.codecov.yml b/.codecov.yml
index 6e83a239a2..203d81f1e9 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -21,7 +21,9 @@ comment:
coverage:
# required range
- range: 99.8..100
+ precision: 5
+ round: down
+ range: 100..100
status:
# require patches to be 100%
patch:
From bd097dd779b19e7f6bfe2e0c539613f7cb24f9d6 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 11:42:58 +0000
Subject: [PATCH 56/66] Update .codecov.yml
---
.codecov.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.codecov.yml b/.codecov.yml
index 203d81f1e9..36539b26dc 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -26,6 +26,9 @@ coverage:
range: 100..100
status:
# require patches to be 100%
+ project:
+ default:
+ target: 100%
patch:
default:
target: 100%
From e53c7efbeaf3c654d60c79c804cf8049bd57f241 Mon Sep 17 00:00:00 2001
From: EXPLOSION
Date: Sat, 21 Dec 2024 03:13:23 +0900
Subject: [PATCH 57/66] Disable ruff's COM812 (#3144)
* Disable ruff's COM812
---------
Co-authored-by: John Litborn <11260241+jakkdl@users.noreply.github.com>
---
pyproject.toml | 21 ++++++++++++---------
src/trio/_core/_generated_io_kqueue.py | 11 +++--------
src/trio/_core/_generated_io_windows.py | 19 +++++--------------
src/trio/_core/_generated_run.py | 5 +----
4 files changed, 21 insertions(+), 35 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index be5ac58ea4..ac8e8fc41d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -131,15 +131,18 @@ select = [
"YTT", # flake8-2020
]
extend-ignore = [
- 'A002', # builtin-argument-shadowing
- 'ANN401', # any-type (mypy's `disallow_any_explicit` is better)
- 'E402', # module-import-not-at-top-of-file (usually OS-specific)
- 'E501', # line-too-long
- 'F403', # undefined-local-with-import-star
- 'F405', # undefined-local-with-import-star-usage
- 'PERF203', # try-except-in-loop (not always possible to refactor)
- 'PT012', # multiple statements in pytest.raises block
- 'SIM117', # multiple-with-statements (messes up lots of context-based stuff and looks bad)
+ "A002", # builtin-argument-shadowing
+ "ANN401", # any-type (mypy's `disallow_any_explicit` is better)
+ "E402", # module-import-not-at-top-of-file (usually OS-specific)
+ "E501", # line-too-long
+ "F403", # undefined-local-with-import-star
+ "F405", # undefined-local-with-import-star-usage
+ "PERF203", # try-except-in-loop (not always possible to refactor)
+ "PT012", # multiple statements in pytest.raises block
+ "SIM117", # multiple-with-statements (messes up lots of context-based stuff and looks bad)
+
+ # conflicts with formatter (ruff recommends these be disabled)
+ "COM812",
]
[tool.ruff.lint.per-file-ignores]
diff --git a/src/trio/_core/_generated_io_kqueue.py b/src/trio/_core/_generated_io_kqueue.py
index 016704eac7..556d29e1f2 100644
--- a/src/trio/_core/_generated_io_kqueue.py
+++ b/src/trio/_core/_generated_io_kqueue.py
@@ -45,8 +45,7 @@ def current_kqueue() -> select.kqueue:
@enable_ki_protection
def monitor_kevent(
- ident: int,
- filter: int,
+ ident: int, filter: int
) -> AbstractContextManager[_core.UnboundedQueue[select.kevent]]:
"""TODO: these are implemented, but are currently more of a sketch than
anything real. See `#26
@@ -60,9 +59,7 @@ def monitor_kevent(
@enable_ki_protection
async def wait_kevent(
- ident: int,
- filter: int,
- abort_func: Callable[[RaiseCancelT], Abort],
+ ident: int, filter: int, abort_func: Callable[[RaiseCancelT], Abort]
) -> Abort:
"""TODO: these are implemented, but are currently more of a sketch than
anything real. See `#26
@@ -70,9 +67,7 @@ async def wait_kevent(
"""
try:
return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_kevent(
- ident,
- filter,
- abort_func,
+ ident, filter, abort_func
)
except AttributeError:
raise RuntimeError("must be called from async context") from None
diff --git a/src/trio/_core/_generated_io_windows.py b/src/trio/_core/_generated_io_windows.py
index 745fa4fc4e..211f81215c 100644
--- a/src/trio/_core/_generated_io_windows.py
+++ b/src/trio/_core/_generated_io_windows.py
@@ -136,8 +136,7 @@ async def wait_overlapped(handle_: int | CData, lpOverlapped: CData | int) -> ob
"""
try:
return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_overlapped(
- handle_,
- lpOverlapped,
+ handle_, lpOverlapped
)
except AttributeError:
raise RuntimeError("must be called from async context") from None
@@ -145,9 +144,7 @@ async def wait_overlapped(handle_: int | CData, lpOverlapped: CData | int) -> ob
@enable_ki_protection
async def write_overlapped(
- handle: int | CData,
- data: Buffer,
- file_offset: int = 0,
+ handle: int | CData, data: Buffer, file_offset: int = 0
) -> int:
"""TODO: these are implemented, but are currently more of a sketch than
anything real. See `#26
@@ -156,9 +153,7 @@ async def write_overlapped(
"""
try:
return await GLOBAL_RUN_CONTEXT.runner.io_manager.write_overlapped(
- handle,
- data,
- file_offset,
+ handle, data, file_offset
)
except AttributeError:
raise RuntimeError("must be called from async context") from None
@@ -166,9 +161,7 @@ async def write_overlapped(
@enable_ki_protection
async def readinto_overlapped(
- handle: int | CData,
- buffer: Buffer,
- file_offset: int = 0,
+ handle: int | CData, buffer: Buffer, file_offset: int = 0
) -> int:
"""TODO: these are implemented, but are currently more of a sketch than
anything real. See `#26
@@ -177,9 +170,7 @@ async def readinto_overlapped(
"""
try:
return await GLOBAL_RUN_CONTEXT.runner.io_manager.readinto_overlapped(
- handle,
- buffer,
- file_offset,
+ handle, buffer, file_offset
)
except AttributeError:
raise RuntimeError("must be called from async context") from None
diff --git a/src/trio/_core/_generated_run.py b/src/trio/_core/_generated_run.py
index 67d70d9077..db1454e6c7 100644
--- a/src/trio/_core/_generated_run.py
+++ b/src/trio/_core/_generated_run.py
@@ -186,10 +186,7 @@ def spawn_system_task(
"""
try:
return GLOBAL_RUN_CONTEXT.runner.spawn_system_task(
- async_fn,
- *args,
- name=name,
- context=context,
+ async_fn, *args, name=name, context=context
)
except AttributeError:
raise RuntimeError("must be called from async context") from None
From 8a3307fe4bff09b12580cf7b05d29a57c88a44fa Mon Sep 17 00:00:00 2001
From: Ilya Egorov <0x42005e1f@gmail.com>
Date: Fri, 20 Dec 2024 22:16:25 +0400
Subject: [PATCH 58/66] Add aiologic and culsans to Awesome Trio Libraries in
the docs (#3092)
* Add aiologic to Awesome Trio Libraries in the docs
---------
Co-authored-by: John Litborn <11260241+jakkdl@users.noreply.github.com>
---
docs/source/awesome-trio-libraries.rst | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/source/awesome-trio-libraries.rst b/docs/source/awesome-trio-libraries.rst
index c5c415583a..875471a21f 100644
--- a/docs/source/awesome-trio-libraries.rst
+++ b/docs/source/awesome-trio-libraries.rst
@@ -108,6 +108,8 @@ Tools and Utilities
* `aiometer `__ - Execute lots of tasks concurrently while controlling concurrency limits
* `triotp `__ - OTP framework for Python Trio
* `aioresult `__ - Get the return value of a background async function in Trio or anyio, along with a simple Future class and wait utilities
+* `aiologic `__ - Thread-safe synchronization and communication primitives: locks, capacity limiters, queues, etc.
+* `culsans `__ - Janus-like sync-async queue with Trio support. Unlike aiologic queues, provides API compatible interfaces.
Trio/Asyncio Interoperability
From 431f2edf5b0ad529a7681fae210c255c94b147b5 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 19:01:18 +0000
Subject: [PATCH 59/66] more TODO comments
---
src/trio/_core/_io_kqueue.py | 2 +-
src/trio/_dtls.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/trio/_core/_io_kqueue.py b/src/trio/_core/_io_kqueue.py
index a7d90e56a7..1dfb8c854b 100644
--- a/src/trio/_core/_io_kqueue.py
+++ b/src/trio/_core/_io_kqueue.py
@@ -81,7 +81,7 @@ def get_events(self, timeout: float) -> EventResult:
events += batch
if len(batch) < max_events:
break
- else:
+ else: # TODO: test this line
timeout = 0
# and loop back to the start
return events
diff --git a/src/trio/_dtls.py b/src/trio/_dtls.py
index 52367af5fb..54dcd178b8 100644
--- a/src/trio/_dtls.py
+++ b/src/trio/_dtls.py
@@ -1039,7 +1039,7 @@ def read_volley() -> list[_AnyHandshakeMessage]:
if (
isinstance(maybe_volley[0], PseudoHandshakeMessage)
and maybe_volley[0].content_type == ContentType.alert
- ):
+ ): # TODO: test this branch
# we're sending an alert (e.g. due to a corrupted
# packet). We want to send it once, but don't save it to
# retransmit -- keep the last volley as the current
From 117fe044f4aff323a78c50b67f348b693899fbb8 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 20:09:24 +0000
Subject: [PATCH 60/66] add one last TODO
---
src/trio/_core/_io_kqueue.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/trio/_core/_io_kqueue.py b/src/trio/_core/_io_kqueue.py
index 1dfb8c854b..9718c4df80 100644
--- a/src/trio/_core/_io_kqueue.py
+++ b/src/trio/_core/_io_kqueue.py
@@ -93,7 +93,7 @@ def process_events(self, events: EventResult) -> None:
self._force_wakeup.drain()
continue
receiver = self._registered[key]
- if event.flags & select.KQ_EV_ONESHOT:
+ if event.flags & select.KQ_EV_ONESHOT: # TODO: test this branch
del self._registered[key]
if isinstance(receiver, _core.Task):
_core.reschedule(receiver, outcome.Value(event))
From 84de11a7deab1f7848b80b2ec2765268b02ea32d Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 20:10:35 +0000
Subject: [PATCH 61/66] fix patches comment in .codecov.yml
---
.codecov.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.codecov.yml b/.codecov.yml
index 36539b26dc..fd577eb919 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -25,10 +25,9 @@ coverage:
round: down
range: 100..100
status:
- # require patches to be 100%
project:
default:
target: 100%
patch:
default:
- target: 100%
+ target: 100% # require patches to be 100%
From 2484749c82ec256877202c81f2993651ed79251a Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 20:57:46 +0000
Subject: [PATCH 62/66] swap branch for line in dtls
---
src/trio/_dtls.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/trio/_dtls.py b/src/trio/_dtls.py
index 54dcd178b8..a7709632a4 100644
--- a/src/trio/_dtls.py
+++ b/src/trio/_dtls.py
@@ -1039,7 +1039,7 @@ def read_volley() -> list[_AnyHandshakeMessage]:
if (
isinstance(maybe_volley[0], PseudoHandshakeMessage)
and maybe_volley[0].content_type == ContentType.alert
- ): # TODO: test this branch
+ ): # TODO: test this line
# we're sending an alert (e.g. due to a corrupted
# packet). We want to send it once, but don't save it to
# retransmit -- keep the last volley as the current
From d055b38132f1ecb48f4316bc9cc93b2c0092e4d4 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Fri, 20 Dec 2024 21:37:55 +0000
Subject: [PATCH 63/66] Update newsfragments/3159.misc.rst
---
newsfragments/3159.misc.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/newsfragments/3159.misc.rst b/newsfragments/3159.misc.rst
index 88f1d4cb89..9460e11c65 100644
--- a/newsfragments/3159.misc.rst
+++ b/newsfragments/3159.misc.rst
@@ -1 +1 @@
-Get more coverage
+Get and enforce 100% coverage
From afa13f50e68de07198566f4c29ca499d5507561b Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Sat, 21 Dec 2024 10:50:17 +0000
Subject: [PATCH 64/66] Update src/trio/_tests/test_exports.py
Co-authored-by: John Litborn <11260241+jakkdl@users.noreply.github.com>
---
src/trio/_tests/test_exports.py | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/trio/_tests/test_exports.py b/src/trio/_tests/test_exports.py
index 830fd78d80..81e75e1a94 100644
--- a/src/trio/_tests/test_exports.py
+++ b/src/trio/_tests/test_exports.py
@@ -384,10 +384,7 @@ def lookup_symbol(symbol: str) -> dict[str, str]:
elif tool == "mypy":
# load the cached type information
cached_type_info = cache_json["names"][class_name]
- # previously this was an 'if' but it seems it's no longer possible
- # for this cache to contain 'node', if this assert raises for you
- # please let us know!
- assert "node" not in cached_type_info
+ assert "node" not in cached_type_info, "previously this was an 'if' but it seems it's no longer possible for this cache to contain 'node', if this assert raises for you please let us know!"
cached_type_info = lookup_symbol(cached_type_info["cross_ref"])
assert "node" in cached_type_info
From e78f317bee59381ca2396359a6c7fbc1bfe1c76a Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Sat, 21 Dec 2024 10:50:29 +0000
Subject: [PATCH 65/66] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
src/trio/_tests/test_exports.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/trio/_tests/test_exports.py b/src/trio/_tests/test_exports.py
index 81e75e1a94..f89d4105e6 100644
--- a/src/trio/_tests/test_exports.py
+++ b/src/trio/_tests/test_exports.py
@@ -384,7 +384,9 @@ def lookup_symbol(symbol: str) -> dict[str, str]:
elif tool == "mypy":
# load the cached type information
cached_type_info = cache_json["names"][class_name]
- assert "node" not in cached_type_info, "previously this was an 'if' but it seems it's no longer possible for this cache to contain 'node', if this assert raises for you please let us know!"
+ assert (
+ "node" not in cached_type_info
+ ), "previously this was an 'if' but it seems it's no longer possible for this cache to contain 'node', if this assert raises for you please let us know!"
cached_type_info = lookup_symbol(cached_type_info["cross_ref"])
assert "node" in cached_type_info
From d962e0584aecf0df899ea146c4f90683ed892922 Mon Sep 17 00:00:00 2001
From: Thomas Grainger
Date: Sat, 21 Dec 2024 11:03:01 +0000
Subject: [PATCH 66/66] Update src/trio/_tools/gen_exports.py
Co-authored-by: John Litborn <11260241+jakkdl@users.noreply.github.com>
---
src/trio/_tools/gen_exports.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/trio/_tools/gen_exports.py b/src/trio/_tools/gen_exports.py
index 452d857790..5b1affe24a 100755
--- a/src/trio/_tools/gen_exports.py
+++ b/src/trio/_tools/gen_exports.py
@@ -180,7 +180,7 @@ def run_linters(file: File, source: str) -> str:
SystemExit: If either failed.
"""
- for fn in (run_black, run_ruff, run_black):
+ for fn in (run_black, run_ruff):
success, source = fn(file, source)
if not success:
print(source)