Skip to content

Commit bb1515b

Browse files
Merge pull request #486 from Pylons/update-runners-to-uvx
Update runners to uvx
2 parents 0c03e18 + 447975f commit bb1515b

File tree

9 files changed

+67
-126
lines changed

9 files changed

+67
-126
lines changed

.github/workflows/ci-tests.yml

Lines changed: 34 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -14,119 +14,72 @@ on:
1414
jobs:
1515
test:
1616
strategy:
17+
# See https://github.com/actions/runner-images
1718
matrix:
1819
py:
19-
- "3.9"
2020
- "3.10"
2121
- "3.11"
2222
- "3.12"
2323
- "3.13"
24-
- "pypy-3.9"
24+
- "3.14"
2525
- "pypy-3.10"
26-
# Pre-release
2726
os:
28-
- "ubuntu-22.04"
29-
- "windows-latest"
30-
- "macos-14" # arm64
31-
- "macos-13" # x64
32-
architecture:
33-
- x64
34-
- x86
35-
- arm64
27+
- "ubuntu-24.04" # x64
28+
- "windows-2022" # x64
29+
- "macos-15" # arm64
30+
- "macos-15-intel" # x64
3631
include:
37-
- py: "pypy-3.9"
38-
toxenv: "pypy39"
32+
- os: "ubuntu-24.04"
33+
pytest-args: "--cov"
3934
- py: "pypy-3.10"
4035
toxenv: "pypy310"
41-
exclude:
42-
# Ubuntu does not have x86/arm64 Python
43-
- os: "ubuntu-22.04"
44-
architecture: x86
45-
- os: "ubuntu-22.04"
46-
architecture: arm64
47-
# MacOS we need to make sure to remove x86 on all
48-
# We need to run no arm64 on macos-13 (Intel), but some
49-
# Python versions: 3.9/3.10
50-
#
51-
# From 3.11 onward, there is support for running x64 and
52-
# arm64 on Apple Silicon based systems (macos-14)
53-
- os: "macos-13"
54-
architecture: x86
55-
- os: "macos-13"
56-
architecture: arm64
57-
- os: "macos-14"
58-
architecture: x86
59-
- os: "macos-14"
60-
architecture: x64
61-
py: "3.9"
62-
- os: "macos-14"
63-
architecture: x64
64-
py: "3.10"
65-
# Windows does not have arm64 releases
66-
- os: "windows-latest"
67-
architecture: arm64
68-
# Don't run all PyPy versions except latest on
69-
# Windows/macOS. They are expensive to run.
70-
- os: "windows-latest"
71-
py: "pypy-3.9"
72-
- os: "macos-13"
73-
py: "pypy-3.9"
74-
- os: "macos-14"
75-
py: "pypy-3.9"
36+
pytest-args: ""
7637

77-
name: "Python: ${{ matrix.py }}-${{ matrix.architecture }} on ${{ matrix.os }}"
38+
name: "Python: ${{ matrix.py }} on ${{ matrix.os }}"
7839
runs-on: ${{ matrix.os }}
7940
steps:
80-
- uses: actions/checkout@v4
81-
- name: Setup python
82-
uses: actions/setup-python@v5
41+
- uses: actions/checkout@v6
42+
- name: Setup uv
43+
uses: astral-sh/setup-uv@v7
8344
with:
8445
python-version: ${{ matrix.py }}
85-
architecture: ${{ matrix.architecture }}
86-
- run: pip install tox
8746
- name: Running tox with specific toxenv
8847
if: ${{ matrix.toxenv != '' }}
8948
env:
9049
TOXENV: ${{ matrix.toxenv }}
91-
run: tox
50+
run: uvx tox
9251
- name: Running tox for current python version
9352
if: ${{ matrix.toxenv == '' }}
94-
run: tox -e py
53+
run: uvx tox -e py -- ${{ matrix.pytest-args }}
9554

