Skip to content

Commit 9fba582

Browse files
committed
Drop support for Python 3.6 and old urllib3
1 parent 4857661 commit 9fba582

File tree

6 files changed

+28
-70
lines changed

6 files changed

+28
-70
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ jobs:
5757
run: |
5858
set -x
5959
python -VV
60-
# Must pin virtualenv for tox py36 testenv:
61-
python -m pip install 'tox<4' 'virtualenv<20.22.0'
60+
python -m pip install 'tox<4'
6261
python -m tox --version
6362
- name: Test ${{ matrix.tox.name }}
6463
run: |

CHANGELOG.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ Release history
2525
^^^^^^^^^^^^^^^
2626
.. This extra heading level keeps the ToC from becoming unmanageably long
2727
28+
vNext
29+
-----
30+
31+
*unreleased changes*
32+
33+
Breaking changes
34+
~~~~~~~~~~~~~~~~
35+
36+
* Require Python 3.7 or later.
37+
* Require urllib3 1.25 or later (released 2019-04-29).
38+
39+
2840
v9.2
2941
-----
3042

anymail/backends/mailgun.py

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,12 @@
11
from datetime import datetime
2-
from email.utils import encode_rfc2231
32
from urllib.parse import quote
43

5-
from requests import Request
6-
74
from ..exceptions import AnymailError, AnymailRequestsAPIError
85
from ..message import AnymailRecipientStatus
96
from ..utils import get_anymail_setting, rfc2822date
107
from .base_requests import AnymailRequestsBackend, RequestsPayload
118

129

