Drop python2 support

This commit is contained in:
Bastien Gérard 2020-03-08 14:58:21 +01:00
parent a0b803959c
commit 421e3f324f
19 changed files with 51 additions and 134 deletions

View File

@ -1,13 +1,10 @@
# For full coverage, we'd have to test all supported Python, MongoDB, and
# PyMongo combinations. However, that would result in an overly long build
# with a very large number of jobs, hence we only test a subset of all the
# combinations:
# * MongoDB v3.4 & the latest PyMongo v3.x is currently the "main" setup,
# tested against Python v2.7, v3.5, v3.6, v3.7, v3.8, PyPy and PyPy3.
# * Besides that, we test the lowest actively supported Python/MongoDB/PyMongo
# combination: MongoDB v3.4, PyMongo v3.4, Python v2.7.
# * MongoDB v3.6 is tested against Python v3.6, and PyMongo v3.6, v3.7, v3.8.
#
# combinations.
# * Python3.7, MongoDB v3.4 & the latest PyMongo v3.x is currently the "main" setup,
# Other combinations are tested. See below for the details or check the travis jobs
# We should periodically check MongoDB Server versions supported by MongoDB
# Inc., add newly released versions to the test matrix, and remove versions
# which have reached their End of Life. See:
@ -16,21 +13,15 @@
#
# Reminder: Update README.rst if you change MongoDB versions we test.
language: python
dist: xenial
python:
- 2.7
- 3.5
- 3.6
- 3.7
- 3.8
- pypy
- pypy3
dist: xenial
dist: xenial
env:
global:
- MONGODB_3_4=3.4.17
@ -41,6 +32,8 @@ env:
- PYMONGO_3_6=3.6
- PYMONGO_3_9=3.9
- PYMONGO_3_10=3.10
- MAIN_PYTHON_VERSION = "3.7"
matrix:
- MONGODB=${MONGODB_3_4} PYMONGO=${PYMONGO_3_10}
@ -49,8 +42,6 @@ matrix:
fast_finish: true
include:
- python: 2.7
env: MONGODB=${MONGODB_3_4} PYMONGO=${PYMONGO_3_4}
- python: 3.7
env: MONGODB=${MONGODB_3_6} PYMONGO=${PYMONGO_3_6}
- python: 3.7
@ -74,20 +65,20 @@ install:
# tox dryrun to setup the tox venv (we run a mock test).
- tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -a "-k=test_ci_placeholder"
# Install black for Python v3.7 only.
- if [[ $TRAVIS_PYTHON_VERSION == '3.7' ]]; then pip install black; fi
- if [[ $TRAVIS_PYTHON_VERSION == $MAIN_PYTHON_VERSION ]]; then pip install black; fi
before_script:
- mkdir ${PWD}/mongodb-linux-x86_64-${MONGODB}/data
- ${PWD}/mongodb-linux-x86_64-${MONGODB}/bin/mongod --dbpath ${PWD}/mongodb-linux-x86_64-${MONGODB}/data --logpath ${PWD}/mongodb-linux-x86_64-${MONGODB}/mongodb.log --fork
- if [[ $TRAVIS_PYTHON_VERSION == '3.7' ]]; then flake8 .; else echo "flake8 only runs on py37"; fi # Run flake8 for Python 3.7 only
- if [[ $TRAVIS_PYTHON_VERSION == '3.7' ]]; then black --check .; else echo "black only runs on py37"; fi # Run black for Python 3.7 only
- if [[ $TRAVIS_PYTHON_VERSION == $MAIN_PYTHON_VERSION ]]; then flake8 .; else echo "flake8 only runs on py37"; fi
- if [[ $TRAVIS_PYTHON_VERSION == $MAIN_PYTHON_VERSION ]]; then black --check .; else echo "black only runs on py37"; fi
- mongo --eval 'db.version();' # Make sure mongo is awake
script:
- tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -a "--cov=mongoengine"
after_success:
- coveralls --verbose
- - if [[ $TRAVIS_PYTHON_VERSION == $MAIN_PYTHON_VERSION ]]; then coveralls --verbose; else echo "coveralls only sent for py37"; fi
notifications:
irc: irc.freenode.org#mongoengine
@ -109,11 +100,11 @@ deploy:
distributions: "sdist bdist_wheel"
# Only deploy on tagged commits (aka GitHub releases) and only for the parent
# repo's builds running Python v2.7 along with PyMongo v3.x and MongoDB v3.4.
# repo's builds running Python v3.7 along with PyMongo v3.x and MongoDB v3.4.
# We run Travis against many different Python, PyMongo, and MongoDB versions
# and we don't want the deploy to occur multiple times).
on:
tags: true
repo: MongoEngine/mongoengine
condition: ($PYMONGO = ${PYMONGO_3_10}) && ($MONGODB = ${MONGODB_3_4})
python: 2.7
python: 3.7