9655
coverage:
97-
runs-on: ubuntu-22.04
56+
runs-on: ubuntu-24.04
9857
name: Validate coverage
9958
steps:
100-
- uses: actions/checkout@v4
101-
- name: Setup python
102-
uses: actions/setup-python@v5
59+
- uses: actions/checkout@v6
60+
- name: Setup uv
61+
uses: astral-sh/setup-uv@v7
10362
with:
104-
python-version: "3.13"
105-
architecture: x64
106-
107-
- run: pip install tox
108-
- run: tox -e py313,coverage
63+
python-version: "3.14"
64+
- run: uvx tox -e py314,coverage
10965
docs:
110-
runs-on: ubuntu-22.04
66+
runs-on: ubuntu-24.04
11167
name: Build the documentation
11268
steps:
113-
- uses: actions/checkout@v4
114-
- name: Setup python
115-
uses: actions/setup-python@v5
69+
- uses: actions/checkout@v6
70+
- name: Setup uv
71+
uses: astral-sh/setup-uv@v7
11672
with:
117-
python-version: "3.13"
118-
architecture: x64
119-
- run: pip install tox
120-
- run: tox -e docs
73+
python-version: "3.14"
74+
- run: uvx tox -e docs
12175
lint:
122-
runs-on: ubuntu-22.04
76+
runs-on: ubuntu-24.04
12377
name: Lint the package
12478
steps:
125-
- uses: actions/checkout@v4
126-
- name: Setup python
127-
uses: actions/setup-python@v5
79+
- uses: actions/checkout@v6
80+
- name: Setup uv
81+
uses: astral-sh/setup-uv@v7
12882
with:
129-
python-version: "3.13"
130-
architecture: x64
131-
- run: pip install tox
132-
- run: tox -e lint
83+
python-version: "3.14"
84+
- run: uvx tox -e lint
85+

CHANGES.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ Feature
2121

2222
- Add support for Python 3.12.
2323

24+
- Add support for Python 3.13.
25+
26+
- Add support for Python 3.14.
27+
2428
- Add Request.remote_host, exposing REMOTE_HOST environment variable.
2529

2630
- Added ``acceptparse.Accept.parse_offer`` to codify what types of offers
@@ -38,7 +42,7 @@ Compatibility
3842
Backwards Incompatibilities
3943
~~~~~~~~~~~~~~~~~~~~~~~~~~~
4044

41-
- Drop support for Python 2.7, 3.4, 3.5, 3.6, and 3.7.
45+
- Drop support for Python 2.7, 3.4, 3.5, 3.6, 3.7, 3.8 and 3.9
4246

4347
Experimental Features
4448
~~~~~~~~~~~~~~~~~~~~~

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import pkg_resources
21
import sys
32
import os
43
import shlex
4+
import importlib.metadata as metadata
55

