Skip to content

Commit a4a432e

Browse files
authored
Merge branch 'MagicStack:master' into master
2 parents aee115e + 96b7ed3 commit a4a432e

28 files changed

+270
-115
lines changed

.github/workflows/release.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@ jobs:
7676
fail-fast: false
7777
matrix:
7878
os: [ubuntu-latest, macos-latest]
79-
cibw_python: ["cp38-*", "cp39-*", "cp310-*", "cp311-*", "cp312-*"]
79+
cibw_python:
80+
- "cp38-*"
81+
- "cp39-*"
82+
- "cp310-*"
83+
- "cp311-*"
84+
- "cp312-*"
85+
- "cp313-*"
8086
cibw_arch: ["x86_64", "aarch64", "universal2"]
8187
exclude:
8288
- os: ubuntu-latest
@@ -108,7 +114,7 @@ jobs:
108114
run: |
109115
brew install gnu-sed libtool autoconf automake
110116
111-
- uses: pypa/cibuildwheel@fff9ec32ed25a9c576750c91e06b410ed0c15db7 # v2.16.2
117+
- uses: pypa/cibuildwheel@7940a4c0e76eb2030e473a5f864f291f63ee879b # v2.21.3
112118
env:
113119
CIBW_BUILD_VERBOSITY: 1
114120
CIBW_BUILD: ${{ matrix.cibw_python }}

.github/workflows/tests.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,15 @@ jobs:
1313
test:
1414
runs-on: ${{ matrix.os }}
1515
strategy:
16+
fail-fast: false
1617
matrix:
17-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
18+
python-version:
19+
- "3.8"
20+
- "3.9"
21+
- "3.10"
22+
- "3.11"
23+
- "3.12"
24+
- "3.13"
1825
os: [ubuntu-latest, macos-latest]
1926

2027
env:
@@ -41,6 +48,7 @@ jobs:
4148
if: steps.release.outputs.version == 0
4249
with:
4350
python-version: ${{ matrix.python-version }}
51+
allow-prereleases: true
4452

4553
- name: Install macOS deps
4654
if: matrix.os == 'macos-latest' && steps.release.outputs.version == 0
@@ -50,16 +58,15 @@ jobs:
5058
- name: Install Python Deps
5159
if: steps.release.outputs.version == 0
5260
run: |
53-
pip install -e .[test]
61+
pip install -e .[test,dev]
5462
5563
- name: Test
5664
if: steps.release.outputs.version == 0
5765
run: |
5866
make test
5967
6068
- name: Test (debug build)
61-
# XXX Re-enable 3.12 once we migrate to Cython 3
62-
if: steps.release.outputs.version == 0 && matrix.python-version != '3.12'
69+
if: steps.release.outputs.version == 0
6370
run: |
6471
make distclean && make debug && make test
6572

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ _default: compile
99

1010

