Skip to content

Commit

Permalink
PYTHON-3707 Drop support for Python 2.7, 3.4, 3.5, and 3.6 (#642)
Browse files Browse the repository at this point in the history
  • Loading branch information
ShaneHarvey authored May 8, 2023
1 parent 792beee commit af95177
Show file tree
Hide file tree
Showing 12 changed files with 34 additions and 111 deletions.
4 changes: 2 additions & 2 deletions .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ tasks:
- func: "fetch source"
- func: "build python release"
- func: "test python release"
vars: { PYTHON: python }
vars: { PYTHON: /opt/python/3.7/bin/python3 }
- func: "upload python release"

- name: "release-python-windows"
Expand All @@ -476,7 +476,7 @@ tasks:
- func: "fetch source"
- func: "build python release"
- func: "test python release"
vars: { PYTHON: python }
vars: { PYTHON: C:/python/Python37/python.exe }
- func: "upload python release"

- name: "release-python-combine"
Expand Down
21 changes: 4 additions & 17 deletions bindings/python/.evergreen/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ git clone https://github.com/mongodb-labs/drivers-evergreen-tools.git
if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
PYMONGOCRYPT_LIB=${MONGOCRYPT_DIR}/nocrypto/bin/mongocrypt.dll
export PYMONGOCRYPT_LIB=$(cygpath -m $PYMONGOCRYPT_LIB)
PYTHONS=("C:/python/Python27/python.exe"
"C:/python/Python34/python.exe"
"C:/python/Python35/python.exe"
"C:/python/Python36/python.exe"
"C:/python/Python37/python.exe"
PYTHONS=("C:/python/Python37/python.exe"
"C:/python/Python38/python.exe"
"C:/python/Python39/python.exe"
"C:/python/Python310/python.exe"
Expand All @@ -33,12 +29,7 @@ elif [ "Darwin" = "$(uname -s)" ]; then
PYTHONS=("/Library/Frameworks/Python.framework/Versions/3.10/bin/python3"
)
else
PYTHONS=("python" # Python 2.7 from brew
"python3" # Python 3 from brew
"/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python"
"/Library/Frameworks/Python.framework/Versions/3.4/bin/python3"
"/Library/Frameworks/Python.framework/Versions/3.5/bin/python3"
"/Library/Frameworks/Python.framework/Versions/3.6/bin/python3"
PYTHONS=("python3" # Python 3 from brew
"/Library/Frameworks/Python.framework/Versions/3.7/bin/python3"
"/Library/Frameworks/Python.framework/Versions/3.8/bin/python3"
"/Library/Frameworks/Python.framework/Versions/3.9/bin/python3"
Expand All @@ -51,12 +42,8 @@ elif [ "Darwin" = "$(uname -s)" ]; then
--version latest --out ../crypt_shared/
else
export PYMONGOCRYPT_LIB=${MONGOCRYPT_DIR}/nocrypto/lib64/libmongocrypt.so
PYTHONS=("/opt/python/2.7/bin/python"
"/opt/python/3.4/bin/python3"
"/opt/python/3.5/bin/python3"
"/opt/python/3.6/bin/python3"
"/opt/python/pypy/bin/pypy"
"/opt/python/pypy3.6/bin/pypy3")
PYTHONS=("/opt/mongodbtoolchain/v3/bin/python3"
)
export CRYPT_SHARED_PATH="../crypt_shared/lib/mongo_crypt_v1.so"
/opt/mongodbtoolchain/v3/bin/python3 drivers-evergreen-tools/.evergreen/mongodl.py --component \
crypt_shared --version latest --out ../crypt_shared/ --target rhel70
Expand Down
10 changes: 1 addition & 9 deletions bindings/python/.evergreen/utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,6 @@ createvirtualenv () {
fi
# Upgrade to the latest versions of pip setuptools wheel so that
# pip can always download the latest cryptography+cffi wheels.
PYTHON_VERSION=$(python -c 'import sys;print("%s.%s" % sys.version_info[:2])')
if [[ $PYTHON_VERSION == "2.7" || $PYTHON_VERSION == "3.4" || $PYTHON_VERSION == "3.5" ]]; then
# pip 19.2 dropped support for Python 3.4.
# pip 21 dropped support for Python 2.7 and 3.5.
curl -sSL https://bootstrap.pypa.io/pip/${PYTHON_VERSION}/get-pip.py -o get-pip.py
python get-pip.py
else
python -m pip install --upgrade pip
fi
python -m pip install --upgrade pip
python -m pip install --upgrade pip setuptools wheel
}
5 changes: 5 additions & 0 deletions bindings/python/CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

Changes in Version 1.6.0
------------------------

- Drop support for Python 2 and Python <3.7. Python >=3.7 is now required.

Changes in Version 1.5.2
------------------------

Expand Down
4 changes: 2 additions & 2 deletions bindings/python/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Python wrapper library for libmongocrypt that supports client side encryption
in drivers. PyMongoCrypt uses `cffi <https://pypi.org/project/cffi/>`_ and
`cryptography <https://pypi.org/project/cryptography/>`_.

PyMongoCrypt supports Python 2.7, 3.4+, and PyPy3.5+.
PyMongoCrypt supports Python 3.7+ and PyPy3.7+.

Support / Feedback
==================
Expand Down Expand Up @@ -145,7 +145,7 @@ Linux::
Dependencies
============

PyMongoCrypt supports CPython 2.7, 3.4+, PyPy, and PyPy3.5+.
PyMongoCrypt supports Python 3.7+ and PyPy3.7+.

PyMongoCrypt requires `cffi <https://pypi.org/project/cffi/>`_ and
`cryptography <https://pypi.org/project/cryptography/>`_.
Expand Down
2 changes: 1 addition & 1 deletion bindings/python/build-manylinux-wheel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ cd /python
# Compile wheel
# https://github.com/pypa/manylinux/issues/49
rm -rf build
/opt/python/cp37-cp37m/bin/python setup.py bdist_wheel --universal
/opt/python/cp37-cp37m/bin/python setup.py bdist_wheel

# Audit wheels and write manylinux tag
for whl in dist/*.whl; do
Expand Down
27 changes: 0 additions & 27 deletions bindings/python/pymongocrypt/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

"""Utility functions and definitions for Python compatibility."""

import base64
import sys

PY3 = sys.version_info[0] == 3
Expand All @@ -33,29 +32,3 @@ def str_to_bytes(string):
if isinstance(string, bytes):
return string
return string.encode('utf-8')


def safe_bytearray_or_base64(data):
"""Convert the given value to a type that, when BSON-encoded can be safely
passed to libmongocrypt functions that expect a BSON document containing
BSON Binary data or a base64-encoded string.
pymongo.bson encodes bytes to BSON string in Python 2, while the
libmongocrypt API expects BSON Binary or a base64 encoded string.
To avoid needing to import bson.Binary, we return a base64 encoded string
when using Python 2.
"""
# On Python 3 byte-arrays are encoded as BSON Binary and
# base64 encoded strings can be passed as-is.
if PY3:
return data

# On Python 2 unicode literals are assumed to contain base64-encoded
# strings that can be passed to libmongocrypt as-is.
if isinstance(data, unicode_type):
return data

# On Python 2, all other types are assumed to contain raw bytes.
# To avoid importing bson.binary.Binary, we convert these to
# base64 strings.
return unicode_type(base64.b64encode(data))
26 changes: 5 additions & 21 deletions bindings/python/pymongocrypt/mongocrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
from pymongocrypt.binary import (MongoCryptBinaryIn,
MongoCryptBinaryOut)
from pymongocrypt.binding import ffi, lib, _to_string
from pymongocrypt.compat import (safe_bytearray_or_base64, str_to_bytes,
unicode_type)
from pymongocrypt.compat import str_to_bytes, unicode_type
from pymongocrypt.credentials import _ask_for_kms_credentials
from pymongocrypt.errors import MongoCryptError
from pymongocrypt.state_machine import MongoCryptCallback
Expand Down Expand Up @@ -48,12 +47,10 @@ def __init__(self, kms_providers, schema_map=None, encrypted_fields_map=None,
and optionally a "sessionToken" for temporary credentials.
- `azure`: Map with "clientId" and "clientSecret" as strings.
- `gcp`: Map with "email" as a string and "privateKey" as
a byte array or a base64-encoded string. On Python 2,
base64-encoded strings must be passed as unicode literals.
a byte array or a base64-encoded string.
- `kmip`: Map with "endpoint" as a string.
- `local`: Map with "key" as a 96-byte array or the equivalent
base64-encoded string. On Python 2, base64-encoded strings
must be passed as unicode literals.
base64-encoded string.
- `schema_map`: Optional map of collection namespace ("db.coll") to
JSON Schema. By default, a collection's JSONSchema is periodically
polled with the listCollections command. But a JSONSchema may be
Expand Down Expand Up @@ -125,8 +122,7 @@ def __init__(self, kms_providers, schema_map=None, encrypted_fields_map=None,
if not isinstance(kms_providers['gcp']['privateKey'],
(bytes, unicode_type)):
raise TypeError("kms_providers['gcp']['privateKey'] must "
"be an instance of bytes or str "
"(unicode in Python 2)")
"be an instance of bytes or str")

if 'kmip' in kms_providers:
kmip = kms_providers['kmip']
Expand All @@ -149,8 +145,7 @@ def __init__(self, kms_providers, schema_map=None, encrypted_fields_map=None,
if not isinstance(kms_providers['local']['key'],
(bytes, unicode_type)):
raise TypeError("kms_providers['local']['key'] must be an "
"instance of bytes or str (unicode in "
"Python 2)")
"instance of bytes or str")

if schema_map is not None and not isinstance(schema_map, bytes):
raise TypeError("schema_map must be bytes or None")
Expand Down Expand Up @@ -200,17 +195,6 @@ def __init__(self, options, callback):
def __init(self):
"""Internal init helper."""
kms_providers = self.__opts.kms_providers

# Make fields that can be passed as binary or string safe to
# encode to BSON.
base64_or_bytes_fields = [("local", "key"), ("gcp", "privateKey")]
for f1, f2 in base64_or_bytes_fields:
value = kms_providers.get(f1, {}).get(f2, None)
if value is not None:
safe_value = safe_bytearray_or_base64(value)
if value != safe_value:
kms_providers = copy.deepcopy(kms_providers)
kms_providers[f1][f2] = safe_value
with MongoCryptBinaryIn(
self.__callback.bson_encode(kms_providers)) as kmsopt:
if not lib.mongocrypt_setopt_kms_providers(
Expand Down
15 changes: 7 additions & 8 deletions bindings/python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

from setuptools import setup, find_packages

if sys.version_info[:3] < (3, 7):
raise RuntimeError("pymongocrypt requires Python version >= 3.7")

# Make our Windows and macOS wheels platform specific because we embed
# libmongocrypt. On Linux we ship manylinux2010 wheels which cannot do this or
# else auditwheel raises the following error:
Expand All @@ -22,8 +25,8 @@ def finalize_options(self):

def get_tag(self):
python, abi, plat = _bdist_wheel.get_tag(self)
# Our python source is py2/3 compatible.
python, abi = 'py2.py3', 'none'
# Our python source is py3 compatible.
python, abi = 'py3', 'none'
return python, abi, plat

cmdclass['bdist_wheel'] = bdist_wheel
Expand Down Expand Up @@ -64,20 +67,16 @@ def get_tag(self):
"bson"],
test_suite="test",
license="Apache License, Version 2.0",
python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*",
python_requires=">=3.7",
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Operating System :: MacOS :: MacOS X",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
Expand Down
16 changes: 4 additions & 12 deletions bindings/python/test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
pymongo[aws]>=3.11
# cffi==1.14.3 was the last installable release on RHEL 6.3 with Python 3.4
cffi==1.14.3;python_version=="3.4"
cffi>=1.12.0,<2;python_version!="3.4"
# We test PyPy on RHEL 6.2 which has OpenSSL 1.0.1e
# Pin to cryptography 2.8.* until we figure our how to
# install cryptography with PyPy with a newer OpenSSL.
cryptography>=2,<2.9;platform_python_implementation=='PyPy'
cryptography>=2;platform_python_implementation!='PyPy'
mock;python_version=='2.7'
six>=1.5;python_version=='2.7'
requests_mock>=1.10
pymongo[aws]>=4
cffi>=1.12.0,<2
cryptography>=2
requests_mock>=1.10
11 changes: 1 addition & 10 deletions bindings/python/test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,4 @@
except ImportError:
pass

# Use assertRaisesRegex if available, otherwise use Python 2.7's
# deprecated assertRaisesRegexp, with a 'p'.
if not hasattr(unittest.TestCase, 'assertRaisesRegex'):
unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp

try:
from unittest import mock
except ImportError: # python 2
import mock

from unittest import mock
4 changes: 2 additions & 2 deletions bindings/python/test/test_mongocrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ def test_mongocrypt_options_validation(self):
MongoCryptOptions(invalid_kms_providers)
with self.assertRaisesRegex(
TypeError, r"kms_providers\['local'\]\['key'\] must be an "
r"instance of bytes or str \(unicode in Python 2\)"):
r"instance of bytes or str"):
MongoCryptOptions({'local': {'key': None}})
with self.assertRaisesRegex(
TypeError, r"kms_providers\['gcp'\]\['privateKey'\] must be an "
r"instance of bytes or str \(unicode in Python 2\)"):
r"instance of bytes or str"):
MongoCryptOptions({'gcp': {'email': "[email protected]",
"privateKey": None}})

Expand Down

0 comments on commit af95177

Please sign in to comment.