13-
# Feature-detect whether requests (urllib3) correctly uses RFC 7578 encoding for non-
14-
# ASCII filenames in Content-Disposition headers. (This was fixed in urllib3 v1.25.)
15-
# See MailgunPayload.get_request_params for info (and a workaround on older versions).
16-
# (Note: when this workaround is removed, please also remove "old_urllib3" tox envs.)
17-
def is_requests_rfc_5758_compliant():
18-
request = Request(
19-
method="POST",
20-
url="https://www.example.com",
21-
files=[("attachment", ("\N{NOT SIGN}.txt", "test", "text/plain"))],
22-
)
23-
prepared = request.prepare()
24-
form_data = prepared.body # bytes
25-
return b"filename*=" not in form_data
26-
27-
28-
REQUESTS_IS_RFC_7578_COMPLIANT = is_requests_rfc_5758_compliant()
29-
30-
3110
class EmailBackend(AnymailRequestsBackend):
3211
"""
3312
Mailgun API Email Backend
@@ -163,37 +142,6 @@ def get_api_endpoint(self):
163142
)
164143
return "%s/messages" % quote(self.sender_domain, safe="")
165144

166-
def get_request_params(self, api_url):
167-
params = super().get_request_params(api_url)
168-
non_ascii_filenames = [
169-
filename
170-
for (field, (filename, content, mimetype)) in params["files"]
171-
if filename is not None and not isascii(filename)
172-
]
173-
if non_ascii_filenames and not REQUESTS_IS_RFC_7578_COMPLIANT:
174-
# Workaround https://github.com/requests/requests/issues/4652:
175-
# Mailgun expects RFC 7578 compliant multipart/form-data, and is confused
176-
# by Requests/urllib3's improper use of RFC 2231 encoded filename parameters
177-
# ("filename*=utf-8''...") in Content-Disposition headers.
178-
# The workaround is to pre-generate the (non-compliant) form-data body, and
179-
# replace 'filename*={RFC 2231 encoded}' with 'filename="{UTF-8 bytes}"'.
180-
# Replace _only_ filenames that will be problems (not all "filename*=...")
181-
# to minimize potential side effects--e.g., in attached messages that might
182-
# have their own attachments with (correctly) RFC 2231 encoded filenames.
183-
prepared = Request(**params).prepare()
184-
form_data = prepared.body # bytes
185-
for filename in non_ascii_filenames: # text
186-
rfc2231_filename = encode_rfc2231(filename, charset="utf-8")
187-
form_data = form_data.replace(
188-
b"filename*=" + rfc2231_filename.encode("utf-8"),
189-
b'filename="' + filename.encode("utf-8") + b'"',
190-
)
191-
params["data"] = form_data
192-
# Content-Type: multipart/form-data; boundary=...
193-
params["headers"]["Content-Type"] = prepared.headers["Content-Type"]
194-
params["files"] = None # these are now in the form_data body
195-
return params
196-
197145
def serialize_data(self):
198146
self.populate_recipient_variables()
199147
return self.data

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[tool.black]
22
force-exclude = '^/tests/test_settings/settings_.*\.py'
33
max-line-length = 88
4-
target-version = ["py36"]
4+
target-version = ["py37"]
55

66
[tool.doc8]
77
# ignore very long lines in ESP support table:
@@ -16,4 +16,4 @@ max-line-length = 120
1616
combine_as_imports = true
1717
known_first_party = "anymail"
1818
profile = "black"
19-
py_version = "36"
19+
py_version = "37"

setup.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ def long_description_from_readme(rst):
4545
"sphinx-rtd-theme",
4646
"tox",
4747
"twine",
48-
"virtualenv<20.22.0", # tox dependency, pinned for Python 3.6 tox testenv
4948
"wheel",
5049
]
5150

@@ -71,8 +70,12 @@ def long_description_from_readme(rst):
7170
license="BSD License",
7271
packages=["anymail"],
7372
zip_safe=False,
74-
python_requires=">=3.6",
75-
install_requires=["django>=2.0", "requests>=2.4.3"],
73+
python_requires=">=3.7",
74+
install_requires=[
75+
"django>=2.0",
76+
"requests>=2.4.3",
77+
"urllib3>=1.25.0", # requests dependency: fixes RFC 7578 header encoding
78+
],
7679
extras_require={
7780
# This can be used if particular backends have unique dependencies.
7881
# For simplicity, requests is included in the base requirements.
@@ -100,7 +103,6 @@ def long_description_from_readme(rst):
100103
"Programming Language :: Python :: Implementation :: PyPy",
101104
"Programming Language :: Python :: Implementation :: CPython",
102105
"Programming Language :: Python :: 3",
103-
"Programming Language :: Python :: 3.6",
104106
"Programming Language :: Python :: 3.7",
105107
"Programming Language :: Python :: 3.8",
106108
"Programming Language :: Python :: 3.9",

tox.ini

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ envlist =
66
# Test lint, docs, earliest/latest Django first, to catch most errors early...
77
lint
88
django42-py311-all
9-
django30-py36-all
9+
django30-py37-all
1010
docs
1111
# ... then test all the other supported combinations:
1212
# Django 4.2: Python 3.8, 3.9, 3.10, 3.11
@@ -15,21 +15,19 @@ envlist =
1515
django41-py{38,39,310,py38,py39}-all
1616
# Django 4.0: Python 3.8, 3.9, 3.10
1717
django40-py{38,39,310,py38,py39}-all
18-
# Django 3.2: Python 3.6, 3.7, 3.8, 3.9
19-
django32-py{36,37,38,39,py38,py39}-all
20-
# Django 3.1: Python 3.6, 3.7, 3.8, 3.9 (added in 3.1.3)
21-
django31-py{36,37,38,39,py38,py39}-all
22-
# Django 3.0: Python 3.6, 3.7, 3.8, 3.9 (added in 3.0.11)
23-
django30-py{37,38,39,py38,py39}-all
18+
# Django 3.2: Python 3.6 (eol 2021-12-23), 3.7, 3.8, 3.9
19+
django32-py{37,38,39,py38,py39}-all
20+
# Django 3.1: Python 3.6 (eol 2021-12-23), 3.7, 3.8, 3.9 (added in 3.1.3)
21+
django31-py{37,38,39,py38,py39}-all
22+
# Django 3.0: Python 3.6 (eol 2021-12-23), 3.7, 3.8, 3.9 (added in 3.0.11)
23+
django30-py{38,39,py38,py39}-all
2424
# ... then prereleases (if available) and current development:
2525
# Django 5.0 alpha: Python 3.10+
2626
# [not yet in alpha] django50-py{310,311,py310,py311}-all
2727
# Django 5.0 dev: Python 3.10+
2828
djangoDev-py{310,311}-all
2929
# ... then partial installation (limit extras):
3030
django42-py311-{none,amazon_ses,postal}
31-
# ... then older versions of some dependencies:
32-
django32-py37-all-old_urllib3
3331

3432
[testenv]
3533
deps =
@@ -41,7 +39,6 @@ deps =
4139
django42: django~=4.2.0
4240
django50: django~=5.0.0a0
4341
djangoDev: https://github.com/django/django/tarball/main
44-
old_urllib3: urllib3<1.25
4542
extras =
4643
# install [test] extras, unconditionally
4744
test

0 commit comments

Comments
 (0)