View File

@ -20,23 +20,23 @@ post to the `user group <http://groups.google.com/group/mongoengine-users>`
Supported Interpreters
----------------------
MongoEngine supports CPython 2.7 and newer. Language
features not supported by all interpreters can not be used.
MongoEngine supports CPython 3.7 and newer as well as Pypy3.
Language features not supported by all interpreters can not be used.
Python 2/3 compatibility
Python3 codebase
----------------------
The codebase is written in a compatible manner for python 2 & 3 so it
is important that this is taken into account when it comes to discrepencies
between the two versions (see https://python-future.org/compatible_idioms.html).
Travis runs the tests against different Python versions as a safety net.
Since 0.20, the codebase is exclusively Python 3.
Earlier versions were exclusively Python2, and was relying on 2to3 to support Python3 installs.
Travis runs the tests against the main Python 3.x versions.
Style Guide
-----------
MongoEngine uses `black <https://github.com/python/black>`_ for code
formatting.
MongoEngine uses `black <https://github.com/python/black>`_ for code formatting.
Black runs as part of the CI so it will fail in case the code is not formatted properly.
Testing
-------

View File

@ -3,8 +3,6 @@ import timeit
def main():
setup = """
from builtins import range
from pymongo import MongoClient
connection = MongoClient()
@ -59,8 +57,6 @@ myNoddys = noddy.find()
print("{}s".format(t.timeit(1)))
setup = """
from builtins import range
from pymongo import MongoClient
connection = MongoClient()

View File

@ -1,7 +1,6 @@
import weakref
from bson import DBRef
from future.utils import listitems
import six
from six import iteritems
@ -181,19 +180,6 @@ class BaseList(list):
__iadd__ = mark_as_changed_wrapper(list.__iadd__)
__imul__ = mark_as_changed_wrapper(list.__imul__)
if six.PY2:
# Under py3 __setslice__, __delslice__ and __getslice__
# are replaced by __setitem__, __delitem__ and __getitem__ with a slice as parameter
# so we mimic this under python 2
def __setslice__(self, i, j, sequence):
return self.__setitem__(slice(i, j), sequence)
def __delslice__(self, i, j):
return self.__delitem__(slice(i, j))
def __getslice__(self, i, j):
return self.__getitem__(slice(i, j))
def _mark_as_changed(self, key=None):
if hasattr(self._instance, "_mark_as_changed"):
if key:
@ -426,7 +412,7 @@ class StrictDict(object):
return len(list(iteritems(self)))
def __eq__(self, other):
return listitems(self) == listitems(other)
return list(self.items()) == list(other.items())
def __ne__(self, other):
return not (self == other)

View File

@ -1,9 +1,9 @@
import copy
import numbers
from functools import partial
from bson import DBRef, ObjectId, SON, json_util
from future.utils import listitems
import pymongo
import six
from six import iteritems
@ -26,7 +26,6 @@ from mongoengine.errors import (
OperationError,
ValidationError,
)
from mongoengine.python_support import Hashable
__all__ = ("BaseDocument", "NON_FIELD_ERRORS")
@ -294,10 +293,7 @@ class BaseDocument(object):
def __str__(self):
# TODO this could be simpler?
if hasattr(self, "__unicode__"):
if six.PY3:
return self.__unicode__()
else:
return six.text_type(self).encode("utf-8")
return self.__unicode__()
return six.text_type("%s object" % self.__class__.__name__)
def __eq__(self, other):
@ -671,7 +667,7 @@ class BaseDocument(object):
del set_data["_id"]
# Determine if any changed items were actually unset.
for path, value in listitems(set_data):
for path, value in list(set_data.items()):
if value or isinstance(
value, (numbers.Number, bool)
): # Account for 0 and True that are truthy

View File

@ -5,12 +5,12 @@ import re
import socket
import time
import uuid
from io import BytesIO
from operator import itemgetter
from bson import Binary, DBRef, ObjectId, SON
from bson.int64 import Int64
import gridfs
from past.builtins import long
import pymongo
from pymongo import ReturnDocument
import six
@ -39,7 +39,6 @@ from mongoengine.connection import DEFAULT_CONNECTION_NAME, get_db
from mongoengine.document import Document, EmbeddedDocument
from mongoengine.errors import DoesNotExist, InvalidQueryError, ValidationError
from mongoengine.mongodb_support import MONGODB_36, get_mongodb_version
from mongoengine.python_support import StringIO
from mongoengine.queryset import DO_NOTHING
from mongoengine.queryset.base import BaseQuerySet
from mongoengine.queryset.transform import STRING_OPERATORS
@ -338,7 +337,7 @@ class IntField(BaseField):
class LongField(BaseField):
"""64-bit integer field."""
"""64-bit integer field. (Equivalent to IntField since the support to Python2 was dropped)"""
def __init__(self, min_value=None, max_value=None, **kwargs):
self.min_value, self.max_value = min_value, max_value
@ -346,7 +345,7 @@ class LongField(BaseField):
def to_python(self, value):
try:
value = long(value)
value = int(value)
except (TypeError, ValueError):
pass
return value
@ -356,7 +355,7 @@ class LongField(BaseField):
def validate(self, value):
try:
value = long(value)
value = int(value)
except (TypeError, ValueError):
self.error("%s could not be converted to long" % value)
@ -370,7 +369,7 @@ class LongField(BaseField):
if value is None:
return value
return super(LongField, self).prepare_query_value(op, long(value))
return super(LongField, self).prepare_query_value(op, int(value))
class FloatField(BaseField):
@ -1679,8 +1678,6 @@ class GridFSProxy(object):
def __bool__(self):
return bool(self.grid_id)
__nonzero__ = __bool__ # For Py2 support
def __getstate__(self):
self_dict = self.__dict__
self_dict["_fs"] = None
@ -1952,7 +1949,7 @@ class ImageGridFsProxy(GridFSProxy):
w, h = img.size
io = StringIO()
io = BytesIO()
img.save(io, img_format, progressive=progressive)
io.seek(0)
@ -1971,7 +1968,7 @@ class ImageGridFsProxy(GridFSProxy):
def _put_thumbnail(self, thumbnail, format, progressive, **kwargs):
w, h = thumbnail.size
io = StringIO()
io = BytesIO()
thumbnail.save(io, format, progressive=progressive)
io.seek(0)

View File

@ -1,23 +0,0 @@
"""
Helper functions, constants, and types to aid with Python v2.7 - v3.x support
"""
import six
# six.BytesIO resolves to StringIO.StringIO in Py2 and io.BytesIO in Py3.
StringIO = six.BytesIO
# Additionally for Py2, try to use the faster cStringIO, if available
if not six.PY3:
try:
import cStringIO
except ImportError:
pass
else:
StringIO = cStringIO.StringIO
if six.PY3:
from collections.abc import Hashable
else:
# raises DeprecationWarnings in Python >=3.7
from collections import Hashable

View File

@ -1,5 +1,3 @@
from __future__ import absolute_import
import copy
import itertools
import re
@ -204,8 +202,6 @@ class BaseQuerySet(object):
"""Avoid to open all records in an if stmt in Py3."""
return self._has_data()
__nonzero__ = __bool__ # For Py2 support
# Core functions
def all(self):

View File

@ -69,8 +69,6 @@ class QueryFieldList(object):
def __bool__(self):
return bool(self.fields)
__nonzero__ = __bool__ # For Py2 support
def as_dict(self):
field_list = {field: self.value for field in self.fields}
if self.slice:

View File

@ -10,7 +10,7 @@ from mongoengine.base import UPDATE_OPERATORS
from mongoengine.common import _import_class
from mongoengine.errors import InvalidQueryError
__all__ = ("query", "update")
__all__ = ("query", "update", "STRING_OPERATORS")
COMPARISON_OPERATORS = (
"ne",

View File

@ -143,8 +143,6 @@ class QCombination(QNode):
def __bool__(self):
return bool(self.children)
__nonzero__ = __bool__ # For Py2 support
def accept(self, visitor):
for i in range(len(self.children)):
if isinstance(self.children[i], QNode):
@ -180,8 +178,6 @@ class Q(QNode):
def __bool__(self):
return bool(self.query)
__nonzero__ = __bool__ # For Py2 support
def __eq__(self, other):
return self.__class__ == other.__class__ and self.query == other.query

View File

@ -110,7 +110,6 @@ CLASSIFIERS = [
PYTHON_VERSION = sys.version_info[0]
PY3 = PYTHON_VERSION == 3
PY2 = PYTHON_VERSION == 2
extra_opts = {
"packages": find_packages(exclude=["tests", "tests.*"]),
@ -124,14 +123,11 @@ extra_opts = {
],
}
if PY3:
if "test" in sys.argv:
extra_opts["packages"] = find_packages()
extra_opts["package_data"] = {
"tests": ["fields/mongoengine.png", "fields/mongodb_leaf.png"]
}
else:
extra_opts["tests_require"] += ["python-dateutil"]
if "test" in sys.argv:
extra_opts["packages"] = find_packages()
extra_opts["package_data"] = {
"tests": ["fields/mongoengine.png", "fields/mongodb_leaf.png"]
}
setup(
name="mongoengine",
@ -148,7 +144,8 @@ setup(
long_description=LONG_DESCRIPTION,
platforms=["any"],
classifiers=CLASSIFIERS,
install_requires=["pymongo>=3.4, <4.0", "six>=1.10.0", "future"],
python_requires=">=3.5",
install_requires=["pymongo>=3.4, <4.0", "six>=1.10.0"],
cmdclass={"test": PyTest},
**extra_opts
)

View File

@ -123,10 +123,7 @@ class TestBinaryField(MongoDBTestCase):
upsert=True, new=True, set__bin_field=BIN_VALUE
)
assert doc.some_field == "test"
if six.PY3:
assert doc.bin_field == BIN_VALUE
else:
assert doc.bin_field == Binary(BIN_VALUE)
assert doc.bin_field == BIN_VALUE
def test_update_one(self):
"""Ensures no regression of bug #1127"""
@ -144,7 +141,4 @@ class TestBinaryField(MongoDBTestCase):
)
assert n_updated == 1
fetched = MyDocument.objects.with_id(doc.id)
if six.PY3:
assert fetched.bin_field == BIN_VALUE
else:
assert fetched.bin_field == Binary(BIN_VALUE)
assert fetched.bin_field == BIN_VALUE

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from builtins import str
import pytest
from mongoengine import (

View File

@ -3,6 +3,7 @@ import copy
import os
import tempfile
import unittest
from io import BytesIO
import gridfs
import pytest
@ -10,7 +11,6 @@ import six
from mongoengine import *
from mongoengine.connection import get_db
from mongoengine.python_support import StringIO
try:
from PIL import Image
@ -30,7 +30,7 @@ TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), "mongodb_leaf.png")
def get_file(path):
"""Use a BytesIO instead of a file to allow
to have a one-liner and avoid that the file remains opened"""
bytes_io = StringIO()
bytes_io = BytesIO()
with open(path, "rb") as f:
bytes_io.write(f.read())
bytes_io.seek(0)
@ -80,7 +80,7 @@ class TestFileField(MongoDBTestCase):
PutFile.drop_collection()
putfile = PutFile()
putstring = StringIO()
putstring = BytesIO()
putstring.write(text)
putstring.seek(0)
putfile.the_file.put(putstring, content_type=content_type)

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from builtins import str
import pytest
from mongoengine import *

View File

@ -4470,10 +4470,7 @@ class TestQueryset(unittest.TestCase):
pks = self.Person.objects.order_by("age").scalar("pk")[1:3]
names = self.Person.objects.scalar("name").in_bulk(list(pks)).values()
if six.PY3:
expected = "['A1', 'A2']"
else:
expected = "[u'A1', u'A2']"
expected = "['A1', 'A2']"
assert expected == "%s" % sorted(names)
def test_elem_match(self):

View File

@ -287,7 +287,7 @@ class TestBaseList:
base_list[:] = [
0,
1,
] # Will use __setslice__ under py2 and __setitem__ under py3
]
assert base_list._instance._changed_fields == ["my_name"]
assert base_list == [0, 1]
@ -296,13 +296,13 @@ class TestBaseList:
base_list[0:2] = [
1,
0,
] # Will use __setslice__ under py2 and __setitem__ under py3
]
assert base_list._instance._changed_fields == ["my_name"]
assert base_list == [1, 0, 2]
def test___setitem___calls_with_step_slice_mark_as_changed(self):
base_list = self._get_baselist([0, 1, 2])
base_list[0:3:2] = [-1, -2] # uses __setitem__ in both py2 & 3
base_list[0:3:2] = [-1, -2] # uses __setitem__
assert base_list._instance._changed_fields == ["my_name"]
assert base_list == [-1, 1, -2]

View File

@ -1,5 +1,5 @@
[tox]
envlist = {py27,py35,pypy,pypy3}-{mg34,mg36,mg39,mg310}
envlist = {py35,pypy3}-{mg34,mg36,mg39,mg310}
[testenv]
commands =