1111
clean:
12-
rm -fr dist/ doc/_build/ *.egg-info uvloop/loop.*.pyd
12+
rm -fr dist/ doc/_build/ *.egg-info uvloop/loop.*.pyd uvloop/loop_d.*.pyd
1313
rm -fr uvloop/*.c uvloop/*.html uvloop/*.so
1414
rm -fr uvloop/handles/*.html uvloop/includes/*.html
1515
find . -name '__pycache__' | xargs rm -rf

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ To build uvloop, you'll need Python 3.8 or greater:
109109

110110
.. code::
111111
112-
$ python3.7 -m venv uvloop-dev
112+
$ python3 -m venv uvloop-dev
113113
$ source uvloop-dev/bin/activate
114114
115115
3. Install development dependencies:

pyproject.toml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ classifiers = [
2424
"Programming Language :: Python :: 3.10",
2525
"Programming Language :: Python :: 3.11",
2626
"Programming Language :: Python :: 3.12",
27+
"Programming Language :: Python :: 3.13",
2728
"Programming Language :: Python :: Implementation :: CPython",
2829
"Topic :: System :: Networking",
2930
]
@@ -36,14 +37,16 @@ test = [
3637
# pycodestyle is a dependency of flake8, but it must be frozen because
3738
# their combination breaks too often
3839
# (example breakage: https://gitlab.com/pycqa/flake8/issues/427)
39-
'aiohttp>=3.8.1; python_version < "3.12"',
40-
'aiohttp==3.9.0b0; python_version >= "3.12"',
40+
'aiohttp>=3.10.5',
4141
'flake8~=5.0',
4242
'psutil',
4343
'pycodestyle~=2.9.0',
4444
'pyOpenSSL~=23.0.0',
4545
'mypy>=0.800',
46-
'Cython(>=0.29.36,<0.30.0)',
46+
]
47+
dev = [
48+
'setuptools>=60',
49+
'Cython~=3.0',
4750
]
4851
docs = [
4952
'Sphinx~=4.1.2',
@@ -55,7 +58,7 @@ docs = [
5558
requires = [
5659
"setuptools>=60",
5760
"wheel",
58-
"Cython(>=0.29.36,<0.30.0)",
61+
"Cython~=3.0",
5962
]
6063
build-backend = "setuptools.build_meta"
6164

setup.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from setuptools.command.sdist import sdist
2222

2323

24-
CYTHON_DEPENDENCY = 'Cython(>=0.29.36,<0.30.0)'
24+
CYTHON_DEPENDENCY = 'Cython~=3.0'
2525
MACHINE = platform.machine()
2626
MODULES_CFLAGS = [os.getenv('UVLOOP_OPT_CFLAGS', '-O2')]
2727
_ROOT = pathlib.Path(__file__).parent
@@ -140,11 +140,14 @@ def finalize_options(self):
140140
v = True
141141

142142
directives[k] = v
143+
self.cython_directives = directives
143144

144145
self.distribution.ext_modules[:] = cythonize(
145146
self.distribution.ext_modules,
146147
compiler_directives=directives,
147-
annotate=self.cython_annotate)
148+
annotate=self.cython_annotate,
149+
compile_time_env=dict(DEFAULT_FREELIST_SIZE=250),
150+
emit_linenums=self.debug)
148151

149152
super().finalize_options()
150153

@@ -176,7 +179,11 @@ def build_libuv(self):
176179
cmd,
177180
cwd=LIBUV_BUILD_DIR, env=env, check=True)
178181

179-
j_flag = '-j{}'.format(os.cpu_count() or 1)
182+
try:
183+
njobs = len(os.sched_getaffinity(0))
184+
except AttributeError:
185+
njobs = os.cpu_count()
186+
j_flag = '-j{}'.format(njobs or 1)
180187
c_flag = "CFLAGS={}".format(env['CFLAGS'])
181188
subprocess.run(
182189
['make', j_flag, c_flag],

tests/test_base.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -576,9 +576,14 @@ def get_name(self):
576576
async def coro():
577577
pass
578578

579-
factory = lambda loop, coro, **kwargs: MyTask(
580-
coro, loop=loop, **kwargs
581-
)
579+
def factory(loop, coro, **kwargs):
580+
task = MyTask(coro, loop=loop, **kwargs)
581+
# Python moved the responsibility to set the name to the Task
582+
# class constructor, so MyTask.set_name is never called by
583+
# Python's create_task. Compensate for that here.
584+
if self.is_asyncio_loop() and "name" in kwargs:
585+
task.set_name(kwargs["name"])
586+
return task
582587

583588
self.assertIsNone(self.loop.get_task_factory())
584589
task = self.loop.create_task(coro(), name="mytask")

tests/test_dns.py

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ def patched_getaddrinfo(*args, **kwargs):
1010
# flag AI_CANONNAME, even if `host` is an IP
1111
rv = []
1212
result = socket.getaddrinfo(*args, **kwargs)
13+
first = True
1314
for af, sk, proto, canon_name, addr in result:
1415
if kwargs.get('flags', 0) & socket.AI_CANONNAME:
15-
if not canon_name:
16+
if not canon_name and first:
17+
first = False
1618
canon_name = args[0]
1719
if not isinstance(canon_name, str):
1820
canon_name = canon_name.decode('ascii')
@@ -24,20 +26,20 @@ def patched_getaddrinfo(*args, **kwargs):
2426

2527
class BaseTestDNS:
2628

27-
def _test_getaddrinfo(self, *args, _patch=False, **kwargs):
29+
def _test_getaddrinfo(self, *args, _patch=False, _sorted=False, **kwargs):
2830
err = None
2931
try:
3032
if _patch:
3133
a1 = patched_getaddrinfo(*args, **kwargs)
3234
else:
3335
a1 = socket.getaddrinfo(*args, **kwargs)
34-
except socket.gaierror as ex:
36+
except (socket.gaierror, UnicodeError) as ex:
3537
err = ex
3638

3739
try:
3840
a2 = self.loop.run_until_complete(
3941
self.loop.getaddrinfo(*args, **kwargs))
40-
except socket.gaierror as ex:
42+
except (socket.gaierror, UnicodeError) as ex:
4143
if err is not None:
4244
self.assertEqual(ex.args, err.args)
4345
else:
@@ -50,7 +52,17 @@ def _test_getaddrinfo(self, *args, _patch=False, **kwargs):
5052
if err is not None:
5153
raise err
5254

53-
self.assertEqual(a1, a2)
55+
if _sorted:
56+
if kwargs.get('flags', 0) & socket.AI_CANONNAME and a1 and a2:
57+
af, sk, proto, canon_name1, addr = a1[0]
58+
a1[0] = (af, sk, proto, '', addr)
59+
af, sk, proto, canon_name2, addr = a2[0]
60+
a2[0] = (af, sk, proto, '', addr)
61+
self.assertEqual(canon_name1, canon_name2)
62+
63+
self.assertEqual(sorted(a1), sorted(a2))
64+
else:
65+
self.assertEqual(a1, a2)
5466

5567
def _test_getnameinfo(self, *args, **kwargs):
5668
err = None
@@ -77,11 +89,13 @@ def _test_getnameinfo(self, *args, **kwargs):
7789
self.assertEqual(a1, a2)
7890

7991
def test_getaddrinfo_1(self):
80-
self._test_getaddrinfo('example.com', 80)
81-
self._test_getaddrinfo('example.com', 80, type=socket.SOCK_STREAM)
92+
self._test_getaddrinfo('example.com', 80, _sorted=True)
93+
self._test_getaddrinfo('example.com', 80, type=socket.SOCK_STREAM,
94+
_sorted=True)
8295

8396
def test_getaddrinfo_2(self):
84-
self._test_getaddrinfo('example.com', 80, flags=socket.AI_CANONNAME)
97+
self._test_getaddrinfo('example.com', 80, flags=socket.AI_CANONNAME,
98+
_sorted=True)
8599

86100
def test_getaddrinfo_3(self):
87101
self._test_getaddrinfo('a' + '1' * 50 + '.wat', 800)
@@ -92,12 +106,14 @@ def test_getaddrinfo_4(self):
92106
family=-1)
93107

94108
def test_getaddrinfo_5(self):
95-
self._test_getaddrinfo('example.com', '80')
96-
self._test_getaddrinfo('example.com', '80', type=socket.SOCK_STREAM)
109+
self._test_getaddrinfo('example.com', '80', _sorted=True)
110+
self._test_getaddrinfo('example.com', '80', type=socket.SOCK_STREAM,
111+
_sorted=True)
97112

98113
def test_getaddrinfo_6(self):
99-
self._test_getaddrinfo(b'example.com', b'80')
100-
self._test_getaddrinfo(b'example.com', b'80', type=socket.SOCK_STREAM)
114+
self._test_getaddrinfo(b'example.com', b'80', _sorted=True)
115+
self._test_getaddrinfo(b'example.com', b'80', type=socket.SOCK_STREAM,
116+
_sorted=True)
101117

102118
def test_getaddrinfo_7(self):
103119
self._test_getaddrinfo(None, 0)
@@ -116,8 +132,9 @@ def test_getaddrinfo_10(self):
116132
self._test_getaddrinfo(None, None, type=socket.SOCK_STREAM)
117133

118134
def test_getaddrinfo_11(self):
119-
self._test_getaddrinfo(b'example.com', '80')
120-
self._test_getaddrinfo(b'example.com', '80', type=socket.SOCK_STREAM)
135+
self._test_getaddrinfo(b'example.com', '80', _sorted=True)
136+
self._test_getaddrinfo(b'example.com', '80', type=socket.SOCK_STREAM,
137+
_sorted=True)
121138

122139
def test_getaddrinfo_12(self):
123140
# musl always returns ai_canonname but we don't
@@ -187,6 +204,18 @@ def test_getaddrinfo_20(self):
187204
self._test_getaddrinfo('127.0.0.1', 80, type=socket.SOCK_STREAM,
188205
flags=socket.AI_CANONNAME, _patch=patch)
189206

207+
# https://github.com/libuv/libuv/security/advisories/GHSA-f74f-cvh7-c6q6
208+
# See also: https://github.com/MagicStack/uvloop/pull/600
209+
def test_getaddrinfo_21(self):
210+
payload = f'0x{"0" * 246}7f000001.example.com'.encode('ascii')
211+
self._test_getaddrinfo(payload, 80)
212+
self._test_getaddrinfo(payload, 80, type=socket.SOCK_STREAM)
213+
214+
def test_getaddrinfo_22(self):
215+
payload = f'0x{"0" * 246}7f000001.example.com'
216+
self._test_getaddrinfo(payload, 80)
217+
self._test_getaddrinfo(payload, 80, type=socket.SOCK_STREAM)
218+
190219
######
191220

192221
def test_getnameinfo_1(self):

tests/test_tcp.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,9 @@ def test_create_server_4(self):
248248
addr = sock.getsockname()
249249

250250
with self.assertRaisesRegex(OSError,
251-
r"error while attempting.*\('127.*: "
252-
r"address( already)? in use"):
251+
r"error while attempting.*\('127.*:"
252+
r"( \[errno \d+\])? address"
253+
r"( already)? in use"):
253254

254255
self.loop.run_until_complete(
255256
self.loop.create_server(object, *addr))
@@ -1630,17 +1631,22 @@ async def client(addr):
16301631
self.fail("unexpected call to connection_made()")
16311632

16321633
def test_ssl_connect_accepted_socket(self):
1633-
if hasattr(ssl, 'PROTOCOL_TLS'):
1634-
proto = ssl.PROTOCOL_TLS
1634+
if hasattr(ssl, 'PROTOCOL_TLS_SERVER'):
1635+
server_proto = ssl.PROTOCOL_TLS_SERVER
1636+
client_proto = ssl.PROTOCOL_TLS_CLIENT
16351637
else:
1636-
proto = ssl.PROTOCOL_SSLv23
1637-
server_context = ssl.SSLContext(proto)
1638+
if hasattr(ssl, 'PROTOCOL_TLS'):
1639+
client_proto = server_proto = ssl.PROTOCOL_TLS
1640+
else:
1641+
client_proto = server_proto = ssl.PROTOCOL_SSLv23
1642+
1643+
server_context = ssl.SSLContext(server_proto)
16381644
server_context.load_cert_chain(self.ONLYCERT, self.ONLYKEY)
16391645
if hasattr(server_context, 'check_hostname'):
16401646
server_context.check_hostname = False
16411647
server_context.verify_mode = ssl.CERT_NONE
16421648

1643-
client_context = ssl.SSLContext(proto)
1649+
client_context = ssl.SSLContext(client_proto)
16441650
if hasattr(server_context, 'check_hostname'):
16451651
client_context.check_hostname = False
16461652
client_context.verify_mode = ssl.CERT_NONE
@@ -2233,8 +2239,7 @@ def test_renegotiation(self):
22332239
sslctx.use_privatekey_file(self.ONLYKEY)
22342240
sslctx.use_certificate_chain_file(self.ONLYCERT)
22352241
client_sslctx = self._create_client_ssl_context()
2236-
if hasattr(ssl, 'OP_NO_TLSv1_3'):
2237-
client_sslctx.options |= ssl.OP_NO_TLSv1_3
2242+
client_sslctx.maximum_version = ssl.TLSVersion.TLSv1_2
22382243

22392244
def server(sock):
22402245
conn = openssl_ssl.Connection(sslctx, sock)
@@ -2592,8 +2597,7 @@ def test_flush_before_shutdown(self):
25922597
sslctx_openssl.use_privatekey_file(self.ONLYKEY)
25932598
sslctx_openssl.use_certificate_chain_file(self.ONLYCERT)
25942599
client_sslctx = self._create_client_ssl_context()
2595-
if hasattr(ssl, 'OP_NO_TLSv1_3'):
2596-
client_sslctx.options |= ssl.OP_NO_TLSv1_3
2600+
client_sslctx.maximum_version = ssl.TLSVersion.TLSv1_2
25972601

25982602
future = None
25992603

0 commit comments

Comments
 (0)