Skip to content

Commit

Permalink
drop python 3.7 support, drop pypy3.7-3.8, add pypy3.10 (except on wi…
Browse files Browse the repository at this point in the history
…ndows) (#2668)

* drop support for Python 3.7, PyPy3.7 & 3.8. Updates documentation, CI, and code with help of `pyupgrade --py38-plus`.
  • Loading branch information
jakkdl authored Jul 17, 2023
1 parent 6f187fb commit bf9fe64
Show file tree
Hide file tree
Showing 32 changed files with 116 additions and 161 deletions.
16 changes: 9 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.8-nightly', 'pypy-3.9-nightly']
# pypy-3.10 is failing, see https://github.com/python-trio/trio/issues/2678
python: ['3.8', '3.9', '3.10', 'pypy-3.9-nightly'] #, 'pypy-3.10-nightly']
arch: ['x86', 'x64']
lsp: ['']
lsp_extract_file: ['']
extra_name: ['']
exclude:
- python: 'pypy-3.8-nightly'
arch: 'x86'
# pypy does not release 32-bit binaries
- python: 'pypy-3.9-nightly'
arch: 'x86'
#- python: 'pypy-3.10-nightly'
# arch: 'x86'
include:
- python: '3.8'
arch: 'x64'
Expand Down Expand Up @@ -65,8 +67,8 @@ jobs:
# and then finally an actual release version. actions/setup-python doesn't
# support this for PyPy presently so we get no help there.
#
# CPython -> 3.9.0-alpha - 3.9.X
# PyPy -> pypy-3.7
# 'CPython' -> '3.9.0-alpha - 3.9.X'
# 'PyPy' -> 'pypy-3.9'
python-version: ${{ fromJSON(format('["{0}", "{1}"]', format('{0}.0-alpha - {0}.X', matrix.python), matrix.python))[startsWith(matrix.python, 'pypy')] }}
architecture: '${{ matrix.arch }}'
cache: pip
Expand All @@ -92,7 +94,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ['pypy-3.7', 'pypy-3.8', 'pypy-3.9', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12-dev', 'pypy-3.8-nightly', 'pypy-3.9-nightly']
python: ['pypy-3.9', 'pypy-3.10', '3.8', '3.9', '3.10', '3.11', '3.12-dev', 'pypy-3.9-nightly', 'pypy-3.10-nightly']
check_formatting: ['0']
extra_name: ['']
include:
Expand Down Expand Up @@ -143,7 +145,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.8-nightly', 'pypy-3.9-nightly']
python: ['3.8', '3.9', '3.10', 'pypy-3.9-nightly', 'pypy-3.10-nightly']
continue-on-error: >-
${{
(
Expand Down
5 changes: 3 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,9 @@ demonstration of implementing the "Happy Eyeballs" algorithm in an
older library versus Trio.

**Cool, but will it work on my system?** Probably! As long as you have
some kind of Python 3.7-or-better (CPython or the latest PyPy3 are
both fine), and are using Linux, macOS, Windows, or FreeBSD, then Trio
some kind of Python 3.8-or-better (CPython or [currently maintained versions of
PyPy3](https://doc.pypy.org/en/latest/faq.html#which-python-versions-does-pypy-implement)
are both fine), and are using Linux, macOS, Windows, or FreeBSD, then Trio
will work. Other environments might work too, but those
are the ones we test on. And all of our dependencies are pure Python,
except for CFFI on Windows, which has wheels available, so
Expand Down
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Vital statistics:

* Supported environments: We test on

- Python: 3.7+ (CPython and PyPy)
- Python: 3.8+ (CPython and PyPy)
- Windows, macOS, Linux (glibc and musl), FreeBSD

Other environments might also work; give it a try and see.
Expand Down
10 changes: 3 additions & 7 deletions docs/source/reference-core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -974,12 +974,8 @@ work. What we need is something that's *like* a global variable, but
that can have different values depending on which request handler is
accessing it.

To solve this problem, Python 3.7 added a new module to the standard
library: :mod:`contextvars`. And not only does Trio have built-in
support for :mod:`contextvars`, but if you're using an earlier version
of Python, then Trio makes sure that a backported version of
:mod:`contextvars` is installed. So you can assume :mod:`contextvars`
is there and works regardless of what version of Python you're using.
To solve this problem, Python has a module in the standard
library: :mod:`contextvars`.

Here's a toy example demonstrating how to use :mod:`contextvars`:

Expand Down Expand Up @@ -1009,7 +1005,7 @@ Example output (yours may differ slightly):
request 0: Request received finished
For more information, read the
`contextvars docs <https://docs.python.org/3.7/library/contextvars.html>`__.
`contextvars docs <https://docs.python.org/3/library/contextvars.html>`__.


.. _synchronization:
Expand Down
2 changes: 1 addition & 1 deletion docs/source/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Okay, ready? Let's get started.
Before you begin
----------------

1. Make sure you're using Python 3.7 or newer.
1. Make sure you're using Python 3.8 or newer.

2. ``python3 -m pip install --upgrade trio`` (or on Windows, maybe
``py -3 -m pip install --upgrade trio`` – `details
Expand Down
1 change: 1 addition & 0 deletions newsfragments/2668.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Drop support for Python3.7 and PyPy3.7/3.8.
1 change: 1 addition & 0 deletions newsfragments/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Each file should be named like ``<ISSUE>.<TYPE>.rst``, where
deprecated features after an appropriate time, go in the
``deprecated`` category instead)
* ``feature``: any new feature that doesn't qualify for ``headline``
* ``removal``: removing support for old python versions, or other removals with no deprecation period.
* ``bugfix``
* ``doc``
* ``deprecated``
Expand Down
26 changes: 17 additions & 9 deletions notes-to-self/how-does-windows-so-reuseaddr-work.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
modes = ["default", "SO_REUSEADDR", "SO_EXCLUSIVEADDRUSE"]
bind_types = ["wildcard", "specific"]


def sock(mode):
s = socket.socket(family=socket.AF_INET)
if mode == "SO_REUSEADDR":
Expand All @@ -18,6 +19,7 @@ def sock(mode):
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))
Expand All @@ -26,6 +28,7 @@ def bind(sock, bind_type):
else:
assert False


def table_entry(mode1, bind_type1, mode2, bind_type2):
with sock(mode1) as sock1:
bind(sock1, bind_type1)
Expand All @@ -41,19 +44,22 @@ def table_entry(mode1, bind_type1, mode2, bind_type2):
else:
return "Success"

print("""

print(
"""
second bind
| """
+ " | ".join(["%-19s" % mode for mode in modes])
+ " | ".join(["%-19s" % mode for mode in modes])
)

print(""" """, end='')
print(""" """, end="")
for mode in modes:
print(" | " + " | ".join(["%8s" % bind_type for bind_type in bind_types]), end='')
print(" | " + " | ".join(["%8s" % bind_type for bind_type in bind_types]), end="")

print("""
print(
"""
first bind -----------------------------------------------------------------"""
# default | wildcard | INUSE | Success | ACCESS | Success | INUSE | Success
# default | wildcard | INUSE | Success | ACCESS | Success | INUSE | Success
)

for i, mode1 in enumerate(modes):
Expand All @@ -63,6 +69,8 @@ def table_entry(mode1, bind_type1, mode2, bind_type2):
for l, bind_type2 in enumerate(bind_types):
entry = table_entry(mode1, bind_type1, mode2, bind_type2)
row.append(entry)
#print(mode1, bind_type1, mode2, bind_type2, entry)
print("{:>19} | {:>8} | ".format(mode1, bind_type1)
+ " | ".join(["%8s" % entry for entry in row]))
# print(mode1, bind_type1, mode2, bind_type2, entry)
print(
f"{mode1:>19} | {bind_type1:>8} | "
+ " | ".join(["%8s" % entry for entry in row])
)
10 changes: 6 additions & 4 deletions notes-to-self/reopen-pipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import time
import tempfile


def check_reopen(r1, w):
try:
print("Reopening read end")
r2 = os.open("/proc/self/fd/{}".format(r1), os.O_RDONLY)
r2 = os.open(f"/proc/self/fd/{r1}", os.O_RDONLY)

print("r1 is {}, r2 is {}".format(r1, r2))
print(f"r1 is {r1}, r2 is {r2}")

print("checking they both can receive from w...")

Expand Down Expand Up @@ -36,11 +37,12 @@ def check_reopen(r1, w):
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("ERROR: {!r}".format(exc))
print(f"ERROR: {exc!r}")


print("-- testing anonymous pipe --")
Expand All @@ -63,6 +65,6 @@ def sleep_then_write():

print("-- testing socketpair --")
import socket

rs, ws = socket.socketpair()
check_reopen(rs.fileno(), ws.fileno())

8 changes: 6 additions & 2 deletions notes-to-self/schedule-timing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
LOOPS = 0
RUNNING = True


async def reschedule_loop(depth):
if depth == 0:
global LOOPS
while RUNNING:
LOOPS += 1
await trio.sleep(0)
#await trio.lowlevel.cancel_shielded_checkpoint()
# await trio.lowlevel.cancel_shielded_checkpoint()
else:
await reschedule_loop(depth - 1)


async def report_loop():
global RUNNING
try:
Expand All @@ -25,13 +27,15 @@ async def report_loop():
end_count = LOOPS
loops = end_count - start_count
duration = end_time - start_time
print("{} loops/sec".format(loops / duration))
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)
2 changes: 1 addition & 1 deletion notes-to-self/socketpair-buffering.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@
except BlockingIOError:
pass

print("setsockopt bufsize {}: {}".format(bufsize, i))
print(f"setsockopt bufsize {bufsize}: {i}")
a.close()
b.close()
22 changes: 15 additions & 7 deletions notes-to-self/ssl-handshake/ssl-handshake.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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)
Expand All @@ -20,16 +21,19 @@ def _ssl_echo_serve_sync(sock):
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)
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()
Expand Down Expand Up @@ -82,21 +86,23 @@ def unwrap(self):
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,
wrap_socket_via_wrap_socket,
wrap_socket_via_wrap_bio,
]:
print("\n--- checking {} ---\n".format(wrap_socket.__name__))
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")
client_ctx, client_sock, server_hostname="trio-test-1.example.org"
)
wrapped.do_handshake()
wrapped.sendall(b"x")
assert wrapped.recv(1) == b"x"
Expand All @@ -107,7 +113,8 @@ def wrap_socket_via_wrap_bio(ctx, sock, **kwargs):
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")
client_ctx, client_sock, server_hostname="trio-test-2.example.org"
)
try:
wrapped.do_handshake()
except Exception:
Expand All @@ -119,7 +126,8 @@ def wrap_socket_via_wrap_bio(ctx, sock, **kwargs):
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")
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"
Expand Down
4 changes: 3 additions & 1 deletion notes-to-self/sslobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
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("expected {}".format(etype))
raise AssertionError(f"expected {etype}")


with expect(ssl.SSLWantReadError):
cso.do_handshake()
Expand Down
8 changes: 6 additions & 2 deletions notes-to-self/thread-closure-bug-demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@

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
Expand All @@ -46,13 +49,14 @@ def traced_looper():
t.start()

for i in range(COUNT):
print("after {} increments, x is {}".format(i, x))
print(f"after {i} increments, x is {x}")
x += 1
time.sleep(0.01)

done[0] = True
t.join()

print("Final discrepancy: {} (should be 0)".format(COUNT - x))
print(f"Final discrepancy: {COUNT - x} (should be 0)")


outer()
Loading

0 comments on commit bf9fe64

Please sign in to comment.