66
extensions = [
77
"sphinx.ext.autodoc",
@@ -27,7 +27,7 @@
2727
copyright = "2018, Ian Bicking, Pylons Project and contributors"
2828
author = "Ian Bicking, Pylons Project, and contributors"
2929

30-
version = release = pkg_resources.get_distribution("webob").version
30+
version = release = metadata.version("webob")
3131

3232
# The language for content autogenerated by Sphinx. Refer to documentation
3333
# for a list of supported languages.

src/webob/exc.py

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -238,23 +238,18 @@ class WSGIHTTPException(Response, HTTPException):
238238
code = 500
239239
title = "Internal Server Error"
240240
explanation = ""
241-
body_template_obj = Template(
242-
"""\
241+
body_template_obj = Template("""\
243242
${explanation}<br /><br />
244243
${detail}
245244
${html_comment}
246-
"""
247-
)
245+
""")
248246

249-
plain_template_obj = Template(
250-
"""\
247+
plain_template_obj = Template("""\
251248
${status}
252249
253-
${body}"""
254-
)
250+
${body}""")
255251

256-
html_template_obj = Template(
257-
"""\
252+
html_template_obj = Template("""\
258253
<html>
259254
<head>
260255
<title>${status}</title>
@@ -263,8 +258,7 @@ class WSGIHTTPException(Response, HTTPException):
263258
<h1>${status}</h1>
264259
${body}
265260
</body>
266-
</html>"""
267-
)
261+
</html>""")
268262

269263
# Set this to True for responses that should have no request body
270264
empty_body = False
@@ -543,13 +537,11 @@ class _HTTPMove(HTTPRedirection):
543537
"""
544538

545539
explanation = "The resource has been moved to"
546-
body_template_obj = Template(
547-
"""\
540+
body_template_obj = Template("""\
548541
${explanation} <a href="${location}">${location}</a>;
549542
you should be redirected automatically.
550543
${detail}
551-
${html_comment}"""
552-
)
544+
${html_comment}""")
553545

554546
def __init__(
555547
self,
@@ -820,11 +812,9 @@ class HTTPMethodNotAllowed(HTTPClientError):
820812
code = 405
821813
title = "Method Not Allowed"
822814
# override template since we need an environment variable
823-
body_template_obj = Template(
824-
"""\
815+
body_template_obj = Template("""\
825816
The method ${REQUEST_METHOD} is not allowed for this resource. <br /><br />
826-
${detail}"""
827-
)
817+
${detail}""")
828818

829819

830820
class HTTPNotAcceptable(HTTPClientError):
@@ -842,12 +832,10 @@ class HTTPNotAcceptable(HTTPClientError):
842832
code = 406
843833
title = "Not Acceptable"
844834
# override template since we need an environment variable
845-
body_template_obj = Template(
846-
"""\
835+
body_template_obj = Template("""\
847836
The resource could not be generated that was acceptable to your browser
848837
(content of type ${HTTP_ACCEPT}. <br /><br />
849-
${detail}"""
850-
)
838+
${detail}""")
851839

852840

853841
class HTTPProxyAuthenticationRequired(HTTPClientError):
@@ -991,12 +979,10 @@ class HTTPUnsupportedMediaType(HTTPClientError):
991979
code = 415
992980
title = "Unsupported Media Type"
993981
# override template since we need an environment variable
994-
body_template_obj = Template(
995-
"""\
982+
body_template_obj = Template("""\
996983
The request media type ${CONTENT_TYPE} is not supported by this server.
997984
<br /><br />
998-
${detail}"""
999-
)
985+
${detail}""")
1000986

1001987

1002988
class HTTPRequestRangeNotSatisfiable(HTTPClientError):
@@ -1199,11 +1185,9 @@ class HTTPNotImplemented(HTTPServerError):
11991185

12001186
code = 501
12011187
title = "Not Implemented"
1202-
body_template_obj = Template(
1203-
"""
1188+
body_template_obj = Template("""
12041189
The request method ${REQUEST_METHOD} is not implemented for this server. <br /><br />
1205-
${detail}"""
1206-
)
1190+
${detail}""")
12071191

12081192

12091193
class HTTPBadGateway(HTTPServerError):

src/webob/multidict.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"""
55
Gives a multi-value dictionary object (MultiDict) plus several wrappers
66
"""
7+
78
import binascii
89
from collections.abc import MutableMapping
910
from urllib.parse import urlencode as url_encode

src/webob/response.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ def from_file(cls, fp):
351351
_http = b"HTTP/"
352352

353353
if status.startswith(_http):
354-
(http_ver, status_num, status_text) = status.split(None, 2)
354+
http_ver, status_num, status_text = status.split(None, 2)
355355
status = f"{text_(status_num)} {text_(status_text)}"
356356

357357
while 1:

tests/test_client_functional.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ def cookie_app(req):
6868
resp = Response("test")
6969
resp.headers.add("Set-Cookie", "a=b")
7070
resp.headers.add("Set-Cookie", "c=d")
71-
resp.headerlist.append(("X-Crazy", "value\r\n continuation"))
7271
return resp
7372

7473

@@ -78,9 +77,6 @@ def test_client_cookies(serve, client_app=None):
7877
req = Request.blank(server.url + "/?test")
7978
resp = req.send(client_app)
8079
assert resp.headers.getall("Set-Cookie") == ["a=b", "c=d"]
81-
assert resp.headers["X-Crazy"] == "value, continuation", repr(
82-
resp.headers["X-Crazy"]
83-
)
8480

8581

8682
@wsgify

tests/test_in_wsgiref.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import cgi
2+
from io import DEFAULT_BUFFER_SIZE
23
import logging
34
import socket
45
import sys
@@ -13,6 +14,8 @@
1314

1415
log = logging.getLogger(__name__)
1516

17+
TARGET_CL = DEFAULT_BUFFER_SIZE * 2
18+
1619

1720
@pytest.mark.usefixtures("serve")
1821
def test_request_reading(serve):
@@ -64,7 +67,7 @@ def test_interrupted_request(serve):
6467

6568

6669
def _test_app_req_interrupt(env, sr):
67-
target_cl = 100000
70+
target_cl = TARGET_CL
6871
try:
6972
req = Request(env)
7073
cl = req.content_length
@@ -94,7 +97,7 @@ def _req_int_cgi(req):
9497
def _req_int_readline(req):
9598
try:
9699
assert req.body_file.readline() == b"a=b\n"
97-
except OSError:
100+
except OSError as exc:
98101
# too early to detect disconnect
99102
raise AssertionError("False disconnect alert")
100103
req.body_file.readline()
@@ -126,7 +129,7 @@ def _send_interrupted_req(server, path="/"):
126129
_interrupted_req = (
127130
"POST %s HTTP/1.0\r\n"
128131
"content-type: application/x-www-form-urlencoded\r\n"
129-
"content-length: 100000\r\n"
132+
f"content-length: {TARGET_CL}\r\n"
130133
"\r\n"
131134
)
132-
_interrupted_req += "a=b\nz=" + "x" * 10000
135+
_interrupted_req += "a=b\nz=" + "x" * DEFAULT_BUFFER_SIZE

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[tox]
22
envlist =
33
lint,
4-
py39,py310,py311,py312,py313,pypy39,pypy310,
4+
py310,py311,py312,py313,py314,pypy39,pypy310,
55
coverage,
66
docs,
77
isolated_build = True

0 commit comments

Comments
 (0)