Skip to content

Commit

Permalink
Add support for Python 3.12 and drop EOL 3.6 and 3.7 (psf#323)
Browse files Browse the repository at this point in the history
* Add support for Python 3.12

* Drop support for EOL Python 3.6

* Upgrade Python syntax with pyupgrade --py37-plus

* examples, tests: reformat

* cachecontrol, tox: run mypy on 3.12

and tweak ignores.

---------

Co-authored-by: William Woodruff <[email protected]>
  • Loading branch information
hugovk and woodruffw committed Jul 3, 2024
1 parent 6449d15 commit 7d8689b
Show file tree
Hide file tree
Showing 19 changed files with 27 additions and 38 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ jobs:
contents: write

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: actions/setup-python@v4
with:
python-version: ">= 3.6"
python-version: "3.x"

- name: deps
run: python -m pip install -U build
Expand All @@ -35,7 +35,7 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1

- name: sign
uses: sigstore/gh-action-sigstore-python@v1.2.3
uses: sigstore/gh-action-sigstore-python@v2.0.1
with:
inputs: ./dist/*.tar.gz ./dist/*.whl
release-signing-artifacts: true
5 changes: 3 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
os: ["macos-latest", "windows-latest", "ubuntu-latest"]

steps:
- uses: "actions/checkout@v3"
- uses: "actions/checkout@v4"
- uses: "actions/setup-python@v4"
with:
python-version: "${{ matrix.python-version }}"
allow-prereleases: true
- name: "Install dependencies"
run: |
python -VV
Expand Down
10 changes: 5 additions & 5 deletions cachecontrol/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,21 +125,21 @@ def build_response(
else:
# Wrap the response file with a wrapper that will cache the
# response when the stream has been consumed.
response._fp = CallbackFileWrapper( # type: ignore[attr-defined]
response._fp, # type: ignore[attr-defined]
response._fp = CallbackFileWrapper( # type: ignore[assignment]
response._fp, # type: ignore[arg-type]
functools.partial(
self.controller.cache_response, request, response
),
)
if response.chunked:
super_update_chunk_length = response._update_chunk_length # type: ignore[attr-defined]
super_update_chunk_length = response._update_chunk_length

def _update_chunk_length(self: HTTPResponse) -> None:
super_update_chunk_length()
if self.chunk_left == 0:
self._fp._close() # type: ignore[attr-defined]
self._fp._close() # type: ignore[union-attr]

response._update_chunk_length = types.MethodType( # type: ignore[attr-defined]
response._update_chunk_length = types.MethodType( # type: ignore[method-assign]
_update_chunk_length, response
)

Expand Down
2 changes: 1 addition & 1 deletion cachecontrol/caches/file_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class _FileCacheMixin:

def __init__(
self,
directory: Union[str, Path],
directory: str | Path,
forever: bool = False,
filemode: int = 0o0600,
dirmode: int = 0o0700,
Expand Down
2 changes: 1 addition & 1 deletion cachecontrol/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ def update_cached_response(
cached_response.headers.update(
{
k: v
for k, v in response.headers.items() # type: ignore[no-untyped-call]
for k, v in response.headers.items()
if k.lower() not in excluded_headers
}
)
Expand Down
2 changes: 1 addition & 1 deletion cachecontrol/heuristics.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def update_headers(self, response: HTTPResponse) -> dict[str, str]:

if "expires" not in response.headers:
date = parsedate(response.headers["date"])
expires = expire_after(timedelta(days=1), date=datetime(*date[:6], tzinfo=timezone.utc)) # type: ignore[misc]
expires = expire_after(timedelta(days=1), date=datetime(*date[:6], tzinfo=timezone.utc)) # type: ignore[index,misc]
headers["expires"] = datetime_to_header(expires)
headers["cache-control"] = "public"
return headers
Expand Down
4 changes: 2 additions & 2 deletions cachecontrol/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ def dumps(
# also update the response with a new file handler to be
# sure it acts as though it was never read.
body = response.read(decode_content=False)
response._fp = io.BytesIO(body) # type: ignore[attr-defined]
response._fp = io.BytesIO(body) # type: ignore[assignment]
response.length_remaining = len(body)

data = {
"response": {
"body": body, # Empty bytestring if body is stored separately
"headers": {str(k): str(v) for k, v in response.headers.items()}, # type: ignore[no-untyped-call]
"headers": {str(k): str(v) for k, v in response.headers.items()},
"status": response.status,
"version": response.version,
"reason": str(response.reason),
Expand Down
2 changes: 0 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-

# SPDX-FileCopyrightText: 2015 Eric Larson
#
# SPDX-License-Identifier: Apache-2.0
Expand Down
8 changes: 4 additions & 4 deletions examples/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@

HOST = "localhost"
PORT = 8050
URL = "http://{}:{}/".format(HOST, PORT)
URL = f"http://{HOST}:{PORT}/"


class Server(object):

class Server:
def __call__(self, env, sr):
body = "Hello World!"
status = "200 OK"
headers = [
("Cache-Control", "max-age=%i" % (60 * 10)), ("Content-Type", "text/plain")
("Cache-Control", "max-age=%i" % (60 * 10)),
("Content-Type", "text/plain"),
]
sr(status, headers)
return body
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ classifiers = [
"Environment :: Web Environment",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Internet :: WWW/HTTP",
]
keywords = ["requests", "http", "caching", "web"]
Expand Down
3 changes: 1 addition & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@


class SimpleApp:

def __init__(self):
self.etag_count = 0
self.update_etag_string()
Expand Down Expand Up @@ -109,7 +108,7 @@ def fixed_length(self, env, start_response):
headers = [
("Content-Type", "text/plain"),
("Cache-Control", "max-age=5000"),
("Content-Length", str(len(body)))
("Content-Length", str(len(body))),
]
start_response("200 OK", headers)
return [body]
Expand Down
1 change: 0 additions & 1 deletion tests/test_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ def sess(url, request):


class TestSessionActions:

def test_get_caches(self, url, sess):
r2 = sess.get(url)
assert r2.from_cache is True
Expand Down
1 change: 0 additions & 1 deletion tests/test_chunked_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ def sess():


class TestChunkedResponses:

def test_cache_chunked_response(self, url, sess):
"""
Verify that an otherwise cacheable response is cached when the
Expand Down
1 change: 0 additions & 1 deletion tests/test_max_age.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@


class TestMaxAge:

@pytest.fixture()
def sess(self, url):
self.url = url
Expand Down
5 changes: 0 additions & 5 deletions tests/test_regressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#
# SPDX-License-Identifier: Apache-2.0

import sys
import pytest


Expand All @@ -13,10 +12,6 @@


class Test39:

@pytest.mark.skipif(
sys.version.startswith("2"), reason="Only run this for python 3.x"
)
def test_file_cache_recognizes_consumed_file_handle(self, url):
s = CacheControl(Session(), FileCache("web_cache"))
the_url = url + "cache_60"
Expand Down
1 change: 0 additions & 1 deletion tests/test_storage_filecache.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ def randomdata():


class FileCacheTestsMixin:

FileCacheClass = None # Either FileCache or SeparateBodyFileCache

@pytest.fixture()
Expand Down
3 changes: 1 addition & 2 deletions tests/test_storage_redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ def test_set_expiration_datetime(self):
assert self.conn.setex.called

def test_set_expiration_datetime_aware(self):
self.cache.set("foo", "bar",
expires=datetime(2014, 2, 2, tzinfo=timezone.utc))
self.cache.set("foo", "bar", expires=datetime(2014, 2, 2, tzinfo=timezone.utc))
assert self.conn.setex.called

def test_set_expiration_int(self):
Expand Down
2 changes: 1 addition & 1 deletion tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@


class NullSerializer(Serializer):

def dumps(self, request, response, body=None):
return response

Expand All @@ -20,6 +19,7 @@ def loads(self, request, data, body_file=None):

class DummyResponse:
"""Match a ``urllib3.response.HTTPResponse``."""

version = "1.1"
reason = b"Because"
strict = 0
Expand Down
5 changes: 3 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@

[tox]
isolated_build = True
envlist = py{36,37,38,39,310,311}, mypy
envlist = py{37,38,39,310,311,312}, mypy

[gh-actions]
python =
3.7: py37
3.8: py38
3.9: py39
3.10: py310, mypy
3.10: py310
3.11: py311
3.12: py312, mypy

[testenv]
deps = pytest
Expand Down

0 comments on commit 7d8689b

Please sign in to comment.