Merge pull request #2279 from bagerard/drop_py2_support
Drop python 2 support
This commit is contained in:
commit
ece63ad071
@ -5,17 +5,12 @@ pylint:
|
||||
|
||||
options:
|
||||
additional-builtins:
|
||||
# add xrange and long as valid built-ins. In Python 3, xrange is
|
||||
# translated into range and long is translated into int via 2to3 (see
|
||||
# "use_2to3" in setup.py). This should be removed when we drop Python
|
||||
# 2 support (which probably won't happen any time soon).
|
||||
- xrange
|
||||
# add long as valid built-ins.
|
||||
- long
|
||||
|
||||
pyflakes:
|
||||
disable:
|
||||
# undefined variables are already covered by pylint (and exclude
|
||||
# xrange & long)
|
||||
# undefined variables are already covered by pylint (and exclude long)
|
||||
- F821
|
||||
|
||||
ignore-paths:
|
||||
|
39
.travis.yml
39
.travis.yml
@ -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,19 +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
|
||||
|
||||
env:
|
||||
global:
|
||||
- MONGODB_3_4=3.4.17
|
||||
@ -39,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}
|
||||
|
||||
@ -47,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
|
||||
@ -67,28 +60,24 @@ install:
|
||||
- pip install --upgrade pip
|
||||
- pip install coveralls
|
||||
- pip install flake8 flake8-import-order
|
||||
- pip install tox # tox 3.11.0 has requirement virtualenv>=14.0.0
|
||||
- pip install virtualenv # virtualenv>=14.0.0 has dropped Python 3.2 support (and pypy3 is based on py32)
|
||||
- pip install tox
|
||||
# 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 == '2.7' ]]; then flake8 .; else echo "flake8 only runs on py27"; fi # Run flake8 for Python 2.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"
|
||||
|
||||
# For now only submit coveralls for Python v2.7. Python v3.x currently shows
|
||||
# 0% coverage. That's caused by 'use_2to3', which builds the py3-compatible
|
||||
# code in a separate dir and runs tests on that.
|
||||
after_success:
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then coveralls --verbose; else echo "coveralls only sent for py27"; fi
|
||||
- - if [[ $TRAVIS_PYTHON_VERSION == $MAIN_PYTHON_VERSION ]]; then coveralls --verbose; else echo "coveralls only sent for py37"; fi
|
||||
|
||||
notifications:
|
||||
irc: irc.freenode.org#mongoengine
|
||||
@ -110,11 +99,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
|
||||
|
@ -20,19 +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.
|
||||
The codebase is written in python 2 so you must be using python 2
|
||||
when developing new features. Compatibility of the library with Python 3
|
||||
relies on the 2to3 package that gets executed as part of the installation
|
||||
build. You should ensure that your code is properly converted by
|
||||
`2to3 <http://docs.python.org/library/2to3.html>`_.
|
||||
MongoEngine supports CPython 3.5 and newer as well as Pypy3.
|
||||
Language features not supported by all interpreters can not be used.
|
||||
|
||||
Python3 codebase
|
||||
----------------------
|
||||
|
||||
Since 0.20, the codebase is exclusively Python 3.
|
||||
|
||||
Earlier versions were exclusively Python2, and were 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
|
||||
-------
|
||||
|
@ -42,13 +42,14 @@ to both create the virtual environment and install the package. Otherwise, you c
|
||||
download the source from `GitHub <http://github.com/MongoEngine/mongoengine>`_ and
|
||||
run ``python setup.py install``.
|
||||
|
||||
The support for Python2 was dropped with MongoEngine 0.20.0
|
||||
|
||||
Dependencies
|
||||
============
|
||||
All of the dependencies can easily be installed via `pip <https://pip.pypa.io/>`_.
|
||||
At the very least, you'll need these two packages to use MongoEngine:
|
||||
|
||||
- pymongo>=3.4
|
||||
- six>=1.10.0
|
||||
|
||||
If you utilize a ``DateTimeField``, you might also use a more flexible date parser:
|
||||
|
||||
@ -58,6 +59,10 @@ If you need to use an ``ImageField`` or ``ImageGridFsProxy``:
|
||||
|
||||
- Pillow>=2.0.0
|
||||
|
||||
If you need to use signals:
|
||||
|
||||
- blinker>=1.3
|
||||
|
||||
Examples
|
||||
========
|
||||
Some simple examples of what MongoEngine code looks like:
|
||||
|
@ -4,12 +4,14 @@ import timeit
|
||||
def main():
|
||||
setup = """
|
||||
from pymongo import MongoClient
|
||||
|
||||
connection = MongoClient()
|
||||
connection.drop_database('mongoengine_benchmark_test')
|
||||
"""
|
||||
|
||||
stmt = """
|
||||
from pymongo import MongoClient
|
||||
|
||||
connection = MongoClient()
|
||||
|
||||
db = connection.mongoengine_benchmark_test
|
||||
@ -56,6 +58,7 @@ myNoddys = noddy.find()
|
||||
|
||||
setup = """
|
||||
from pymongo import MongoClient
|
||||
|
||||
connection = MongoClient()
|
||||
connection.drop_database('mongoengine_benchmark_test')
|
||||
connection.close()
|
||||
|
@ -6,6 +6,7 @@ Changelog
|
||||
Development
|
||||
===========
|
||||
- (Fill this out as you fix issues and develop your features).
|
||||
- ATTENTION: Drop support for Python2
|
||||
- Add Mongo 4.0 to Travis
|
||||
- Bump development Status classifier to Production/Stable #2232
|
||||
- Improve Queryset.get to avoid confusing MultipleObjectsReturned message in case multiple match are found #630
|
||||
|
@ -153,7 +153,7 @@ inherited classes like so: ::
|
||||
|
||||
# 4. Remove indexes
|
||||
info = collection.index_information()
|
||||
indexes_to_drop = [key for key, value in info.iteritems()
|
||||
indexes_to_drop = [key for key, value in info.items()
|
||||
if '_types' in dict(value['key'])]
|
||||
for index in indexes_to_drop:
|
||||
collection.drop_index(index)
|
||||
|
@ -1,8 +1,6 @@
|
||||
import weakref
|
||||
|
||||
from bson import DBRef
|
||||
import six
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine.common import _import_class
|
||||
from mongoengine.errors import DoesNotExist, MultipleObjectsReturned
|
||||
@ -53,7 +51,7 @@ class BaseDict(dict):
|
||||
if isinstance(instance, BaseDocument):
|
||||
self._instance = weakref.proxy(instance)
|
||||
self._name = name
|
||||
super(BaseDict, self).__init__(dict_items)
|
||||
super().__init__(dict_items)
|
||||
|
||||
def get(self, key, default=None):
|
||||
# get does not use __getitem__ by default so we must override it as well
|
||||
@ -63,18 +61,18 @@ class BaseDict(dict):
|
||||
return default
|
||||
|
||||
def __getitem__(self, key):
|
||||
value = super(BaseDict, self).__getitem__(key)
|
||||
value = super().__getitem__(key)
|
||||
|
||||
EmbeddedDocument = _import_class("EmbeddedDocument")
|
||||
if isinstance(value, EmbeddedDocument) and value._instance is None:
|
||||
value._instance = self._instance
|
||||
elif isinstance(value, dict) and not isinstance(value, BaseDict):
|
||||
value = BaseDict(value, None, "%s.%s" % (self._name, key))
|
||||
super(BaseDict, self).__setitem__(key, value)
|
||||
value = BaseDict(value, None, "{}.{}".format(self._name, key))
|
||||
super().__setitem__(key, value)
|
||||
value._instance = self._instance
|
||||
elif isinstance(value, list) and not isinstance(value, BaseList):
|
||||
value = BaseList(value, None, "%s.%s" % (self._name, key))
|
||||
super(BaseDict, self).__setitem__(key, value)
|
||||
value = BaseList(value, None, "{}.{}".format(self._name, key))
|
||||
super().__setitem__(key, value)
|
||||
value._instance = self._instance
|
||||
return value
|
||||
|
||||
@ -99,7 +97,7 @@ class BaseDict(dict):
|
||||
def _mark_as_changed(self, key=None):
|
||||
if hasattr(self._instance, "_mark_as_changed"):
|
||||
if key:
|
||||
self._instance._mark_as_changed("%s.%s" % (self._name, key))
|
||||
self._instance._mark_as_changed("{}.{}".format(self._name, key))
|
||||
else:
|
||||
self._instance._mark_as_changed(self._name)
|
||||
|
||||
@ -117,13 +115,13 @@ class BaseList(list):
|
||||
if isinstance(instance, BaseDocument):
|
||||
self._instance = weakref.proxy(instance)
|
||||
self._name = name
|
||||
super(BaseList, self).__init__(list_items)
|
||||
super().__init__(list_items)
|
||||
|
||||
def __getitem__(self, key):
|
||||
# change index to positive value because MongoDB does not support negative one
|
||||
if isinstance(key, int) and key < 0:
|
||||
key = len(self) + key
|
||||
value = super(BaseList, self).__getitem__(key)
|
||||
value = super().__getitem__(key)
|
||||
|
||||
if isinstance(key, slice):
|
||||
# When receiving a slice operator, we don't convert the structure and bind
|
||||
@ -135,19 +133,18 @@ class BaseList(list):
|
||||
value._instance = self._instance
|
||||
elif isinstance(value, dict) and not isinstance(value, BaseDict):
|
||||
# Replace dict by BaseDict
|
||||
value = BaseDict(value, None, "%s.%s" % (self._name, key))
|
||||
super(BaseList, self).__setitem__(key, value)
|
||||
value = BaseDict(value, None, "{}.{}".format(self._name, key))
|
||||
super().__setitem__(key, value)
|
||||
value._instance = self._instance
|
||||
elif isinstance(value, list) and not isinstance(value, BaseList):
|
||||
# Replace list by BaseList
|
||||
value = BaseList(value, None, "%s.%s" % (self._name, key))
|
||||
super(BaseList, self).__setitem__(key, value)
|
||||
value = BaseList(value, None, "{}.{}".format(self._name, key))
|
||||
super().__setitem__(key, value)
|
||||
value._instance = self._instance
|
||||
return value
|
||||
|
||||
def __iter__(self):
|
||||
for v in super(BaseList, self).__iter__():
|
||||
yield v
|
||||
yield from super().__iter__()
|
||||
|
||||
def __getstate__(self):
|
||||
self.instance = None
|
||||
@ -165,7 +162,7 @@ class BaseList(list):
|
||||
# instead, we simply marks the whole list as changed
|
||||
changed_key = None
|
||||
|
||||
result = super(BaseList, self).__setitem__(key, value)
|
||||
result = super().__setitem__(key, value)
|
||||
self._mark_as_changed(changed_key)
|
||||
return result
|
||||
|
||||
@ -180,30 +177,19 @@ 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:
|
||||
self._instance._mark_as_changed("%s.%s" % (self._name, key % len(self)))
|
||||
self._instance._mark_as_changed(
|
||||
"{}.{}".format(self._name, key % len(self))
|
||||
)
|
||||
else:
|
||||
self._instance._mark_as_changed(self._name)
|
||||
|
||||
|
||||
class EmbeddedDocumentList(BaseList):
|
||||
def __init__(self, list_items, instance, name):
|
||||
super(EmbeddedDocumentList, self).__init__(list_items, instance, name)
|
||||
super().__init__(list_items, instance, name)
|
||||
self._instance = instance
|
||||
|
||||
@classmethod
|
||||
@ -213,7 +199,7 @@ class EmbeddedDocumentList(BaseList):
|
||||
"""
|
||||
for key, expected_value in kwargs.items():
|
||||
doc_val = getattr(embedded_doc, key)
|
||||
if doc_val != expected_value and six.text_type(doc_val) != expected_value:
|
||||
if doc_val != expected_value and str(doc_val) != expected_value:
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -368,13 +354,13 @@ class EmbeddedDocumentList(BaseList):
|
||||
return len(values)
|
||||
|
||||
|
||||
class StrictDict(object):
|
||||
class StrictDict:
|
||||
__slots__ = ()
|
||||
_special_fields = {"get", "pop", "iteritems", "items", "keys", "create"}
|
||||
_classes = {}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for k, v in iteritems(kwargs):
|
||||
for k, v in kwargs.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
def __getitem__(self, key):
|
||||
@ -422,13 +408,13 @@ class StrictDict(object):
|
||||
return (key for key in self.__slots__ if hasattr(self, key))
|
||||
|
||||
def __len__(self):
|
||||
return len(list(iteritems(self)))
|
||||
return len(list(self.items()))
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.items() == other.items()
|
||||
return list(self.items()) == list(other.items())
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.items() != other.items()
|
||||
return not (self == other)
|
||||
|
||||
@classmethod
|
||||
def create(cls, allowed_keys):
|
||||
@ -443,7 +429,7 @@ class StrictDict(object):
|
||||
|
||||
def __repr__(self):
|
||||
return "{%s}" % ", ".join(
|
||||
'"{0!s}": {1!r}'.format(k, v) for k, v in self.items()
|
||||
'"{!s}": {!r}'.format(k, v) for k, v in self.items()
|
||||
)
|
||||
|
||||
cls._classes[allowed_keys] = SpecificStrictDict
|
||||
@ -468,9 +454,7 @@ class LazyReference(DBRef):
|
||||
self.document_type = document_type
|
||||
self._cached_doc = cached_doc
|
||||
self.passthrough = passthrough
|
||||
super(LazyReference, self).__init__(
|
||||
self.document_type._get_collection_name(), pk
|
||||
)
|
||||
super().__init__(self.document_type._get_collection_name(), pk)
|
||||
|
||||
def __getitem__(self, name):
|
||||
if not self.passthrough:
|
||||
@ -488,4 +472,4 @@ class LazyReference(DBRef):
|
||||
raise AttributeError()
|
||||
|
||||
def __repr__(self):
|
||||
return "<LazyReference(%s, %r)>" % (self.document_type, self.pk)
|
||||
return "<LazyReference({}, {!r})>".format(self.document_type, self.pk)
|
||||
|
@ -1,11 +1,10 @@
|
||||
import copy
|
||||
|
||||
import numbers
|
||||
from functools import partial
|
||||
|
||||
from bson import DBRef, ObjectId, SON, json_util
|
||||
import pymongo
|
||||
import six
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine import signals
|
||||
from mongoengine.base.common import get_document
|
||||
@ -25,14 +24,13 @@ from mongoengine.errors import (
|
||||
OperationError,
|
||||
ValidationError,
|
||||
)
|
||||
from mongoengine.python_support import Hashable
|
||||
|
||||
__all__ = ("BaseDocument", "NON_FIELD_ERRORS")
|
||||
|
||||
NON_FIELD_ERRORS = "__all__"
|
||||
|
||||
|
||||
class BaseDocument(object):
|
||||
class BaseDocument:
|
||||
# TODO simplify how `_changed_fields` is used.
|
||||
# Currently, handling of `_changed_fields` seems unnecessarily convoluted:
|
||||
# 1. `BaseDocument` defines `_changed_fields` in its `__slots__`, yet it's
|
||||
@ -92,10 +90,10 @@ class BaseDocument(object):
|
||||
# if so raise an Exception.
|
||||
if not self._dynamic and (self._meta.get("strict", True) or _created):
|
||||
_undefined_fields = set(values.keys()) - set(
|
||||
self._fields.keys() + ["id", "pk", "_cls", "_text_score"]
|
||||
list(self._fields.keys()) + ["id", "pk", "_cls", "_text_score"]
|
||||
)
|
||||
if _undefined_fields:
|
||||
msg = ('The fields "{0}" do not exist on the document "{1}"').format(
|
||||
msg = ('The fields "{}" do not exist on the document "{}"').format(
|
||||
_undefined_fields, self._class_name
|
||||
)
|
||||
raise FieldDoesNotExist(msg)
|
||||
@ -110,7 +108,7 @@ class BaseDocument(object):
|
||||
# Assign default values to the instance.
|
||||
# We set default values only for fields loaded from DB. See
|
||||
# https://github.com/mongoengine/mongoengine/issues/399 for more info.
|
||||
for key, field in iteritems(self._fields):
|
||||
for key, field in self._fields.items():
|
||||
if self._db_field_map.get(key, key) in __only_fields:
|
||||
continue
|
||||
value = getattr(self, key, None)
|
||||
@ -122,14 +120,14 @@ class BaseDocument(object):
|
||||
# Set passed values after initialisation
|
||||
if self._dynamic:
|
||||
dynamic_data = {}
|
||||
for key, value in iteritems(values):
|
||||
for key, value in values.items():
|
||||
if key in self._fields or key == "_id":
|
||||
setattr(self, key, value)
|
||||
else:
|
||||
dynamic_data[key] = value
|
||||
else:
|
||||
FileField = _import_class("FileField")
|
||||
for key, value in iteritems(values):
|
||||
for key, value in values.items():
|
||||
key = self._reverse_db_field_map.get(key, key)
|
||||
if key in self._fields or key in ("id", "pk", "_cls"):
|
||||
if __auto_convert and value is not None:
|
||||
@ -145,7 +143,7 @@ class BaseDocument(object):
|
||||
|
||||
if self._dynamic:
|
||||
self._dynamic_lock = False
|
||||
for key, value in iteritems(dynamic_data):
|
||||
for key, value in dynamic_data.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
# Flag initialised
|
||||
@ -163,7 +161,7 @@ class BaseDocument(object):
|
||||
default = default()
|
||||
setattr(self, field_name, default)
|
||||
else:
|
||||
super(BaseDocument, self).__delattr__(*args, **kwargs)
|
||||
super().__delattr__(*args, **kwargs)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
# Handle dynamic data only if an initialised dynamic document
|
||||
@ -210,9 +208,9 @@ class BaseDocument(object):
|
||||
and self__created
|
||||
and name == self._meta.get("id_field")
|
||||
):
|
||||
super(BaseDocument, self).__setattr__("_created", False)
|
||||
super().__setattr__("_created", False)
|
||||
|
||||
super(BaseDocument, self).__setattr__(name, value)
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def __getstate__(self):
|
||||
data = {}
|
||||
@ -288,16 +286,13 @@ class BaseDocument(object):
|
||||
except (UnicodeEncodeError, UnicodeDecodeError):
|
||||
u = "[Bad Unicode data]"
|
||||
repr_type = str if u is None else type(u)
|
||||
return repr_type("<%s: %s>" % (self.__class__.__name__, u))
|
||||
return repr_type("<{}: {}>".format(self.__class__.__name__, u))
|
||||
|
||||
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 six.text_type("%s object" % self.__class__.__name__)
|
||||
return self.__unicode__()
|
||||
return "%s object" % self.__class__.__name__
|
||||
|
||||
def __eq__(self, other):
|
||||
if (
|
||||
@ -446,7 +441,7 @@ class BaseDocument(object):
|
||||
pk = self.pk
|
||||
elif self._instance and hasattr(self._instance, "pk"):
|
||||
pk = self._instance.pk
|
||||
message = "ValidationError (%s:%s) " % (self._class_name, pk)
|
||||
message = "ValidationError ({}:{}) ".format(self._class_name, pk)
|
||||
raise ValidationError(message, errors=errors)
|
||||
|
||||
def to_json(self, *args, **kwargs):
|
||||
@ -519,7 +514,7 @@ class BaseDocument(object):
|
||||
if "." in key:
|
||||
key, rest = key.split(".", 1)
|
||||
key = self._db_field_map.get(key, key)
|
||||
key = "%s.%s" % (key, rest)
|
||||
key = "{}.{}".format(key, rest)
|
||||
else:
|
||||
key = self._db_field_map.get(key, key)
|
||||
|
||||
@ -578,10 +573,10 @@ class BaseDocument(object):
|
||||
if not hasattr(data, "items"):
|
||||
iterator = enumerate(data)
|
||||
else:
|
||||
iterator = iteritems(data)
|
||||
iterator = data.items()
|
||||
|
||||
for index_or_key, value in iterator:
|
||||
item_key = "%s%s." % (base_key, index_or_key)
|
||||
item_key = "{}{}.".format(base_key, index_or_key)
|
||||
# don't check anything lower if this key is already marked
|
||||
# as changed.
|
||||
if item_key[:-1] in changed_fields:
|
||||
@ -589,7 +584,7 @@ class BaseDocument(object):
|
||||
|
||||
if hasattr(value, "_get_changed_fields"):
|
||||
changed = value._get_changed_fields()
|
||||
changed_fields += ["%s%s" % (item_key, k) for k in changed if k]
|
||||
changed_fields += ["{}{}".format(item_key, k) for k in changed if k]
|
||||
elif isinstance(value, (list, tuple, dict)):
|
||||
self._nestable_types_changed_fields(changed_fields, item_key, value)
|
||||
|
||||
@ -620,7 +615,7 @@ class BaseDocument(object):
|
||||
if isinstance(data, EmbeddedDocument):
|
||||
# Find all embedded fields that have been changed
|
||||
changed = data._get_changed_fields()
|
||||
changed_fields += ["%s%s" % (key, k) for k in changed if k]
|
||||
changed_fields += ["{}{}".format(key, k) for k in changed if k]
|
||||
elif isinstance(data, (list, tuple, dict)):
|
||||
if hasattr(field, "field") and isinstance(
|
||||
field.field, (ReferenceField, GenericReferenceField)
|
||||
@ -670,7 +665,7 @@ class BaseDocument(object):
|
||||
del set_data["_id"]
|
||||
|
||||
# Determine if any changed items were actually unset.
|
||||
for path, value in set_data.items():
|
||||
for path, value in list(set_data.items()):
|
||||
if value or isinstance(
|
||||
value, (numbers.Number, bool)
|
||||
): # Account for 0 and True that are truthy
|
||||
@ -744,7 +739,7 @@ class BaseDocument(object):
|
||||
# Convert SON to a data dict, making sure each key is a string and
|
||||
# corresponds to the right db field.
|
||||
data = {}
|
||||
for key, value in iteritems(son):
|
||||
for key, value in son.items():
|
||||
key = str(key)
|
||||
key = cls._db_field_map.get(key, key)
|
||||
data[key] = value
|
||||
@ -759,7 +754,7 @@ class BaseDocument(object):
|
||||
if not _auto_dereference:
|
||||
fields = copy.deepcopy(fields)
|
||||
|
||||
for field_name, field in iteritems(fields):
|
||||
for field_name, field in fields.items():
|
||||
field._auto_dereference = _auto_dereference
|
||||
if field.db_field in data:
|
||||
value = data[field.db_field]
|
||||
@ -774,17 +769,16 @@ class BaseDocument(object):
|
||||
|
||||
if errors_dict:
|
||||
errors = "\n".join(
|
||||
["Field '%s' - %s" % (k, v) for k, v in errors_dict.items()]
|
||||
["Field '{}' - {}".format(k, v) for k, v in errors_dict.items()]
|
||||
)
|
||||
msg = "Invalid data to create a `%s` instance.\n%s" % (
|
||||
cls._class_name,
|
||||
errors,
|
||||
msg = "Invalid data to create a `{}` instance.\n{}".format(
|
||||
cls._class_name, errors,
|
||||
)
|
||||
raise InvalidDocumentError(msg)
|
||||
|
||||
# In STRICT documents, remove any keys that aren't in cls._fields
|
||||
if cls.STRICT:
|
||||
data = {k: v for k, v in iteritems(data) if k in cls._fields}
|
||||
data = {k: v for k, v in data.items() if k in cls._fields}
|
||||
|
||||
obj = cls(
|
||||
__auto_convert=False, _created=created, __only_fields=only_fields, **data
|
||||
@ -831,7 +825,7 @@ class BaseDocument(object):
|
||||
@classmethod
|
||||
def _build_index_spec(cls, spec):
|
||||
"""Build a PyMongo index spec from a MongoEngine index spec."""
|
||||
if isinstance(spec, six.string_types):
|
||||
if isinstance(spec, str):
|
||||
spec = {"fields": [spec]}
|
||||
elif isinstance(spec, (list, tuple)):
|
||||
spec = {"fields": list(spec)}
|
||||
@ -928,7 +922,7 @@ class BaseDocument(object):
|
||||
|
||||
# Add any unique_with fields to the back of the index spec
|
||||
if field.unique_with:
|
||||
if isinstance(field.unique_with, six.string_types):
|
||||
if isinstance(field.unique_with, str):
|
||||
field.unique_with = [field.unique_with]
|
||||
|
||||
# Convert unique_with field names to real field names
|
||||
@ -949,7 +943,8 @@ class BaseDocument(object):
|
||||
|
||||
# Add the new index to the list
|
||||
fields = [
|
||||
("%s%s" % (namespace, f), pymongo.ASCENDING) for f in unique_fields
|
||||
("{}{}".format(namespace, f), pymongo.ASCENDING)
|
||||
for f in unique_fields
|
||||
]
|
||||
index = {"fields": fields, "unique": True, "sparse": sparse}
|
||||
unique_indexes.append(index)
|
||||
@ -1006,7 +1001,7 @@ class BaseDocument(object):
|
||||
elif field._geo_index:
|
||||
field_name = field.db_field
|
||||
if parent_field:
|
||||
field_name = "%s.%s" % (parent_field, field_name)
|
||||
field_name = "{}.{}".format(parent_field, field_name)
|
||||
geo_indices.append({"fields": [(field_name, field._geo_index)]})
|
||||
|
||||
return geo_indices
|
||||
@ -1175,9 +1170,6 @@ class BaseDocument(object):
|
||||
else [value]
|
||||
)
|
||||
return sep.join(
|
||||
[
|
||||
six.text_type(dict(field.choices).get(val, val))
|
||||
for val in values or []
|
||||
]
|
||||
[str(dict(field.choices).get(val, val)) for val in values or []]
|
||||
)
|
||||
return value
|
||||
|
@ -4,8 +4,6 @@ import weakref
|
||||
|
||||
from bson import DBRef, ObjectId, SON
|
||||
import pymongo
|
||||
import six
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine.base.common import UPDATE_OPERATORS
|
||||
from mongoengine.base.datastructures import BaseDict, BaseList, EmbeddedDocumentList
|
||||
@ -15,7 +13,7 @@ from mongoengine.errors import DeprecatedError, ValidationError
|
||||
__all__ = ("BaseField", "ComplexBaseField", "ObjectIdField", "GeoJsonBaseField")
|
||||
|
||||
|
||||
class BaseField(object):
|
||||
class BaseField:
|
||||
"""A base class for fields in a MongoDB document. Instances of this class
|
||||
may be added to subclasses of `Document` to define a document's schema.
|
||||
|
||||
@ -87,13 +85,11 @@ class BaseField(object):
|
||||
self._owner_document = None
|
||||
|
||||
# Make sure db_field is a string (if it's explicitly defined).
|
||||
if self.db_field is not None and not isinstance(
|
||||
self.db_field, six.string_types
|
||||
):
|
||||
if self.db_field is not None and not isinstance(self.db_field, str):
|
||||
raise TypeError("db_field should be a string.")
|
||||
|
||||
# Make sure db_field doesn't contain any forbidden characters.
|
||||
if isinstance(self.db_field, six.string_types) and (
|
||||
if isinstance(self.db_field, str) and (
|
||||
"." in self.db_field
|
||||
or "\0" in self.db_field
|
||||
or self.db_field.startswith("$")
|
||||
@ -216,14 +212,12 @@ class BaseField(object):
|
||||
# Choices which are other types of Documents
|
||||
if isinstance(value, (Document, EmbeddedDocument)):
|
||||
if not any(isinstance(value, c) for c in choice_list):
|
||||
self.error(
|
||||
"Value must be an instance of %s" % (six.text_type(choice_list))
|
||||
)
|
||||
self.error("Value must be an instance of %s" % (choice_list))
|
||||
# Choices which are types other than Documents
|
||||
else:
|
||||
values = value if isinstance(value, (list, tuple)) else [value]
|
||||
if len(set(values) - set(choice_list)):
|
||||
self.error("Value must be one of %s" % six.text_type(choice_list))
|
||||
self.error("Value must be one of %s" % str(choice_list))
|
||||
|
||||
def _validate(self, value, **kwargs):
|
||||
# Check the Choices Constraint
|
||||
@ -311,7 +305,7 @@ class ComplexBaseField(BaseField):
|
||||
if hasattr(instance._data[self.name], "_dereferenced"):
|
||||
instance._data[self.name]._dereferenced = True
|
||||
|
||||
value = super(ComplexBaseField, self).__get__(instance, owner)
|
||||
value = super().__get__(instance, owner)
|
||||
|
||||
# Convert lists / values so we can watch for any changes on them
|
||||
if isinstance(value, (list, tuple)):
|
||||
@ -340,7 +334,7 @@ class ComplexBaseField(BaseField):
|
||||
|
||||
def to_python(self, value):
|
||||
"""Convert a MongoDB-compatible type to a Python type."""
|
||||
if isinstance(value, six.string_types):
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
|
||||
if hasattr(value, "to_python"):
|
||||
@ -394,7 +388,7 @@ class ComplexBaseField(BaseField):
|
||||
EmbeddedDocument = _import_class("EmbeddedDocument")
|
||||
GenericReferenceField = _import_class("GenericReferenceField")
|
||||
|
||||
if isinstance(value, six.string_types):
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
|
||||
if hasattr(value, "to_mongo"):
|
||||
@ -418,11 +412,11 @@ class ComplexBaseField(BaseField):
|
||||
if self.field:
|
||||
value_dict = {
|
||||
key: self.field._to_mongo_safe_call(item, use_db_field, fields)
|
||||
for key, item in iteritems(value)
|
||||
for key, item in value.items()
|
||||
}
|
||||
else:
|
||||
value_dict = {}
|
||||
for k, v in iteritems(value):
|
||||
for k, v in value.items():
|
||||
if isinstance(v, Document):
|
||||
# We need the id from the saved object to create the DBRef
|
||||
if v.pk is None:
|
||||
@ -461,8 +455,8 @@ class ComplexBaseField(BaseField):
|
||||
"""If field is provided ensure the value is valid."""
|
||||
errors = {}
|
||||
if self.field:
|
||||
if hasattr(value, "iteritems") or hasattr(value, "items"):
|
||||
sequence = iteritems(value)
|
||||
if hasattr(value, "items"):
|
||||
sequence = value.items()
|
||||
else:
|
||||
sequence = enumerate(value)
|
||||
for k, v in sequence:
|
||||
@ -475,7 +469,9 @@ class ComplexBaseField(BaseField):
|
||||
|
||||
if errors:
|
||||
field_class = self.field.__class__.__name__
|
||||
self.error("Invalid %s item (%s)" % (field_class, value), errors=errors)
|
||||
self.error(
|
||||
"Invalid {} item ({})".format(field_class, value), errors=errors
|
||||
)
|
||||
# Don't allow empty values if required
|
||||
if self.required and not value:
|
||||
self.error("Field is required and cannot be empty")
|
||||
@ -508,10 +504,9 @@ class ObjectIdField(BaseField):
|
||||
def to_mongo(self, value):
|
||||
if not isinstance(value, ObjectId):
|
||||
try:
|
||||
return ObjectId(six.text_type(value))
|
||||
return ObjectId(str(value))
|
||||
except Exception as e:
|
||||
# e.message attribute has been deprecated since Python 2.6
|
||||
self.error(six.text_type(e))
|
||||
self.error(str(e))
|
||||
return value
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
@ -519,9 +514,9 @@ class ObjectIdField(BaseField):
|
||||
|
||||
def validate(self, value):
|
||||
try:
|
||||
ObjectId(six.text_type(value))
|
||||
ObjectId(str(value))
|
||||
except Exception:
|
||||
self.error("Invalid Object ID")
|
||||
self.error("Invalid ObjectID")
|
||||
|
||||
|
||||
class GeoJsonBaseField(BaseField):
|
||||
@ -541,14 +536,14 @@ class GeoJsonBaseField(BaseField):
|
||||
self._name = "%sField" % self._type
|
||||
if not auto_index:
|
||||
self._geo_index = False
|
||||
super(GeoJsonBaseField, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def validate(self, value):
|
||||
"""Validate the GeoJson object based on its type."""
|
||||
if isinstance(value, dict):
|
||||
if set(value.keys()) == {"type", "coordinates"}:
|
||||
if value["type"] != self._type:
|
||||
self.error('%s type must be "%s"' % (self._name, self._type))
|
||||
self.error('{} type must be "{}"'.format(self._name, self._type))
|
||||
return self.validate(value["coordinates"])
|
||||
else:
|
||||
self.error(
|
||||
|
@ -1,9 +1,6 @@
|
||||
import itertools
|
||||
import warnings
|
||||
|
||||
import six
|
||||
from six import iteritems, itervalues
|
||||
|
||||
from mongoengine.base.common import _document_registry
|
||||
from mongoengine.base.fields import BaseField, ComplexBaseField, ObjectIdField
|
||||
from mongoengine.common import _import_class
|
||||
@ -25,7 +22,7 @@ class DocumentMetaclass(type):
|
||||
# TODO lower complexity of this method
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
flattened_bases = mcs._get_bases(bases)
|
||||
super_new = super(DocumentMetaclass, mcs).__new__
|
||||
super_new = super().__new__
|
||||
|
||||
# If a base class just call super
|
||||
metaclass = attrs.get("my_metaclass")
|
||||
@ -69,7 +66,7 @@ class DocumentMetaclass(type):
|
||||
# Standard object mixin - merge in any Fields
|
||||
if not hasattr(base, "_meta"):
|
||||
base_fields = {}
|
||||
for attr_name, attr_value in iteritems(base.__dict__):
|
||||
for attr_name, attr_value in base.__dict__.items():
|
||||
if not isinstance(attr_value, BaseField):
|
||||
continue
|
||||
attr_value.name = attr_name
|
||||
@ -81,7 +78,7 @@ class DocumentMetaclass(type):
|
||||
|
||||
# Discover any document fields
|
||||
field_names = {}
|
||||
for attr_name, attr_value in iteritems(attrs):
|
||||
for attr_name, attr_value in attrs.items():
|
||||
if not isinstance(attr_value, BaseField):
|
||||
continue
|
||||
attr_value.name = attr_name
|
||||
@ -111,9 +108,7 @@ class DocumentMetaclass(type):
|
||||
|
||||
attrs["_fields_ordered"] = tuple(
|
||||
i[1]
|
||||
for i in sorted(
|
||||
(v.creation_counter, v.name) for v in itervalues(doc_fields)
|
||||
)
|
||||
for i in sorted((v.creation_counter, v.name) for v in doc_fields.values())
|
||||
)
|
||||
|
||||
#
|
||||
@ -173,24 +168,8 @@ class DocumentMetaclass(type):
|
||||
# Add class to the _document_registry
|
||||
_document_registry[new_class._class_name] = new_class
|
||||
|
||||
# In Python 2, User-defined methods objects have special read-only
|
||||
# attributes 'im_func' and 'im_self' which contain the function obj
|
||||
# and class instance object respectively. With Python 3 these special
|
||||
# attributes have been replaced by __func__ and __self__. The Blinker
|
||||
# module continues to use im_func and im_self, so the code below
|
||||
# copies __func__ into im_func and __self__ into im_self for
|
||||
# classmethod objects in Document derived classes.
|
||||
if six.PY3:
|
||||
for val in new_class.__dict__.values():
|
||||
if isinstance(val, classmethod):
|
||||
f = val.__get__(new_class)
|
||||
if hasattr(f, "__func__") and not hasattr(f, "im_func"):
|
||||
f.__dict__.update({"im_func": getattr(f, "__func__")})
|
||||
if hasattr(f, "__self__") and not hasattr(f, "im_self"):
|
||||
f.__dict__.update({"im_self": getattr(f, "__self__")})
|
||||
|
||||
# Handle delete rules
|
||||
for field in itervalues(new_class._fields):
|
||||
for field in new_class._fields.values():
|
||||
f = field
|
||||
if f.owner_document is None:
|
||||
f.owner_document = new_class
|
||||
@ -252,8 +231,7 @@ class DocumentMetaclass(type):
|
||||
if base is object:
|
||||
continue
|
||||
yield base
|
||||
for child_base in mcs.__get_bases(base.__bases__):
|
||||
yield child_base
|
||||
yield from mcs.__get_bases(base.__bases__)
|
||||
|
||||
@classmethod
|
||||
def _import_classes(mcs):
|
||||
@ -271,7 +249,7 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
||||
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
flattened_bases = mcs._get_bases(bases)
|
||||
super_new = super(TopLevelDocumentMetaclass, mcs).__new__
|
||||
super_new = super().__new__
|
||||
|
||||
# Set default _meta data if base class, otherwise get user defined meta
|
||||
if attrs.get("my_metaclass") == TopLevelDocumentMetaclass:
|
||||
@ -398,7 +376,7 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
||||
new_class.objects = QuerySetManager()
|
||||
|
||||
# Validate the fields and set primary key if needed
|
||||
for field_name, field in iteritems(new_class._fields):
|
||||
for field_name, field in new_class._fields.items():
|
||||
if field.primary_key:
|
||||
# Ensure only one primary key is set
|
||||
current_pk = new_class._meta.get("id_field")
|
||||
@ -461,8 +439,8 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
||||
|
||||
id_basename, id_db_basename, i = ("auto_id", "_auto_id", 0)
|
||||
for i in itertools.count():
|
||||
id_name = "{0}_{1}".format(id_basename, i)
|
||||
id_db_name = "{0}_{1}".format(id_db_basename, i)
|
||||
id_name = "{}_{}".format(id_basename, i)
|
||||
id_db_name = "{}_{}".format(id_db_basename, i)
|
||||
if id_name not in existing_fields and id_db_name not in existing_db_fields:
|
||||
return id_name, id_db_name
|
||||
|
||||
@ -475,7 +453,7 @@ class MetaDict(dict):
|
||||
_merge_options = ("indexes",)
|
||||
|
||||
def merge(self, new_options):
|
||||
for k, v in iteritems(new_options):
|
||||
for k, v in new_options.items():
|
||||
if k in self._merge_options:
|
||||
self[k] = self.get(k, []) + v
|
||||
else:
|
||||
|
@ -1,7 +1,7 @@
|
||||
import re
|
||||
|
||||
|
||||
class LazyRegexCompiler(object):
|
||||
class LazyRegexCompiler:
|
||||
"""Descriptor to allow lazy compilation of regex"""
|
||||
|
||||
def __init__(self, pattern, flags=0):
|
||||
|
@ -1,6 +1,5 @@
|
||||
from pymongo import MongoClient, ReadPreference, uri_parser
|
||||
from pymongo.database import _check_name
|
||||
import six
|
||||
|
||||
__all__ = [
|
||||
"DEFAULT_CONNECTION_NAME",
|
||||
@ -39,8 +38,8 @@ def _check_db_name(name):
|
||||
"""Check if a database name is valid.
|
||||
This functionality is copied from pymongo Database class constructor.
|
||||
"""
|
||||
if not isinstance(name, six.string_types):
|
||||
raise TypeError("name must be an instance of %s" % six.string_types)
|
||||
if not isinstance(name, str):
|
||||
raise TypeError("name must be an instance of %s" % str)
|
||||
elif name != "$external":
|
||||
_check_name(name)
|
||||
|
||||
@ -93,7 +92,7 @@ def _get_connection_settings(
|
||||
conn_host = conn_settings["host"]
|
||||
|
||||
# Host can be a list or a string, so if string, force to a list.
|
||||
if isinstance(conn_host, six.string_types):
|
||||
if isinstance(conn_host, str):
|
||||
conn_host = [conn_host]
|
||||
|
||||
resolved_hosts = []
|
||||
@ -148,7 +147,7 @@ def _get_connection_settings(
|
||||
# TODO simplify the code below once we drop support for
|
||||
# PyMongo v3.4.
|
||||
read_pf_mode = uri_options["readpreference"]
|
||||
if isinstance(read_pf_mode, six.string_types):
|
||||
if isinstance(read_pf_mode, str):
|
||||
read_pf_mode = read_pf_mode.lower()
|
||||
for preference in read_preferences:
|
||||
if (
|
||||
@ -318,7 +317,7 @@ def _create_connection(alias, connection_class, **connection_settings):
|
||||
try:
|
||||
return connection_class(**connection_settings)
|
||||
except Exception as e:
|
||||
raise ConnectionFailure("Cannot connect to database %s :\n%s" % (alias, e))
|
||||
raise ConnectionFailure("Cannot connect to database {} :\n{}".format(alias, e))
|
||||
|
||||
|
||||
def _find_existing_connection(connection_settings):
|
||||
@ -396,8 +395,8 @@ def connect(db=None, alias=DEFAULT_CONNECTION_NAME, **kwargs):
|
||||
|
||||
if new_conn_settings != prev_conn_setting:
|
||||
err_msg = (
|
||||
u"A different connection with alias `{}` was already "
|
||||
u"registered. Use disconnect() first"
|
||||
"A different connection with alias `{}` was already "
|
||||
"registered. Use disconnect() first"
|
||||
).format(alias)
|
||||
raise ConnectionFailure(err_msg)
|
||||
else:
|
||||
|
@ -1,7 +1,6 @@
|
||||
from contextlib import contextmanager
|
||||
|
||||
from pymongo.write_concern import WriteConcern
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine.common import _import_class
|
||||
from mongoengine.connection import DEFAULT_CONNECTION_NAME, get_db
|
||||
@ -17,7 +16,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class switch_db(object):
|
||||
class switch_db:
|
||||
"""switch_db alias context manager.
|
||||
|
||||
Example ::
|
||||
@ -58,7 +57,7 @@ class switch_db(object):
|
||||
self.cls._collection = self.collection
|
||||
|
||||
|
||||
class switch_collection(object):
|
||||
class switch_collection:
|
||||
"""switch_collection alias context manager.
|
||||
|
||||
Example ::
|
||||
@ -100,7 +99,7 @@ class switch_collection(object):
|
||||
self.cls._get_collection_name = self.ori_get_collection_name
|
||||
|
||||
|
||||
class no_dereference(object):
|
||||
class no_dereference:
|
||||
"""no_dereference context manager.
|
||||
|
||||
Turns off all dereferencing in Documents for the duration of the context
|
||||
@ -123,7 +122,7 @@ class no_dereference(object):
|
||||
|
||||
self.deref_fields = [
|
||||
k
|
||||
for k, v in iteritems(self.cls._fields)
|
||||
for k, v in self.cls._fields.items()
|
||||
if isinstance(v, (ReferenceField, GenericReferenceField, ComplexBaseField))
|
||||
]
|
||||
|
||||
@ -140,7 +139,7 @@ class no_dereference(object):
|
||||
return self.cls
|
||||
|
||||
|
||||
class no_sub_classes(object):
|
||||
class no_sub_classes:
|
||||
"""no_sub_classes context manager.
|
||||
|
||||
Only returns instances of this class and no sub (inherited) classes::
|
||||
@ -168,7 +167,7 @@ class no_sub_classes(object):
|
||||
self.cls._subclasses = self.cls_initial_subclasses
|
||||
|
||||
|
||||
class query_counter(object):
|
||||
class query_counter:
|
||||
"""Query_counter context manager to get the number of queries.
|
||||
This works by updating the `profiling_level` of the database so that all queries get logged,
|
||||
resetting the db.system.profile collection at the beginning of the context and counting the new entries.
|
||||
@ -235,7 +234,7 @@ class query_counter(object):
|
||||
|
||||
def __repr__(self):
|
||||
"""repr query_counter as the number of queries."""
|
||||
return u"%s" % self._get_count()
|
||||
return "%s" % self._get_count()
|
||||
|
||||
def _get_count(self):
|
||||
"""Get the number of queries by counting the current number of entries in db.system.profile
|
||||
|
@ -1,6 +1,4 @@
|
||||
from bson import DBRef, SON
|
||||
import six
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine.base import (
|
||||
BaseDict,
|
||||
@ -16,7 +14,7 @@ from mongoengine.fields import DictField, ListField, MapField, ReferenceField
|
||||
from mongoengine.queryset import QuerySet
|
||||
|
||||
|
||||
class DeReference(object):
|
||||
class DeReference:
|
||||
def __call__(self, items, max_depth=1, instance=None, name=None):
|
||||
"""
|
||||
Cheaply dereferences the items to a set depth.
|
||||
@ -30,7 +28,7 @@ class DeReference(object):
|
||||
:class:`~mongoengine.base.ComplexBaseField`
|
||||
:param get: A boolean determining if being called by __get__
|
||||
"""
|
||||
if items is None or isinstance(items, six.string_types):
|
||||
if items is None or isinstance(items, str):
|
||||
return items
|
||||
|
||||
# cheapest way to convert a queryset to a list
|
||||
@ -79,7 +77,7 @@ class DeReference(object):
|
||||
|
||||
def _get_items_from_dict(items):
|
||||
new_items = {}
|
||||
for k, v in iteritems(items):
|
||||
for k, v in items.items():
|
||||
value = v
|
||||
if isinstance(v, list):
|
||||
value = _get_items_from_list(v)
|
||||
@ -120,7 +118,7 @@ class DeReference(object):
|
||||
depth += 1
|
||||
for item in iterator:
|
||||
if isinstance(item, (Document, EmbeddedDocument)):
|
||||
for field_name, field in iteritems(item._fields):
|
||||
for field_name, field in item._fields.items():
|
||||
v = item._data.get(field_name, None)
|
||||
if isinstance(v, LazyReference):
|
||||
# LazyReference inherits DBRef but should not be dereferenced here !
|
||||
@ -136,7 +134,7 @@ class DeReference(object):
|
||||
getattr(field, "field", None), "document_type", None
|
||||
)
|
||||
references = self._find_references(v, depth)
|
||||
for key, refs in iteritems(references):
|
||||
for key, refs in references.items():
|
||||
if isinstance(
|
||||
field_cls, (Document, TopLevelDocumentMetaclass)
|
||||
):
|
||||
@ -153,7 +151,7 @@ class DeReference(object):
|
||||
)
|
||||
elif isinstance(item, (dict, list, tuple)) and depth - 1 <= self.max_depth:
|
||||
references = self._find_references(item, depth - 1)
|
||||
for key, refs in iteritems(references):
|
||||
for key, refs in references.items():
|
||||
reference_map.setdefault(key, set()).update(refs)
|
||||
|
||||
return reference_map
|
||||
@ -162,7 +160,7 @@ class DeReference(object):
|
||||
"""Fetch all references and convert to their document objects
|
||||
"""
|
||||
object_map = {}
|
||||
for collection, dbrefs in iteritems(self.reference_map):
|
||||
for collection, dbrefs in self.reference_map.items():
|
||||
|
||||
# we use getattr instead of hasattr because hasattr swallows any exception under python2
|
||||
# so it could hide nasty things without raising exceptions (cfr bug #1688))
|
||||
@ -174,7 +172,7 @@ class DeReference(object):
|
||||
dbref for dbref in dbrefs if (col_name, dbref) not in object_map
|
||||
]
|
||||
references = collection.objects.in_bulk(refs)
|
||||
for key, doc in iteritems(references):
|
||||
for key, doc in references.items():
|
||||
object_map[(col_name, key)] = doc
|
||||
else: # Generic reference: use the refs data to convert to document
|
||||
if isinstance(doc_type, (ListField, DictField, MapField)):
|
||||
@ -250,7 +248,7 @@ class DeReference(object):
|
||||
data = []
|
||||
else:
|
||||
is_list = False
|
||||
iterator = iteritems(items)
|
||||
iterator = items.items()
|
||||
data = {}
|
||||
|
||||
depth += 1
|
||||
@ -274,14 +272,12 @@ class DeReference(object):
|
||||
(v["_ref"].collection, v["_ref"].id), v
|
||||
)
|
||||
elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth:
|
||||
item_name = six.text_type("{0}.{1}.{2}").format(
|
||||
name, k, field_name
|
||||
)
|
||||
item_name = "{}.{}.{}".format(name, k, field_name)
|
||||
data[k]._data[field_name] = self._attach_objects(
|
||||
v, depth, instance=instance, name=item_name
|
||||
)
|
||||
elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth:
|
||||
item_name = "%s.%s" % (name, k) if name else name
|
||||
item_name = "{}.{}".format(name, k) if name else name
|
||||
data[k] = self._attach_objects(
|
||||
v, depth - 1, instance=instance, name=item_name
|
||||
)
|
||||
|
@ -4,8 +4,6 @@ import warnings
|
||||
from bson.dbref import DBRef
|
||||
import pymongo
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
import six
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine import signals
|
||||
from mongoengine.base import (
|
||||
@ -44,7 +42,7 @@ def includes_cls(fields):
|
||||
"""Helper function used for ensuring and comparing indexes."""
|
||||
first_field = None
|
||||
if len(fields):
|
||||
if isinstance(fields[0], six.string_types):
|
||||
if isinstance(fields[0], str):
|
||||
first_field = fields[0]
|
||||
elif isinstance(fields[0], (list, tuple)) and len(fields[0]):
|
||||
first_field = fields[0][0]
|
||||
@ -55,7 +53,7 @@ class InvalidCollectionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
||||
class EmbeddedDocument(BaseDocument, metaclass=DocumentMetaclass):
|
||||
r"""A :class:`~mongoengine.Document` that isn't stored in its own
|
||||
collection. :class:`~mongoengine.EmbeddedDocument`\ s should be used as
|
||||
fields on :class:`~mongoengine.Document`\ s through the
|
||||
@ -71,7 +69,6 @@ class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
||||
|
||||
__slots__ = ("_instance",)
|
||||
|
||||
# The __metaclass__ attribute is removed by 2to3 when running with Python3
|
||||
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
||||
my_metaclass = DocumentMetaclass
|
||||
|
||||
@ -82,7 +79,7 @@ class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
||||
__hash__ = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EmbeddedDocument, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self._instance = None
|
||||
self._changed_fields = []
|
||||
|
||||
@ -95,7 +92,7 @@ class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def to_mongo(self, *args, **kwargs):
|
||||
data = super(EmbeddedDocument, self).to_mongo(*args, **kwargs)
|
||||
data = super().to_mongo(*args, **kwargs)
|
||||
|
||||
# remove _id from the SON if it's in it and it's None
|
||||
if "_id" in data and data["_id"] is None:
|
||||
@ -104,7 +101,7 @@ class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
||||
return data
|
||||
|
||||
|
||||
class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
||||
class Document(BaseDocument, metaclass=TopLevelDocumentMetaclass):
|
||||
"""The base class used for defining the structure and properties of
|
||||
collections of documents stored in MongoDB. Inherit from this class, and
|
||||
add fields as class attributes to define a document's structure.
|
||||
@ -156,7 +153,6 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
||||
in the :attr:`meta` dictionary.
|
||||
"""
|
||||
|
||||
# The __metaclass__ attribute is removed by 2to3 when running with Python3
|
||||
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
||||
my_metaclass = TopLevelDocumentMetaclass
|
||||
|
||||
@ -260,7 +256,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
||||
return db.create_collection(collection_name, **opts)
|
||||
|
||||
def to_mongo(self, *args, **kwargs):
|
||||
data = super(Document, self).to_mongo(*args, **kwargs)
|
||||
data = super().to_mongo(*args, **kwargs)
|
||||
|
||||
# If '_id' is None, try and set it from self._data. If that
|
||||
# doesn't exist either, remove '_id' from the SON completely.
|
||||
@ -431,16 +427,16 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
||||
self.cascade_save(**kwargs)
|
||||
|
||||
except pymongo.errors.DuplicateKeyError as err:
|
||||
message = u"Tried to save duplicate unique keys (%s)"
|
||||
raise NotUniqueError(message % six.text_type(err))
|
||||
message = "Tried to save duplicate unique keys (%s)"
|
||||
raise NotUniqueError(message % err)
|
||||
except pymongo.errors.OperationFailure as err:
|
||||
message = "Could not save document (%s)"
|
||||
if re.match("^E1100[01] duplicate key", six.text_type(err)):
|
||||
if re.match("^E1100[01] duplicate key", str(err)):
|
||||
# E11000 - duplicate key error index
|
||||
# E11001 - duplicate key on update
|
||||
message = u"Tried to save duplicate unique keys (%s)"
|
||||
raise NotUniqueError(message % six.text_type(err))
|
||||
raise OperationError(message % six.text_type(err))
|
||||
message = "Tried to save duplicate unique keys (%s)"
|
||||
raise NotUniqueError(message % err)
|
||||
raise OperationError(message % err)
|
||||
|
||||
# Make sure we store the PK on this document now that it's saved
|
||||
id_field = self._meta["id_field"]
|
||||
@ -559,7 +555,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
||||
if not getattr(ref, "_changed_fields", True):
|
||||
continue
|
||||
|
||||
ref_id = "%s,%s" % (ref.__class__.__name__, str(ref._data))
|
||||
ref_id = "{},{}".format(ref.__class__.__name__, str(ref._data))
|
||||
if ref and ref_id not in _refs:
|
||||
_refs.append(ref_id)
|
||||
kwargs["_refs"] = _refs
|
||||
@ -634,7 +630,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
||||
|
||||
# Delete FileFields separately
|
||||
FileField = _import_class("FileField")
|
||||
for name, field in iteritems(self._fields):
|
||||
for name, field in self._fields.items():
|
||||
if isinstance(field, FileField):
|
||||
getattr(self, name).delete()
|
||||
|
||||
@ -643,7 +639,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
||||
write_concern=write_concern, _from_doc_delete=True
|
||||
)
|
||||
except pymongo.errors.OperationFailure as err:
|
||||
message = u"Could not delete document (%s)" % err.message
|
||||
message = "Could not delete document (%s)" % err.message
|
||||
raise OperationError(message)
|
||||
signals.post_delete.send(self.__class__, document=self, **signal_kwargs)
|
||||
|
||||
@ -979,10 +975,10 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
||||
indexes.append(index)
|
||||
|
||||
# finish up by appending { '_id': 1 } and { '_cls': 1 }, if needed
|
||||
if [(u"_id", 1)] not in indexes:
|
||||
indexes.append([(u"_id", 1)])
|
||||
if [("_id", 1)] not in indexes:
|
||||
indexes.append([("_id", 1)])
|
||||
if cls._meta.get("index_cls", True) and cls._meta.get("allow_inheritance"):
|
||||
indexes.append([(u"_cls", 1)])
|
||||
indexes.append([("_cls", 1)])
|
||||
|
||||
return indexes
|
||||
|
||||
@ -1006,19 +1002,19 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
||||
extra = [index for index in existing if index not in required]
|
||||
|
||||
# if { _cls: 1 } is missing, make sure it's *really* necessary
|
||||
if [(u"_cls", 1)] in missing:
|
||||
if [("_cls", 1)] in missing:
|
||||
cls_obsolete = False
|
||||
for index in existing:
|
||||
if includes_cls(index) and index not in extra:
|
||||
cls_obsolete = True
|
||||
break
|
||||
if cls_obsolete:
|
||||
missing.remove([(u"_cls", 1)])
|
||||
missing.remove([("_cls", 1)])
|
||||
|
||||
return {"missing": missing, "extra": extra}
|
||||
|
||||
|
||||
class DynamicDocument(six.with_metaclass(TopLevelDocumentMetaclass, Document)):
|
||||
class DynamicDocument(Document, metaclass=TopLevelDocumentMetaclass):
|
||||
"""A Dynamic Document class allowing flexible, expandable and uncontrolled
|
||||
schemas. As a :class:`~mongoengine.Document` subclass, acts in the same
|
||||
way as an ordinary document but has expanded style properties. Any data
|
||||
@ -1032,7 +1028,6 @@ class DynamicDocument(six.with_metaclass(TopLevelDocumentMetaclass, Document)):
|
||||
There is one caveat on Dynamic Documents: undeclared fields cannot start with `_`
|
||||
"""
|
||||
|
||||
# The __metaclass__ attribute is removed by 2to3 when running with Python3
|
||||
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
||||
my_metaclass = TopLevelDocumentMetaclass
|
||||
|
||||
@ -1047,16 +1042,15 @@ class DynamicDocument(six.with_metaclass(TopLevelDocumentMetaclass, Document)):
|
||||
setattr(self, field_name, None)
|
||||
self._dynamic_fields[field_name].null = False
|
||||
else:
|
||||
super(DynamicDocument, self).__delattr__(*args, **kwargs)
|
||||
super().__delattr__(*args, **kwargs)
|
||||
|
||||
|
||||
class DynamicEmbeddedDocument(six.with_metaclass(DocumentMetaclass, EmbeddedDocument)):
|
||||
class DynamicEmbeddedDocument(EmbeddedDocument, metaclass=DocumentMetaclass):
|
||||
"""A Dynamic Embedded Document class allowing flexible, expandable and
|
||||
uncontrolled schemas. See :class:`~mongoengine.DynamicDocument` for more
|
||||
information about dynamic documents.
|
||||
"""
|
||||
|
||||
# The __metaclass__ attribute is removed by 2to3 when running with Python3
|
||||
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
||||
my_metaclass = DocumentMetaclass
|
||||
|
||||
@ -1076,7 +1070,7 @@ class DynamicEmbeddedDocument(six.with_metaclass(DocumentMetaclass, EmbeddedDocu
|
||||
setattr(self, field_name, None)
|
||||
|
||||
|
||||
class MapReduceDocument(object):
|
||||
class MapReduceDocument:
|
||||
"""A document returned from a map/reduce query.
|
||||
|
||||
:param collection: An instance of :class:`~pymongo.Collection`
|
||||
|
@ -1,7 +1,5 @@
|
||||
from collections import defaultdict
|
||||
|
||||
import six
|
||||
from six import iteritems
|
||||
|
||||
__all__ = (
|
||||
"NotRegistered",
|
||||
@ -87,24 +85,24 @@ class ValidationError(AssertionError):
|
||||
_message = None
|
||||
|
||||
def __init__(self, message="", **kwargs):
|
||||
super(ValidationError, self).__init__(message)
|
||||
super().__init__(message)
|
||||
self.errors = kwargs.get("errors", {})
|
||||
self.field_name = kwargs.get("field_name")
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
return six.text_type(self.message)
|
||||
return str(self.message)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%s,)" % (self.__class__.__name__, self.message)
|
||||
return "{}({},)".format(self.__class__.__name__, self.message)
|
||||
|
||||
def __getattribute__(self, name):
|
||||
message = super(ValidationError, self).__getattribute__(name)
|
||||
message = super().__getattribute__(name)
|
||||
if name == "message":
|
||||
if self.field_name:
|
||||
message = "%s" % message
|
||||
if self.errors:
|
||||
message = "%s(%s)" % (message, self._format_errors())
|
||||
message = "{}({})".format(message, self._format_errors())
|
||||
return message
|
||||
|
||||
def _get_message(self):
|
||||
@ -126,12 +124,12 @@ class ValidationError(AssertionError):
|
||||
def build_dict(source):
|
||||
errors_dict = {}
|
||||
if isinstance(source, dict):
|
||||
for field_name, error in iteritems(source):
|
||||
for field_name, error in source.items():
|
||||
errors_dict[field_name] = build_dict(error)
|
||||
elif isinstance(source, ValidationError) and source.errors:
|
||||
return build_dict(source.errors)
|
||||
else:
|
||||
return six.text_type(source)
|
||||
return str(source)
|
||||
|
||||
return errors_dict
|
||||
|
||||
@ -147,15 +145,15 @@ class ValidationError(AssertionError):
|
||||
if isinstance(value, list):
|
||||
value = " ".join([generate_key(k) for k in value])
|
||||
elif isinstance(value, dict):
|
||||
value = " ".join([generate_key(v, k) for k, v in iteritems(value)])
|
||||
value = " ".join([generate_key(v, k) for k, v in value.items()])
|
||||
|
||||
results = "%s.%s" % (prefix, value) if prefix else value
|
||||
results = "{}.{}".format(prefix, value) if prefix else value
|
||||
return results
|
||||
|
||||
error_dict = defaultdict(list)
|
||||
for k, v in iteritems(self.to_dict()):
|
||||
for k, v in self.to_dict().items():
|
||||
error_dict[generate_key(v)].append(k)
|
||||
return " ".join(["%s: %s" % (k, v) for k, v in iteritems(error_dict)])
|
||||
return " ".join(["{}: {}".format(k, v) for k, v in error_dict.items()])
|
||||
|
||||
|
||||
class DeprecatedError(Exception):
|
||||
|
@ -5,14 +5,14 @@ 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
|
||||
import pymongo
|
||||
from pymongo import ReturnDocument
|
||||
import six
|
||||
from six import iteritems
|
||||
|
||||
try:
|
||||
import dateutil
|
||||
@ -21,11 +21,6 @@ except ImportError:
|
||||
else:
|
||||
import dateutil.parser
|
||||
|
||||
try:
|
||||
from bson.int64 import Int64
|
||||
except ImportError:
|
||||
Int64 = long
|
||||
|
||||
|
||||
from mongoengine.base import (
|
||||
BaseDocument,
|
||||
@ -42,7 +37,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
|
||||
@ -53,11 +47,6 @@ except ImportError:
|
||||
Image = None
|
||||
ImageOps = None
|
||||
|
||||
if six.PY3:
|
||||
# Useless as long as 2to3 gets executed
|
||||
# as it turns `long` into `int` blindly
|
||||
long = int
|
||||
|
||||
|
||||
__all__ = (
|
||||
"StringField",
|
||||
@ -114,10 +103,10 @@ class StringField(BaseField):
|
||||
self.regex = re.compile(regex) if regex else None
|
||||
self.max_length = max_length
|
||||
self.min_length = min_length
|
||||
super(StringField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
if isinstance(value, six.text_type):
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
try:
|
||||
value = value.decode("utf-8")
|
||||
@ -126,7 +115,7 @@ class StringField(BaseField):
|
||||
return value
|
||||
|
||||
def validate(self, value):
|
||||
if not isinstance(value, six.string_types):
|
||||
if not isinstance(value, str):
|
||||
self.error("StringField only accepts string values")
|
||||
|
||||
if self.max_length is not None and len(value) > self.max_length:
|
||||
@ -142,7 +131,7 @@ class StringField(BaseField):
|
||||
return None
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
if not isinstance(op, six.string_types):
|
||||
if not isinstance(op, str):
|
||||
return value
|
||||
|
||||
if op in STRING_OPERATORS:
|
||||
@ -162,7 +151,7 @@ class StringField(BaseField):
|
||||
# escape unsafe characters which could lead to a re.error
|
||||
value = re.escape(value)
|
||||
value = re.compile(regex % value, flags)
|
||||
return super(StringField, self).prepare_query_value(op, value)
|
||||
return super().prepare_query_value(op, value)
|
||||
|
||||
|
||||
class URLField(StringField):
|
||||
@ -186,17 +175,17 @@ class URLField(StringField):
|
||||
def __init__(self, url_regex=None, schemes=None, **kwargs):
|
||||
self.url_regex = url_regex or self._URL_REGEX
|
||||
self.schemes = schemes or self._URL_SCHEMES
|
||||
super(URLField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def validate(self, value):
|
||||
# Check first if the scheme is valid
|
||||
scheme = value.split("://")[0].lower()
|
||||
if scheme not in self.schemes:
|
||||
self.error(u"Invalid scheme {} in URL: {}".format(scheme, value))
|
||||
self.error("Invalid scheme {} in URL: {}".format(scheme, value))
|
||||
|
||||
# Then check full URL
|
||||
if not self.url_regex.match(value):
|
||||
self.error(u"Invalid URL: {}".format(value))
|
||||
self.error("Invalid URL: {}".format(value))
|
||||
|
||||
|
||||
class EmailField(StringField):
|
||||
@ -214,7 +203,7 @@ class EmailField(StringField):
|
||||
)
|
||||
|
||||
UTF8_USER_REGEX = LazyRegexCompiler(
|
||||
six.u(
|
||||
(
|
||||
# RFC 6531 Section 3.3 extends `atext` (used by dot-atom) to
|
||||
# include `UTF8-non-ascii`.
|
||||
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z\u0080-\U0010FFFF]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z\u0080-\U0010FFFF]+)*\Z"
|
||||
@ -229,7 +218,7 @@ class EmailField(StringField):
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
error_msg = u"Invalid email address: %s"
|
||||
error_msg = "Invalid email address: %s"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -253,7 +242,7 @@ class EmailField(StringField):
|
||||
self.domain_whitelist = domain_whitelist or []
|
||||
self.allow_utf8_user = allow_utf8_user
|
||||
self.allow_ip_domain = allow_ip_domain
|
||||
super(EmailField, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def validate_user_part(self, user_part):
|
||||
"""Validate the user part of the email address. Return True if
|
||||
@ -280,13 +269,13 @@ class EmailField(StringField):
|
||||
try:
|
||||
socket.inet_pton(addr_family, domain_part[1:-1])
|
||||
return True
|
||||
except (socket.error, UnicodeEncodeError):
|
||||
except (OSError, UnicodeEncodeError):
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def validate(self, value):
|
||||
super(EmailField, self).validate(value)
|
||||
super().validate(value)
|
||||
|
||||
if "@" not in value:
|
||||
self.error(self.error_msg % value)
|
||||
@ -303,12 +292,16 @@ class EmailField(StringField):
|
||||
domain_part = domain_part.encode("idna").decode("ascii")
|
||||
except UnicodeError:
|
||||
self.error(
|
||||
"%s %s" % (self.error_msg % value, "(domain failed IDN encoding)")
|
||||
"{} {}".format(
|
||||
self.error_msg % value, "(domain failed IDN encoding)"
|
||||
)
|
||||
)
|
||||
else:
|
||||
if not self.validate_domain_part(domain_part):
|
||||
self.error(
|
||||
"%s %s" % (self.error_msg % value, "(domain validation failed)")
|
||||
"{} {}".format(
|
||||
self.error_msg % value, "(domain validation failed)"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@ -317,7 +310,7 @@ class IntField(BaseField):
|
||||
|
||||
def __init__(self, min_value=None, max_value=None, **kwargs):
|
||||
self.min_value, self.max_value = min_value, max_value
|
||||
super(IntField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
try:
|
||||
@ -342,19 +335,19 @@ class IntField(BaseField):
|
||||
if value is None:
|
||||
return value
|
||||
|
||||
return super(IntField, self).prepare_query_value(op, int(value))
|
||||
return super().prepare_query_value(op, int(value))
|
||||
|
||||
|
||||
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
|
||||
super(LongField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
try:
|
||||
value = long(value)
|
||||
value = int(value)
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
return value
|
||||
@ -364,7 +357,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)
|
||||
|
||||
@ -378,7 +371,7 @@ class LongField(BaseField):
|
||||
if value is None:
|
||||
return value
|
||||
|
||||
return super(LongField, self).prepare_query_value(op, long(value))
|
||||
return super().prepare_query_value(op, int(value))
|
||||
|
||||
|
||||
class FloatField(BaseField):
|
||||
@ -386,7 +379,7 @@ class FloatField(BaseField):
|
||||
|
||||
def __init__(self, min_value=None, max_value=None, **kwargs):
|
||||
self.min_value, self.max_value = min_value, max_value
|
||||
super(FloatField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
try:
|
||||
@ -396,7 +389,7 @@ class FloatField(BaseField):
|
||||
return value
|
||||
|
||||
def validate(self, value):
|
||||
if isinstance(value, six.integer_types):
|
||||
if isinstance(value, int):
|
||||
try:
|
||||
value = float(value)
|
||||
except OverflowError:
|
||||
@ -415,7 +408,7 @@ class FloatField(BaseField):
|
||||
if value is None:
|
||||
return value
|
||||
|
||||
return super(FloatField, self).prepare_query_value(op, float(value))
|
||||
return super().prepare_query_value(op, float(value))
|
||||
|
||||
|
||||
class DecimalField(BaseField):
|
||||
@ -462,7 +455,7 @@ class DecimalField(BaseField):
|
||||
self.precision = precision
|
||||
self.rounding = rounding
|
||||
|
||||
super(DecimalField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
if value is None:
|
||||
@ -481,13 +474,13 @@ class DecimalField(BaseField):
|
||||
if value is None:
|
||||
return value
|
||||
if self.force_string:
|
||||
return six.text_type(self.to_python(value))
|
||||
return str(self.to_python(value))
|
||||
return float(self.to_python(value))
|
||||
|
||||
def validate(self, value):
|
||||
if not isinstance(value, decimal.Decimal):
|
||||
if not isinstance(value, six.string_types):
|
||||
value = six.text_type(value)
|
||||
if not isinstance(value, str):
|
||||
value = str(value)
|
||||
try:
|
||||
value = decimal.Decimal(value)
|
||||
except (TypeError, ValueError, decimal.InvalidOperation) as exc:
|
||||
@ -500,7 +493,7 @@ class DecimalField(BaseField):
|
||||
self.error("Decimal value is too large")
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
return super(DecimalField, self).prepare_query_value(op, self.to_mongo(value))
|
||||
return super().prepare_query_value(op, self.to_mongo(value))
|
||||
|
||||
|
||||
class BooleanField(BaseField):
|
||||
@ -540,7 +533,7 @@ class DateTimeField(BaseField):
|
||||
def validate(self, value):
|
||||
new_value = self.to_mongo(value)
|
||||
if not isinstance(new_value, (datetime.datetime, datetime.date)):
|
||||
self.error(u'cannot parse date "%s"' % value)
|
||||
self.error('cannot parse date "%s"' % value)
|
||||
|
||||
def to_mongo(self, value):
|
||||
if value is None:
|
||||
@ -552,7 +545,7 @@ class DateTimeField(BaseField):
|
||||
if callable(value):
|
||||
return value()
|
||||
|
||||
if not isinstance(value, six.string_types):
|
||||
if not isinstance(value, str):
|
||||
return None
|
||||
|
||||
return self._parse_datetime(value)
|
||||
@ -597,19 +590,19 @@ class DateTimeField(BaseField):
|
||||
return None
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
return super(DateTimeField, self).prepare_query_value(op, self.to_mongo(value))
|
||||
return super().prepare_query_value(op, self.to_mongo(value))
|
||||
|
||||
|
||||
class DateField(DateTimeField):
|
||||
def to_mongo(self, value):
|
||||
value = super(DateField, self).to_mongo(value)
|
||||
value = super().to_mongo(value)
|
||||
# drop hours, minutes, seconds
|
||||
if isinstance(value, datetime.datetime):
|
||||
value = datetime.datetime(value.year, value.month, value.day)
|
||||
return value
|
||||
|
||||
def to_python(self, value):
|
||||
value = super(DateField, self).to_python(value)
|
||||
value = super().to_python(value)
|
||||
# convert datetime to date
|
||||
if isinstance(value, datetime.datetime):
|
||||
value = datetime.date(value.year, value.month, value.day)
|
||||
@ -643,7 +636,7 @@ class ComplexDateTimeField(StringField):
|
||||
"""
|
||||
self.separator = separator
|
||||
self.format = separator.join(["%Y", "%m", "%d", "%H", "%M", "%S", "%f"])
|
||||
super(ComplexDateTimeField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def _convert_from_datetime(self, val):
|
||||
"""
|
||||
@ -674,14 +667,14 @@ class ComplexDateTimeField(StringField):
|
||||
if instance is None:
|
||||
return self
|
||||
|
||||
data = super(ComplexDateTimeField, self).__get__(instance, owner)
|
||||
data = super().__get__(instance, owner)
|
||||
|
||||
if isinstance(data, datetime.datetime) or data is None:
|
||||
return data
|
||||
return self._convert_from_string(data)
|
||||
|
||||
def __set__(self, instance, value):
|
||||
super(ComplexDateTimeField, self).__set__(instance, value)
|
||||
super().__set__(instance, value)
|
||||
value = instance._data[self.name]
|
||||
if value is not None:
|
||||
instance._data[self.name] = self._convert_from_datetime(value)
|
||||
@ -703,9 +696,7 @@ class ComplexDateTimeField(StringField):
|
||||
return self._convert_from_datetime(value)
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
return super(ComplexDateTimeField, self).prepare_query_value(
|
||||
op, self._convert_from_datetime(value)
|
||||
)
|
||||
return super().prepare_query_value(op, self._convert_from_datetime(value))
|
||||
|
||||
|
||||
class EmbeddedDocumentField(BaseField):
|
||||
@ -716,7 +707,7 @@ class EmbeddedDocumentField(BaseField):
|
||||
def __init__(self, document_type, **kwargs):
|
||||
# XXX ValidationError raised outside of the "validate" method.
|
||||
if not (
|
||||
isinstance(document_type, six.string_types)
|
||||
isinstance(document_type, str)
|
||||
or issubclass(document_type, EmbeddedDocument)
|
||||
):
|
||||
self.error(
|
||||
@ -725,11 +716,11 @@ class EmbeddedDocumentField(BaseField):
|
||||
)
|
||||
|
||||
self.document_type_obj = document_type
|
||||
super(EmbeddedDocumentField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@property
|
||||
def document_type(self):
|
||||
if isinstance(self.document_type_obj, six.string_types):
|
||||
if isinstance(self.document_type_obj, str):
|
||||
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
||||
resolved_document_type = self.owner_document
|
||||
else:
|
||||
@ -786,7 +777,7 @@ class EmbeddedDocumentField(BaseField):
|
||||
"Querying the embedded document '%s' failed, due to an invalid query value"
|
||||
% (self.document_type._class_name,)
|
||||
)
|
||||
super(EmbeddedDocumentField, self).prepare_query_value(op, value)
|
||||
super().prepare_query_value(op, value)
|
||||
return self.to_mongo(value)
|
||||
|
||||
|
||||
@ -802,9 +793,7 @@ class GenericEmbeddedDocumentField(BaseField):
|
||||
"""
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
return super(GenericEmbeddedDocumentField, self).prepare_query_value(
|
||||
op, self.to_mongo(value)
|
||||
)
|
||||
return super().prepare_query_value(op, self.to_mongo(value))
|
||||
|
||||
def to_python(self, value):
|
||||
if isinstance(value, dict):
|
||||
@ -855,7 +844,7 @@ class DynamicField(BaseField):
|
||||
"""Convert a Python type to a MongoDB compatible type.
|
||||
"""
|
||||
|
||||
if isinstance(value, six.string_types):
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
|
||||
if hasattr(value, "to_mongo"):
|
||||
@ -877,12 +866,12 @@ class DynamicField(BaseField):
|
||||
value = {k: v for k, v in enumerate(value)}
|
||||
|
||||
data = {}
|
||||
for k, v in iteritems(value):
|
||||
for k, v in value.items():
|
||||
data[k] = self.to_mongo(v, use_db_field, fields)
|
||||
|
||||
value = data
|
||||
if is_list: # Convert back to a list
|
||||
value = [v for k, v in sorted(iteritems(data), key=itemgetter(0))]
|
||||
value = [v for k, v in sorted(data.items(), key=itemgetter(0))]
|
||||
return value
|
||||
|
||||
def to_python(self, value):
|
||||
@ -892,15 +881,15 @@ class DynamicField(BaseField):
|
||||
value = doc_cls._get_db().dereference(value["_ref"])
|
||||
return doc_cls._from_son(value)
|
||||
|
||||
return super(DynamicField, self).to_python(value)
|
||||
return super().to_python(value)
|
||||
|
||||
def lookup_member(self, member_name):
|
||||
return member_name
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
if isinstance(value, six.string_types):
|
||||
if isinstance(value, str):
|
||||
return StringField().prepare_query_value(op, value)
|
||||
return super(DynamicField, self).prepare_query_value(op, self.to_mongo(value))
|
||||
return super().prepare_query_value(op, self.to_mongo(value))
|
||||
|
||||
def validate(self, value, clean=True):
|
||||
if hasattr(value, "validate"):
|
||||
@ -921,7 +910,7 @@ class ListField(ComplexBaseField):
|
||||
self.field = field
|
||||
self.max_length = max_length
|
||||
kwargs.setdefault("default", lambda: [])
|
||||
super(ListField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
if instance is None:
|
||||
@ -935,7 +924,7 @@ class ListField(ComplexBaseField):
|
||||
and value
|
||||
):
|
||||
instance._data[self.name] = [self.field.build_lazyref(x) for x in value]
|
||||
return super(ListField, self).__get__(instance, owner)
|
||||
return super().__get__(instance, owner)
|
||||
|
||||
def validate(self, value):
|
||||
"""Make sure that a list of valid fields is being used."""
|
||||
@ -949,7 +938,7 @@ class ListField(ComplexBaseField):
|
||||
if self.max_length is not None and len(value) > self.max_length:
|
||||
self.error("List is too long")
|
||||
|
||||
super(ListField, self).validate(value)
|
||||
super().validate(value)
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
# Validate that the `set` operator doesn't contain more items than `max_length`.
|
||||
@ -963,14 +952,14 @@ class ListField(ComplexBaseField):
|
||||
if (
|
||||
op in ("set", "unset", None)
|
||||
and hasattr(value, "__iter__")
|
||||
and not isinstance(value, six.string_types)
|
||||
and not isinstance(value, str)
|
||||
and not isinstance(value, BaseDocument)
|
||||
):
|
||||
return [self.field.prepare_query_value(op, v) for v in value]
|
||||
|
||||
return self.field.prepare_query_value(op, value)
|
||||
|
||||
return super(ListField, self).prepare_query_value(op, value)
|
||||
return super().prepare_query_value(op, value)
|
||||
|
||||
|
||||
class EmbeddedDocumentListField(ListField):
|
||||
@ -991,9 +980,7 @@ class EmbeddedDocumentListField(ListField):
|
||||
:param kwargs: Keyword arguments passed directly into the parent
|
||||
:class:`~mongoengine.ListField`.
|
||||
"""
|
||||
super(EmbeddedDocumentListField, self).__init__(
|
||||
field=EmbeddedDocumentField(document_type), **kwargs
|
||||
)
|
||||
super().__init__(field=EmbeddedDocumentField(document_type), **kwargs)
|
||||
|
||||
|
||||
class SortedListField(ListField):
|
||||
@ -1019,10 +1006,10 @@ class SortedListField(ListField):
|
||||
self._ordering = kwargs.pop("ordering")
|
||||
if "reverse" in kwargs.keys():
|
||||
self._order_reverse = kwargs.pop("reverse")
|
||||
super(SortedListField, self).__init__(field, **kwargs)
|
||||
super().__init__(field, **kwargs)
|
||||
|
||||
def to_mongo(self, value, use_db_field=True, fields=None):
|
||||
value = super(SortedListField, self).to_mongo(value, use_db_field, fields)
|
||||
value = super().to_mongo(value, use_db_field, fields)
|
||||
if self._ordering is not None:
|
||||
return sorted(
|
||||
value, key=itemgetter(self._ordering), reverse=self._order_reverse
|
||||
@ -1035,9 +1022,7 @@ def key_not_string(d):
|
||||
dictionary is not a string.
|
||||
"""
|
||||
for k, v in d.items():
|
||||
if not isinstance(k, six.string_types) or (
|
||||
isinstance(v, dict) and key_not_string(v)
|
||||
):
|
||||
if not isinstance(k, str) or (isinstance(v, dict) and key_not_string(v)):
|
||||
return True
|
||||
|
||||
|
||||
@ -1077,7 +1062,7 @@ class DictField(ComplexBaseField):
|
||||
self._auto_dereference = False
|
||||
|
||||
kwargs.setdefault("default", lambda: {})
|
||||
super(DictField, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def validate(self, value):
|
||||
"""Make sure that a list of valid fields is being used."""
|
||||
@ -1097,7 +1082,7 @@ class DictField(ComplexBaseField):
|
||||
self.error(
|
||||
'Invalid dictionary key name - keys may not startswith "$" characters'
|
||||
)
|
||||
super(DictField, self).validate(value)
|
||||
super().validate(value)
|
||||
|
||||
def lookup_member(self, member_name):
|
||||
return DictField(db_field=member_name)
|
||||
@ -1114,7 +1099,7 @@ class DictField(ComplexBaseField):
|
||||
"iexact",
|
||||
]
|
||||
|
||||
if op in match_operators and isinstance(value, six.string_types):
|
||||
if op in match_operators and isinstance(value, str):
|
||||
return StringField().prepare_query_value(op, value)
|
||||
|
||||
if hasattr(
|
||||
@ -1126,7 +1111,7 @@ class DictField(ComplexBaseField):
|
||||
}
|
||||
return self.field.prepare_query_value(op, value)
|
||||
|
||||
return super(DictField, self).prepare_query_value(op, value)
|
||||
return super().prepare_query_value(op, value)
|
||||
|
||||
|
||||
class MapField(DictField):
|
||||
@ -1141,7 +1126,7 @@ class MapField(DictField):
|
||||
# XXX ValidationError raised outside of the "validate" method.
|
||||
if not isinstance(field, BaseField):
|
||||
self.error("Argument to MapField constructor must be a valid field")
|
||||
super(MapField, self).__init__(field=field, *args, **kwargs)
|
||||
super().__init__(field=field, *args, **kwargs)
|
||||
|
||||
|
||||
class ReferenceField(BaseField):
|
||||
@ -1201,7 +1186,7 @@ class ReferenceField(BaseField):
|
||||
:class:`~pymongo.dbref.DBRef`, regardless of the value of `dbref`.
|
||||
"""
|
||||
# XXX ValidationError raised outside of the "validate" method.
|
||||
if not isinstance(document_type, six.string_types) and not issubclass(
|
||||
if not isinstance(document_type, str) and not issubclass(
|
||||
document_type, Document
|
||||
):
|
||||
self.error(
|
||||
@ -1212,11 +1197,11 @@ class ReferenceField(BaseField):
|
||||
self.dbref = dbref
|
||||
self.document_type_obj = document_type
|
||||
self.reverse_delete_rule = reverse_delete_rule
|
||||
super(ReferenceField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@property
|
||||
def document_type(self):
|
||||
if isinstance(self.document_type_obj, six.string_types):
|
||||
if isinstance(self.document_type_obj, str):
|
||||
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
||||
self.document_type_obj = self.owner_document
|
||||
else:
|
||||
@ -1245,7 +1230,7 @@ class ReferenceField(BaseField):
|
||||
else:
|
||||
instance._data[self.name] = cls._from_son(dereferenced)
|
||||
|
||||
return super(ReferenceField, self).__get__(instance, owner)
|
||||
return super().__get__(instance, owner)
|
||||
|
||||
def to_mongo(self, document):
|
||||
if isinstance(document, DBRef):
|
||||
@ -1296,7 +1281,7 @@ class ReferenceField(BaseField):
|
||||
def prepare_query_value(self, op, value):
|
||||
if value is None:
|
||||
return None
|
||||
super(ReferenceField, self).prepare_query_value(op, value)
|
||||
super().prepare_query_value(op, value)
|
||||
return self.to_mongo(value)
|
||||
|
||||
def validate(self, value):
|
||||
@ -1332,7 +1317,7 @@ class CachedReferenceField(BaseField):
|
||||
fields = []
|
||||
|
||||
# XXX ValidationError raised outside of the "validate" method.
|
||||
if not isinstance(document_type, six.string_types) and not issubclass(
|
||||
if not isinstance(document_type, str) and not issubclass(
|
||||
document_type, Document
|
||||
):
|
||||
self.error(
|
||||
@ -1343,7 +1328,7 @@ class CachedReferenceField(BaseField):
|
||||
self.auto_sync = auto_sync
|
||||
self.document_type_obj = document_type
|
||||
self.fields = fields
|
||||
super(CachedReferenceField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def start_listener(self):
|
||||
from mongoengine import signals
|
||||
@ -1355,7 +1340,7 @@ class CachedReferenceField(BaseField):
|
||||
return None
|
||||
|
||||
update_kwargs = {
|
||||
"set__%s__%s" % (self.name, key): val
|
||||
"set__{}__{}".format(self.name, key): val
|
||||
for key, val in document._delta()[0].items()
|
||||
if key in self.fields
|
||||
}
|
||||
@ -1377,7 +1362,7 @@ class CachedReferenceField(BaseField):
|
||||
|
||||
@property
|
||||
def document_type(self):
|
||||
if isinstance(self.document_type_obj, six.string_types):
|
||||
if isinstance(self.document_type_obj, str):
|
||||
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
||||
self.document_type_obj = self.owner_document
|
||||
else:
|
||||
@ -1401,7 +1386,7 @@ class CachedReferenceField(BaseField):
|
||||
else:
|
||||
instance._data[self.name] = self.document_type._from_son(dereferenced)
|
||||
|
||||
return super(CachedReferenceField, self).__get__(instance, owner)
|
||||
return super().__get__(instance, owner)
|
||||
|
||||
def to_mongo(self, document, use_db_field=True, fields=None):
|
||||
id_field_name = self.document_type._meta["id_field"]
|
||||
@ -1500,12 +1485,12 @@ class GenericReferenceField(BaseField):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
choices = kwargs.pop("choices", None)
|
||||
super(GenericReferenceField, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.choices = []
|
||||
# Keep the choices as a list of allowed Document class names
|
||||
if choices:
|
||||
for choice in choices:
|
||||
if isinstance(choice, six.string_types):
|
||||
if isinstance(choice, str):
|
||||
self.choices.append(choice)
|
||||
elif isinstance(choice, type) and issubclass(choice, Document):
|
||||
self.choices.append(choice._class_name)
|
||||
@ -1514,7 +1499,7 @@ class GenericReferenceField(BaseField):
|
||||
# method.
|
||||
self.error(
|
||||
"Invalid choices provided: must be a list of"
|
||||
"Document subclasses and/or six.string_typess"
|
||||
"Document subclasses and/or str"
|
||||
)
|
||||
|
||||
def _validate_choices(self, value):
|
||||
@ -1524,7 +1509,7 @@ class GenericReferenceField(BaseField):
|
||||
value = value.get("_cls")
|
||||
elif isinstance(value, Document):
|
||||
value = value._class_name
|
||||
super(GenericReferenceField, self)._validate_choices(value)
|
||||
super()._validate_choices(value)
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
if instance is None:
|
||||
@ -1540,7 +1525,7 @@ class GenericReferenceField(BaseField):
|
||||
else:
|
||||
instance._data[self.name] = dereferenced
|
||||
|
||||
return super(GenericReferenceField, self).__get__(instance, owner)
|
||||
return super().__get__(instance, owner)
|
||||
|
||||
def validate(self, value):
|
||||
if not isinstance(value, (Document, DBRef, dict, SON)):
|
||||
@ -1604,22 +1589,22 @@ class BinaryField(BaseField):
|
||||
|
||||
def __init__(self, max_bytes=None, **kwargs):
|
||||
self.max_bytes = max_bytes
|
||||
super(BinaryField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def __set__(self, instance, value):
|
||||
"""Handle bytearrays in python 3.1"""
|
||||
if six.PY3 and isinstance(value, bytearray):
|
||||
value = six.binary_type(value)
|
||||
return super(BinaryField, self).__set__(instance, value)
|
||||
if isinstance(value, bytearray):
|
||||
value = bytes(value)
|
||||
return super().__set__(instance, value)
|
||||
|
||||
def to_mongo(self, value):
|
||||
return Binary(value)
|
||||
|
||||
def validate(self, value):
|
||||
if not isinstance(value, (six.binary_type, Binary)):
|
||||
if not isinstance(value, (bytes, Binary)):
|
||||
self.error(
|
||||
"BinaryField only accepts instances of "
|
||||
"(%s, %s, Binary)" % (six.binary_type.__name__, Binary.__name__)
|
||||
"(%s, %s, Binary)" % (bytes.__name__, Binary.__name__)
|
||||
)
|
||||
|
||||
if self.max_bytes is not None and len(value) > self.max_bytes:
|
||||
@ -1628,14 +1613,14 @@ class BinaryField(BaseField):
|
||||
def prepare_query_value(self, op, value):
|
||||
if value is None:
|
||||
return value
|
||||
return super(BinaryField, self).prepare_query_value(op, self.to_mongo(value))
|
||||
return super().prepare_query_value(op, self.to_mongo(value))
|
||||
|
||||
|
||||
class GridFSError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class GridFSProxy(object):
|
||||
class GridFSProxy:
|
||||
"""Proxy object to handle writing and reading of files to and from GridFS
|
||||
|
||||
.. versionadded:: 0.4
|
||||
@ -1685,8 +1670,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
|
||||
@ -1701,12 +1684,12 @@ class GridFSProxy(object):
|
||||
return self.__copy__()
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s: %s>" % (self.__class__.__name__, self.grid_id)
|
||||
return "<{}: {}>".format(self.__class__.__name__, self.grid_id)
|
||||
|
||||
def __str__(self):
|
||||
gridout = self.get()
|
||||
filename = getattr(gridout, "filename") if gridout else "<no file>"
|
||||
return "<%s: %s (%s)>" % (self.__class__.__name__, filename, self.grid_id)
|
||||
return "<{}: {} ({})>".format(self.__class__.__name__, filename, self.grid_id)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, GridFSProxy):
|
||||
@ -1817,7 +1800,7 @@ class FileField(BaseField):
|
||||
def __init__(
|
||||
self, db_alias=DEFAULT_CONNECTION_NAME, collection_name="fs", **kwargs
|
||||
):
|
||||
super(FileField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
self.collection_name = collection_name
|
||||
self.db_alias = db_alias
|
||||
|
||||
@ -1840,7 +1823,7 @@ class FileField(BaseField):
|
||||
key = self.name
|
||||
if (
|
||||
hasattr(value, "read") and not isinstance(value, GridFSProxy)
|
||||
) or isinstance(value, (six.binary_type, six.string_types)):
|
||||
) or isinstance(value, (bytes, str)):
|
||||
# using "FileField() = file/string" notation
|
||||
grid_file = instance._data.get(self.name)
|
||||
# If a file already exists, delete it
|
||||
@ -1958,11 +1941,11 @@ class ImageGridFsProxy(GridFSProxy):
|
||||
|
||||
w, h = img.size
|
||||
|
||||
io = StringIO()
|
||||
io = BytesIO()
|
||||
img.save(io, img_format, progressive=progressive)
|
||||
io.seek(0)
|
||||
|
||||
return super(ImageGridFsProxy, self).put(
|
||||
return super().put(
|
||||
io, width=w, height=h, format=img_format, thumbnail_id=thumb_id, **kwargs
|
||||
)
|
||||
|
||||
@ -1972,12 +1955,12 @@ class ImageGridFsProxy(GridFSProxy):
|
||||
if out and out.thumbnail_id:
|
||||
self.fs.delete(out.thumbnail_id)
|
||||
|
||||
return super(ImageGridFsProxy, self).delete()
|
||||
return super().delete()
|
||||
|
||||
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)
|
||||
|
||||
@ -2047,16 +2030,11 @@ class ImageField(FileField):
|
||||
for att_name, att in extra_args.items():
|
||||
value = None
|
||||
if isinstance(att, (tuple, list)):
|
||||
if six.PY3:
|
||||
value = dict(
|
||||
itertools.zip_longest(params_size, att, fillvalue=None)
|
||||
)
|
||||
else:
|
||||
value = dict(map(None, params_size, att))
|
||||
value = dict(itertools.zip_longest(params_size, att, fillvalue=None))
|
||||
|
||||
setattr(self, att_name, value)
|
||||
|
||||
super(ImageField, self).__init__(collection_name=collection_name, **kwargs)
|
||||
super().__init__(collection_name=collection_name, **kwargs)
|
||||
|
||||
|
||||
class SequenceField(BaseField):
|
||||
@ -2108,14 +2086,14 @@ class SequenceField(BaseField):
|
||||
self.value_decorator = (
|
||||
value_decorator if callable(value_decorator) else self.VALUE_DECORATOR
|
||||
)
|
||||
super(SequenceField, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def generate(self):
|
||||
"""
|
||||
Generate and Increment the counter
|
||||
"""
|
||||
sequence_name = self.get_sequence_name()
|
||||
sequence_id = "%s.%s" % (sequence_name, self.name)
|
||||
sequence_id = "{}.{}".format(sequence_name, self.name)
|
||||
collection = get_db(alias=self.db_alias)[self.collection_name]
|
||||
|
||||
counter = collection.find_one_and_update(
|
||||
@ -2129,7 +2107,7 @@ class SequenceField(BaseField):
|
||||
def set_next_value(self, value):
|
||||
"""Helper method to set the next sequence value"""
|
||||
sequence_name = self.get_sequence_name()
|
||||
sequence_id = "%s.%s" % (sequence_name, self.name)
|
||||
sequence_id = "{}.{}".format(sequence_name, self.name)
|
||||
collection = get_db(alias=self.db_alias)[self.collection_name]
|
||||
counter = collection.find_one_and_update(
|
||||
filter={"_id": sequence_id},
|
||||
@ -2146,7 +2124,7 @@ class SequenceField(BaseField):
|
||||
as it is only fixed on set.
|
||||
"""
|
||||
sequence_name = self.get_sequence_name()
|
||||
sequence_id = "%s.%s" % (sequence_name, self.name)
|
||||
sequence_id = "{}.{}".format(sequence_name, self.name)
|
||||
collection = get_db(alias=self.db_alias)[self.collection_name]
|
||||
data = collection.find_one({"_id": sequence_id})
|
||||
|
||||
@ -2169,7 +2147,7 @@ class SequenceField(BaseField):
|
||||
)
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
value = super(SequenceField, self).__get__(instance, owner)
|
||||
value = super().__get__(instance, owner)
|
||||
if value is None and instance._initialised:
|
||||
value = self.generate()
|
||||
instance._data[self.name] = value
|
||||
@ -2182,7 +2160,7 @@ class SequenceField(BaseField):
|
||||
if value is None and instance._initialised:
|
||||
value = self.generate()
|
||||
|
||||
return super(SequenceField, self).__set__(instance, value)
|
||||
return super().__set__(instance, value)
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
"""
|
||||
@ -2216,14 +2194,14 @@ class UUIDField(BaseField):
|
||||
.. versionchanged:: 0.6.19
|
||||
"""
|
||||
self._binary = binary
|
||||
super(UUIDField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
if not self._binary:
|
||||
original_value = value
|
||||
try:
|
||||
if not isinstance(value, six.string_types):
|
||||
value = six.text_type(value)
|
||||
if not isinstance(value, str):
|
||||
value = str(value)
|
||||
return uuid.UUID(value)
|
||||
except (ValueError, TypeError, AttributeError):
|
||||
return original_value
|
||||
@ -2231,8 +2209,8 @@ class UUIDField(BaseField):
|
||||
|
||||
def to_mongo(self, value):
|
||||
if not self._binary:
|
||||
return six.text_type(value)
|
||||
elif isinstance(value, six.string_types):
|
||||
return str(value)
|
||||
elif isinstance(value, str):
|
||||
return uuid.UUID(value)
|
||||
return value
|
||||
|
||||
@ -2243,7 +2221,7 @@ class UUIDField(BaseField):
|
||||
|
||||
def validate(self, value):
|
||||
if not isinstance(value, uuid.UUID):
|
||||
if not isinstance(value, six.string_types):
|
||||
if not isinstance(value, str):
|
||||
value = str(value)
|
||||
try:
|
||||
uuid.UUID(value)
|
||||
@ -2442,7 +2420,7 @@ class LazyReferenceField(BaseField):
|
||||
document. Note this only work getting field (not setting or deleting).
|
||||
"""
|
||||
# XXX ValidationError raised outside of the "validate" method.
|
||||
if not isinstance(document_type, six.string_types) and not issubclass(
|
||||
if not isinstance(document_type, str) and not issubclass(
|
||||
document_type, Document
|
||||
):
|
||||
self.error(
|
||||
@ -2454,11 +2432,11 @@ class LazyReferenceField(BaseField):
|
||||
self.passthrough = passthrough
|
||||
self.document_type_obj = document_type
|
||||
self.reverse_delete_rule = reverse_delete_rule
|
||||
super(LazyReferenceField, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@property
|
||||
def document_type(self):
|
||||
if isinstance(self.document_type_obj, six.string_types):
|
||||
if isinstance(self.document_type_obj, str):
|
||||
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
||||
self.document_type_obj = self.owner_document
|
||||
else:
|
||||
@ -2497,7 +2475,7 @@ class LazyReferenceField(BaseField):
|
||||
if value:
|
||||
instance._data[self.name] = value
|
||||
|
||||
return super(LazyReferenceField, self).__get__(instance, owner)
|
||||
return super().__get__(instance, owner)
|
||||
|
||||
def to_mongo(self, value):
|
||||
if isinstance(value, LazyReference):
|
||||
@ -2561,7 +2539,7 @@ class LazyReferenceField(BaseField):
|
||||
def prepare_query_value(self, op, value):
|
||||
if value is None:
|
||||
return None
|
||||
super(LazyReferenceField, self).prepare_query_value(op, value)
|
||||
super().prepare_query_value(op, value)
|
||||
return self.to_mongo(value)
|
||||
|
||||
def lookup_member(self, member_name):
|
||||
@ -2588,12 +2566,12 @@ class GenericLazyReferenceField(GenericReferenceField):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.passthrough = kwargs.pop("passthrough", False)
|
||||
super(GenericLazyReferenceField, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def _validate_choices(self, value):
|
||||
if isinstance(value, LazyReference):
|
||||
value = value.document_type._class_name
|
||||
super(GenericLazyReferenceField, self)._validate_choices(value)
|
||||
super()._validate_choices(value)
|
||||
|
||||
def build_lazyref(self, value):
|
||||
if isinstance(value, LazyReference):
|
||||
@ -2622,7 +2600,7 @@ class GenericLazyReferenceField(GenericReferenceField):
|
||||
if value:
|
||||
instance._data[self.name] = value
|
||||
|
||||
return super(GenericLazyReferenceField, self).__get__(instance, owner)
|
||||
return super().__get__(instance, owner)
|
||||
|
||||
def validate(self, value):
|
||||
if isinstance(value, LazyReference) and value.pk is None:
|
||||
@ -2630,7 +2608,7 @@ class GenericLazyReferenceField(GenericReferenceField):
|
||||
"You can only reference documents once they have been"
|
||||
" saved to the database"
|
||||
)
|
||||
return super(GenericLazyReferenceField, self).validate(value)
|
||||
return super().validate(value)
|
||||
|
||||
def to_mongo(self, document):
|
||||
if document is None:
|
||||
@ -2649,4 +2627,4 @@ class GenericLazyReferenceField(GenericReferenceField):
|
||||
)
|
||||
)
|
||||
else:
|
||||
return super(GenericLazyReferenceField, self).to_mongo(document)
|
||||
return super().to_mongo(document)
|
||||
|
@ -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
|
@ -1,5 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import copy
|
||||
import itertools
|
||||
import re
|
||||
@ -11,8 +9,6 @@ import pymongo
|
||||
import pymongo.errors
|
||||
from pymongo.collection import ReturnDocument
|
||||
from pymongo.common import validate_read_preference
|
||||
import six
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine import signals
|
||||
from mongoengine.base import get_document
|
||||
@ -41,7 +37,7 @@ DENY = 3
|
||||
PULL = 4
|
||||
|
||||
|
||||
class BaseQuerySet(object):
|
||||
class BaseQuerySet:
|
||||
"""A set of results returned from a query. Wraps a MongoDB cursor,
|
||||
providing :class:`~mongoengine.Document` objects as the results.
|
||||
"""
|
||||
@ -203,8 +199,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):
|
||||
@ -255,21 +249,20 @@ class BaseQuerySet(object):
|
||||
queryset = queryset.filter(*q_objs, **query)
|
||||
|
||||
try:
|
||||
result = six.next(queryset)
|
||||
result = next(queryset)
|
||||
except StopIteration:
|
||||
msg = "%s matching query does not exist." % queryset._document._class_name
|
||||
raise queryset._document.DoesNotExist(msg)
|
||||
|
||||
try:
|
||||
# Check if there is another match
|
||||
six.next(queryset)
|
||||
next(queryset)
|
||||
except StopIteration:
|
||||
return result
|
||||
|
||||
# If we were able to retrieve the 2nd doc, rewind the cursor and
|
||||
# raise the MultipleObjectsReturned exception.
|
||||
# If we were able to retrieve the 2nd doc, raise the MultipleObjectsReturned exception.
|
||||
raise queryset._document.MultipleObjectsReturned(
|
||||
u"2 or more items returned, instead of 1"
|
||||
"2 or more items returned, instead of 1"
|
||||
)
|
||||
|
||||
def create(self, **kwargs):
|
||||
@ -354,20 +347,20 @@ class BaseQuerySet(object):
|
||||
)
|
||||
except pymongo.errors.DuplicateKeyError as err:
|
||||
message = "Could not save document (%s)"
|
||||
raise NotUniqueError(message % six.text_type(err))
|
||||
raise NotUniqueError(message % err)
|
||||
except pymongo.errors.BulkWriteError as err:
|
||||
# inserting documents that already have an _id field will
|
||||
# give huge performance debt or raise
|
||||
message = u"Bulk write error: (%s)"
|
||||
raise BulkWriteError(message % six.text_type(err.details))
|
||||
message = "Bulk write error: (%s)"
|
||||
raise BulkWriteError(message % err.details)
|
||||
except pymongo.errors.OperationFailure as err:
|
||||
message = "Could not save document (%s)"
|
||||
if re.match("^E1100[01] duplicate key", six.text_type(err)):
|
||||
if re.match("^E1100[01] duplicate key", str(err)):
|
||||
# E11000 - duplicate key error index
|
||||
# E11001 - duplicate key on update
|
||||
message = u"Tried to save duplicate unique keys (%s)"
|
||||
raise NotUniqueError(message % six.text_type(err))
|
||||
raise OperationError(message % six.text_type(err))
|
||||
message = "Tried to save duplicate unique keys (%s)"
|
||||
raise NotUniqueError(message % err)
|
||||
raise OperationError(message % err)
|
||||
|
||||
# Apply inserted_ids to documents
|
||||
for doc, doc_id in zip(docs, ids):
|
||||
@ -539,12 +532,12 @@ class BaseQuerySet(object):
|
||||
elif result.raw_result:
|
||||
return result.raw_result["n"]
|
||||
except pymongo.errors.DuplicateKeyError as err:
|
||||
raise NotUniqueError(u"Update failed (%s)" % six.text_type(err))
|
||||
raise NotUniqueError("Update failed (%s)" % err)
|
||||
except pymongo.errors.OperationFailure as err:
|
||||
if six.text_type(err) == u"multi not coded yet":
|
||||
message = u"update() method requires MongoDB 1.1.3+"
|
||||
if str(err) == "multi not coded yet":
|
||||
message = "update() method requires MongoDB 1.1.3+"
|
||||
raise OperationError(message)
|
||||
raise OperationError(u"Update failed (%s)" % six.text_type(err))
|
||||
raise OperationError("Update failed (%s)" % err)
|
||||
|
||||
def upsert_one(self, write_concern=None, **update):
|
||||
"""Overwrite or add the first document matched by the query.
|
||||
@ -662,9 +655,9 @@ class BaseQuerySet(object):
|
||||
**self._cursor_args
|
||||
)
|
||||
except pymongo.errors.DuplicateKeyError as err:
|
||||
raise NotUniqueError(u"Update failed (%s)" % err)
|
||||
raise NotUniqueError("Update failed (%s)" % err)
|
||||
except pymongo.errors.OperationFailure as err:
|
||||
raise OperationError(u"Update failed (%s)" % err)
|
||||
raise OperationError("Update failed (%s)" % err)
|
||||
|
||||
if full_response:
|
||||
if result["value"] is not None:
|
||||
@ -693,7 +686,7 @@ class BaseQuerySet(object):
|
||||
return queryset.filter(pk=object_id).first()
|
||||
|
||||
def in_bulk(self, object_ids):
|
||||
"""Retrieve a set of documents by their ids.
|
||||
""""Retrieve a set of documents by their ids.
|
||||
|
||||
:param object_ids: a list or tuple of ObjectId's
|
||||
:rtype: dict of ObjectId's as keys and collection-specific
|
||||
@ -989,7 +982,7 @@ class BaseQuerySet(object):
|
||||
.. versionchanged:: 0.5 - Added subfield support
|
||||
"""
|
||||
fields = {f: QueryFieldList.ONLY for f in fields}
|
||||
self.only_fields = fields.keys()
|
||||
self.only_fields = list(fields.keys())
|
||||
return self.fields(True, **fields)
|
||||
|
||||
def exclude(self, *fields):
|
||||
@ -1340,13 +1333,13 @@ class BaseQuerySet(object):
|
||||
map_f_scope = {}
|
||||
if isinstance(map_f, Code):
|
||||
map_f_scope = map_f.scope
|
||||
map_f = six.text_type(map_f)
|
||||
map_f = str(map_f)
|
||||
map_f = Code(queryset._sub_js_fields(map_f), map_f_scope)
|
||||
|
||||
reduce_f_scope = {}
|
||||
if isinstance(reduce_f, Code):
|
||||
reduce_f_scope = reduce_f.scope
|
||||
reduce_f = six.text_type(reduce_f)
|
||||
reduce_f = str(reduce_f)
|
||||
reduce_f_code = queryset._sub_js_fields(reduce_f)
|
||||
reduce_f = Code(reduce_f_code, reduce_f_scope)
|
||||
|
||||
@ -1356,7 +1349,7 @@ class BaseQuerySet(object):
|
||||
finalize_f_scope = {}
|
||||
if isinstance(finalize_f, Code):
|
||||
finalize_f_scope = finalize_f.scope
|
||||
finalize_f = six.text_type(finalize_f)
|
||||
finalize_f = str(finalize_f)
|
||||
finalize_f_code = queryset._sub_js_fields(finalize_f)
|
||||
finalize_f = Code(finalize_f_code, finalize_f_scope)
|
||||
mr_args["finalize"] = finalize_f
|
||||
@ -1372,7 +1365,7 @@ class BaseQuerySet(object):
|
||||
else:
|
||||
map_reduce_function = "map_reduce"
|
||||
|
||||
if isinstance(output, six.string_types):
|
||||
if isinstance(output, str):
|
||||
mr_args["out"] = output
|
||||
|
||||
elif isinstance(output, dict):
|
||||
@ -1559,7 +1552,7 @@ class BaseQuerySet(object):
|
||||
if self._limit == 0 or self._none:
|
||||
raise StopIteration
|
||||
|
||||
raw_doc = six.next(self._cursor)
|
||||
raw_doc = next(self._cursor)
|
||||
|
||||
if self._as_pymongo:
|
||||
return raw_doc
|
||||
@ -1804,13 +1797,13 @@ class BaseQuerySet(object):
|
||||
}
|
||||
"""
|
||||
total, data, types = self.exec_js(freq_func, field)
|
||||
values = {types.get(k): int(v) for k, v in iteritems(data)}
|
||||
values = {types.get(k): int(v) for k, v in data.items()}
|
||||
|
||||
if normalize:
|
||||
values = {k: float(v) / total for k, v in values.items()}
|
||||
|
||||
frequencies = {}
|
||||
for k, v in iteritems(values):
|
||||
for k, v in values.items():
|
||||
if isinstance(k, float):
|
||||
if int(k) == k:
|
||||
k = int(k)
|
||||
@ -1830,7 +1823,7 @@ class BaseQuerySet(object):
|
||||
field_parts = field.split(".")
|
||||
try:
|
||||
field = ".".join(
|
||||
f if isinstance(f, six.string_types) else f.db_field
|
||||
f if isinstance(f, str) else f.db_field
|
||||
for f in self._document._lookup_field(field_parts)
|
||||
)
|
||||
db_field_paths.append(field)
|
||||
@ -1842,7 +1835,7 @@ class BaseQuerySet(object):
|
||||
for subdoc in subclasses:
|
||||
try:
|
||||
subfield = ".".join(
|
||||
f if isinstance(f, six.string_types) else f.db_field
|
||||
f if isinstance(f, str) else f.db_field
|
||||
for f in subdoc._lookup_field(field_parts)
|
||||
)
|
||||
db_field_paths.append(subfield)
|
||||
@ -1916,7 +1909,7 @@ class BaseQuerySet(object):
|
||||
field_name = match.group(1).split(".")
|
||||
fields = self._document._lookup_field(field_name)
|
||||
# Substitute the correct name for the field into the javascript
|
||||
return u'["%s"]' % fields[-1].db_field
|
||||
return '["%s"]' % fields[-1].db_field
|
||||
|
||||
def field_path_sub(match):
|
||||
# Extract just the field name, and look up the field objects
|
||||
|
@ -1,7 +1,7 @@
|
||||
__all__ = ("QueryFieldList",)
|
||||
|
||||
|
||||
class QueryFieldList(object):
|
||||
class QueryFieldList:
|
||||
"""Object that handles combinations of .only() and .exclude() calls"""
|
||||
|
||||
ONLY = 1
|
||||
@ -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:
|
||||
@ -80,7 +78,7 @@ class QueryFieldList(object):
|
||||
return field_list
|
||||
|
||||
def reset(self):
|
||||
self.fields = set([])
|
||||
self.fields = set()
|
||||
self.slice = {}
|
||||
self.value = self.ONLY
|
||||
|
||||
|
@ -4,7 +4,7 @@ from mongoengine.queryset.queryset import QuerySet
|
||||
__all__ = ("queryset_manager", "QuerySetManager")
|
||||
|
||||
|
||||
class QuerySetManager(object):
|
||||
class QuerySetManager:
|
||||
"""
|
||||
The default QuerySet Manager.
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
import six
|
||||
|
||||
from mongoengine.errors import OperationError
|
||||
from mongoengine.queryset.base import (
|
||||
BaseQuerySet,
|
||||
@ -127,8 +125,8 @@ class QuerySet(BaseQuerySet):
|
||||
# Pull in ITER_CHUNK_SIZE docs from the database and store them in
|
||||
# the result cache.
|
||||
try:
|
||||
for _ in six.moves.range(ITER_CHUNK_SIZE):
|
||||
self._result_cache.append(six.next(self))
|
||||
for _ in range(ITER_CHUNK_SIZE):
|
||||
self._result_cache.append(next(self))
|
||||
except StopIteration:
|
||||
# Getting this exception means there are no more docs in the
|
||||
# db cursor. Set _has_more to False so that we can use that
|
||||
@ -143,10 +141,10 @@ class QuerySet(BaseQuerySet):
|
||||
getting the count
|
||||
"""
|
||||
if with_limit_and_skip is False:
|
||||
return super(QuerySet, self).count(with_limit_and_skip)
|
||||
return super().count(with_limit_and_skip)
|
||||
|
||||
if self._len is None:
|
||||
self._len = super(QuerySet, self).count(with_limit_and_skip)
|
||||
self._len = super().count(with_limit_and_skip)
|
||||
|
||||
return self._len
|
||||
|
||||
@ -180,9 +178,9 @@ class QuerySetNoCache(BaseQuerySet):
|
||||
return ".. queryset mid-iteration .."
|
||||
|
||||
data = []
|
||||
for _ in six.moves.range(REPR_OUTPUT_SIZE + 1):
|
||||
for _ in range(REPR_OUTPUT_SIZE + 1):
|
||||
try:
|
||||
data.append(six.next(self))
|
||||
data.append(next(self))
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
|
@ -3,14 +3,12 @@ from collections import defaultdict
|
||||
from bson import ObjectId, SON
|
||||
from bson.dbref import DBRef
|
||||
import pymongo
|
||||
import six
|
||||
from six import iteritems
|
||||
|
||||
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",
|
||||
@ -101,7 +99,7 @@ def query(_doc_cls=None, **kwargs):
|
||||
cleaned_fields = []
|
||||
for field in fields:
|
||||
append_field = True
|
||||
if isinstance(field, six.string_types):
|
||||
if isinstance(field, str):
|
||||
parts.append(field)
|
||||
append_field = False
|
||||
# is last and CachedReferenceField
|
||||
@ -180,7 +178,7 @@ def query(_doc_cls=None, **kwargs):
|
||||
"$near" in value_dict or "$nearSphere" in value_dict
|
||||
):
|
||||
value_son = SON()
|
||||
for k, v in iteritems(value_dict):
|
||||
for k, v in value_dict.items():
|
||||
if k == "$maxDistance" or k == "$minDistance":
|
||||
continue
|
||||
value_son[k] = v
|
||||
@ -281,7 +279,7 @@ def update(_doc_cls=None, **update):
|
||||
appended_sub_field = False
|
||||
for field in fields:
|
||||
append_field = True
|
||||
if isinstance(field, six.string_types):
|
||||
if isinstance(field, str):
|
||||
# Convert the S operator to $
|
||||
if field == "S":
|
||||
field = "$"
|
||||
@ -435,7 +433,9 @@ def _geo_operator(field, op, value):
|
||||
value = {"$near": _infer_geometry(value)}
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
'Geo method "%s" has not been implemented for a %s ' % (op, field._name)
|
||||
'Geo method "{}" has not been implemented for a {} '.format(
|
||||
op, field._name
|
||||
)
|
||||
)
|
||||
return value
|
||||
|
||||
|
@ -7,7 +7,7 @@ from mongoengine.queryset import transform
|
||||
__all__ = ("Q", "QNode")
|
||||
|
||||
|
||||
class QNodeVisitor(object):
|
||||
class QNodeVisitor:
|
||||
"""Base visitor class for visiting Q-object nodes in a query tree.
|
||||
"""
|
||||
|
||||
@ -79,7 +79,7 @@ class QueryCompilerVisitor(QNodeVisitor):
|
||||
return transform.query(self.document, **query.query)
|
||||
|
||||
|
||||
class QNode(object):
|
||||
class QNode:
|
||||
"""Base class for nodes in query trees."""
|
||||
|
||||
AND = 0
|
||||
@ -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
|
||||
|
||||
|
@ -15,11 +15,11 @@ try:
|
||||
signals_available = True
|
||||
except ImportError:
|
||||
|
||||
class Namespace(object):
|
||||
class Namespace:
|
||||
def signal(self, name, doc=None):
|
||||
return _FakeSignal(name, doc)
|
||||
|
||||
class _FakeSignal(object):
|
||||
class _FakeSignal:
|
||||
"""If blinker is unavailable, create a fake class with the same
|
||||
interface that allows sending of signals but will fail with an
|
||||
error on anything else. Instead of doing anything on send, it
|
||||
|
@ -1,4 +1,3 @@
|
||||
pymongo>=3.4
|
||||
six==1.10.0
|
||||
Sphinx==1.5.5
|
||||
sphinx-rtd-theme==0.2.4
|
||||
|
29
setup.py
29
setup.py
@ -97,21 +97,17 @@ CLASSIFIERS = [
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Topic :: Database",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
]
|
||||
|
||||
PYTHON_VERSION = sys.version_info[0]
|
||||
PY3 = PYTHON_VERSION == 3
|
||||
PY2 = PYTHON_VERSION == 2
|
||||
|
||||
extra_opts = {
|
||||
"packages": find_packages(exclude=["tests", "tests.*"]),
|
||||
"tests_require": [
|
||||
@ -120,20 +116,14 @@ extra_opts = {
|
||||
"coverage<5.0", # recent coverage switched to sqlite format for the .coverage file which isn't handled properly by coveralls
|
||||
"blinker",
|
||||
"Pillow>=2.0.0, <7.0.0", # 7.0.0 dropped Python2 support
|
||||
"zipp<2.0.0", # (dependency of pytest) dropped python2 support
|
||||
"pyparsing<3", # sub-dependency that dropped py2 support
|
||||
"configparser<5", # sub-dependency that dropped py2 support
|
||||
],
|
||||
}
|
||||
if PY3:
|
||||
extra_opts["use_2to3"] = True
|
||||
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",
|
||||
@ -150,7 +140,8 @@ setup(
|
||||
long_description=LONG_DESCRIPTION,
|
||||
platforms=["any"],
|
||||
classifiers=CLASSIFIERS,
|
||||
install_requires=["pymongo>=3.4, <4.0", "six>=1.10.0"],
|
||||
python_requires=">=3.5",
|
||||
install_requires=["pymongo>=3.4, <4.0"],
|
||||
cmdclass={"test": PyTest},
|
||||
**extra_opts
|
||||
)
|
||||
|
@ -5,7 +5,6 @@ from datetime import datetime
|
||||
from pymongo.collation import Collation
|
||||
from pymongo.errors import OperationFailure
|
||||
import pytest
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine import *
|
||||
from mongoengine.connection import get_db
|
||||
@ -59,7 +58,7 @@ class TestIndexes(unittest.TestCase):
|
||||
info = BlogPost.objects._collection.index_information()
|
||||
# _id, '-date', 'tags', ('cat', 'date')
|
||||
assert len(info) == 4
|
||||
info = [value["key"] for key, value in iteritems(info)]
|
||||
info = [value["key"] for key, value in info.items()]
|
||||
for expected in expected_specs:
|
||||
assert expected["fields"] in info
|
||||
|
||||
@ -87,7 +86,7 @@ class TestIndexes(unittest.TestCase):
|
||||
# the indices on -date and tags will both contain
|
||||
# _cls as first element in the key
|
||||
assert len(info) == 4
|
||||
info = [value["key"] for key, value in iteritems(info)]
|
||||
info = [value["key"] for key, value in info.items()]
|
||||
for expected in expected_specs:
|
||||
assert expected["fields"] in info
|
||||
|
||||
@ -102,7 +101,7 @@ class TestIndexes(unittest.TestCase):
|
||||
|
||||
ExtendedBlogPost.ensure_indexes()
|
||||
info = ExtendedBlogPost.objects._collection.index_information()
|
||||
info = [value["key"] for key, value in iteritems(info)]
|
||||
info = [value["key"] for key, value in info.items()]
|
||||
for expected in expected_specs:
|
||||
assert expected["fields"] in info
|
||||
|
||||
@ -192,7 +191,7 @@ class TestIndexes(unittest.TestCase):
|
||||
# Indexes are lazy so use list() to perform query
|
||||
list(Person.objects)
|
||||
info = Person.objects._collection.index_information()
|
||||
info = [value["key"] for key, value in iteritems(info)]
|
||||
info = [value["key"] for key, value in info.items()]
|
||||
assert [("rank.title", 1)] in info
|
||||
|
||||
def test_explicit_geo2d_index(self):
|
||||
@ -207,7 +206,7 @@ class TestIndexes(unittest.TestCase):
|
||||
|
||||
Place.ensure_indexes()
|
||||
info = Place._get_collection().index_information()
|
||||
info = [value["key"] for key, value in iteritems(info)]
|
||||
info = [value["key"] for key, value in info.items()]
|
||||
assert [("location.point", "2d")] in info
|
||||
|
||||
def test_explicit_geo2d_index_embedded(self):
|
||||
@ -227,7 +226,7 @@ class TestIndexes(unittest.TestCase):
|
||||
|
||||
Place.ensure_indexes()
|
||||
info = Place._get_collection().index_information()
|
||||
info = [value["key"] for key, value in iteritems(info)]
|
||||
info = [value["key"] for key, value in info.items()]
|
||||
assert [("current.location.point", "2d")] in info
|
||||
|
||||
def test_explicit_geosphere_index(self):
|
||||
@ -244,7 +243,7 @@ class TestIndexes(unittest.TestCase):
|
||||
|
||||
Place.ensure_indexes()
|
||||
info = Place._get_collection().index_information()
|
||||
info = [value["key"] for key, value in iteritems(info)]
|
||||
info = [value["key"] for key, value in info.items()]
|
||||
assert [("location.point", "2dsphere")] in info
|
||||
|
||||
def test_explicit_geohaystack_index(self):
|
||||
@ -266,7 +265,7 @@ class TestIndexes(unittest.TestCase):
|
||||
|
||||
Place.ensure_indexes()
|
||||
info = Place._get_collection().index_information()
|
||||
info = [value["key"] for key, value in iteritems(info)]
|
||||
info = [value["key"] for key, value in info.items()]
|
||||
assert [("location.point", "geoHaystack")] in info
|
||||
|
||||
def test_create_geohaystack_index(self):
|
||||
@ -279,7 +278,7 @@ class TestIndexes(unittest.TestCase):
|
||||
|
||||
Place.create_index({"fields": (")location.point", "name")}, bucketSize=10)
|
||||
info = Place._get_collection().index_information()
|
||||
info = [value["key"] for key, value in iteritems(info)]
|
||||
info = [value["key"] for key, value in info.items()]
|
||||
assert [("location.point", "geoHaystack"), ("name", 1)] in info
|
||||
|
||||
def test_dictionary_indexes(self):
|
||||
@ -308,7 +307,7 @@ class TestIndexes(unittest.TestCase):
|
||||
info = BlogPost.objects._collection.index_information()
|
||||
info = [
|
||||
(value["key"], value.get("unique", False), value.get("sparse", False))
|
||||
for key, value in iteritems(info)
|
||||
for key, value in info.items()
|
||||
]
|
||||
assert ([("addDate", -1)], True, True) in info
|
||||
|
||||
@ -889,7 +888,7 @@ class TestIndexes(unittest.TestCase):
|
||||
self.fail("Unbound local error at index + pk definition")
|
||||
|
||||
info = BlogPost.objects._collection.index_information()
|
||||
info = [value["key"] for key, value in iteritems(info)]
|
||||
info = [value["key"] for key, value in info.items()]
|
||||
index_item = [("_id", 1), ("comments.comment_id", 1)]
|
||||
assert index_item in info
|
||||
|
||||
@ -930,7 +929,7 @@ class TestIndexes(unittest.TestCase):
|
||||
meta = {"indexes": ["provider_ids.foo", "provider_ids.bar"]}
|
||||
|
||||
info = MyDoc.objects._collection.index_information()
|
||||
info = [value["key"] for key, value in iteritems(info)]
|
||||
info = [value["key"] for key, value in info.items()]
|
||||
assert [("provider_ids.foo", 1)] in info
|
||||
assert [("provider_ids.bar", 1)] in info
|
||||
|
||||
|
@ -3,7 +3,6 @@ import unittest
|
||||
import warnings
|
||||
|
||||
import pytest
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine import (
|
||||
BooleanField,
|
||||
@ -549,7 +548,7 @@ class TestInheritance(MongoDBTestCase):
|
||||
class Human(Mammal):
|
||||
pass
|
||||
|
||||
for k, v in iteritems(defaults):
|
||||
for k, v in defaults.items():
|
||||
for cls in [Animal, Fish, Guppy]:
|
||||
assert cls._meta[k] == v
|
||||
|
||||
|
@ -10,7 +10,6 @@ import bson
|
||||
from bson import DBRef, ObjectId
|
||||
from pymongo.errors import DuplicateKeyError
|
||||
import pytest
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine import *
|
||||
from mongoengine import signals
|
||||
@ -3274,7 +3273,7 @@ class TestDocumentInstance(MongoDBTestCase):
|
||||
|
||||
def expand(self):
|
||||
self.flattened_parameter = {}
|
||||
for parameter_name, parameter in iteritems(self.parameters):
|
||||
for parameter_name, parameter in self.parameters.items():
|
||||
parameter.expand()
|
||||
|
||||
class NodesSystem(Document):
|
||||
@ -3282,7 +3281,7 @@ class TestDocumentInstance(MongoDBTestCase):
|
||||
nodes = MapField(ReferenceField(Node, dbref=False))
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
for node_name, node in iteritems(self.nodes):
|
||||
for node_name, node in self.nodes.items():
|
||||
node.expand()
|
||||
node.save(*args, **kwargs)
|
||||
super(NodesSystem, self).save(*args, **kwargs)
|
||||
|
@ -3,13 +3,12 @@ import uuid
|
||||
|
||||
from bson import Binary
|
||||
import pytest
|
||||
import six
|
||||
|
||||
from mongoengine import *
|
||||
from tests.utils import MongoDBTestCase
|
||||
|
||||
BIN_VALUE = six.b(
|
||||
"\xa9\xf3\x8d(\xd7\x03\x84\xb4k[\x0f\xe3\xa2\x19\x85p[J\xa3\xd2>\xde\xe6\x87\xb1\x7f\xc6\xe6\xd9r\x18\xf5"
|
||||
BIN_VALUE = "\xa9\xf3\x8d(\xd7\x03\x84\xb4k[\x0f\xe3\xa2\x19\x85p[J\xa3\xd2>\xde\xe6\x87\xb1\x7f\xc6\xe6\xd9r\x18\xf5".encode(
|
||||
"latin-1"
|
||||
)
|
||||
|
||||
|
||||
@ -22,7 +21,7 @@ class TestBinaryField(MongoDBTestCase):
|
||||
content_type = StringField()
|
||||
blob = BinaryField()
|
||||
|
||||
BLOB = six.b("\xe6\x00\xc4\xff\x07")
|
||||
BLOB = "\xe6\x00\xc4\xff\x07".encode("latin-1")
|
||||
MIME_TYPE = "application/octet-stream"
|
||||
|
||||
Attachment.drop_collection()
|
||||
@ -32,7 +31,7 @@ class TestBinaryField(MongoDBTestCase):
|
||||
|
||||
attachment_1 = Attachment.objects().first()
|
||||
assert MIME_TYPE == attachment_1.content_type
|
||||
assert BLOB == six.binary_type(attachment_1.blob)
|
||||
assert BLOB == bytes(attachment_1.blob)
|
||||
|
||||
def test_validation_succeeds(self):
|
||||
"""Ensure that valid values can be assigned to binary fields.
|
||||
@ -47,11 +46,11 @@ class TestBinaryField(MongoDBTestCase):
|
||||
attachment_required = AttachmentRequired()
|
||||
with pytest.raises(ValidationError):
|
||||
attachment_required.validate()
|
||||
attachment_required.blob = Binary(six.b("\xe6\x00\xc4\xff\x07"))
|
||||
attachment_required.blob = Binary("\xe6\x00\xc4\xff\x07".encode("latin-1"))
|
||||
attachment_required.validate()
|
||||
|
||||
_5_BYTES = six.b("\xe6\x00\xc4\xff\x07")
|
||||
_4_BYTES = six.b("\xe6\x00\xc4\xff")
|
||||
_5_BYTES = "\xe6\x00\xc4\xff\x07".encode("latin-1")
|
||||
_4_BYTES = "\xe6\x00\xc4\xff".encode("latin-1")
|
||||
with pytest.raises(ValidationError):
|
||||
AttachmentSizeLimit(blob=_5_BYTES).validate()
|
||||
AttachmentSizeLimit(blob=_4_BYTES).validate()
|
||||
@ -123,10 +122,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"""
|
||||
@ -136,7 +132,7 @@ class TestBinaryField(MongoDBTestCase):
|
||||
|
||||
MyDocument.drop_collection()
|
||||
|
||||
bin_data = six.b("\xe6\x00\xc4\xff\x07")
|
||||
bin_data = "\xe6\x00\xc4\xff\x07".encode("latin-1")
|
||||
doc = MyDocument(bin_field=bin_data).save()
|
||||
|
||||
n_updated = MyDocument.objects(bin_field=bin_data).update_one(
|
||||
@ -144,7 +140,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
|
||||
|
@ -2,7 +2,6 @@
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
import six
|
||||
|
||||
try:
|
||||
import dateutil
|
||||
@ -89,17 +88,6 @@ class TestDateField(MongoDBTestCase):
|
||||
assert log.date == d1.date()
|
||||
assert log.date == d2.date()
|
||||
|
||||
if not six.PY3:
|
||||
# Pre UTC dates microseconds below 1000 are dropped
|
||||
# This does not seem to be true in PY3
|
||||
d1 = datetime.datetime(1969, 12, 31, 23, 59, 59, 999)
|
||||
d2 = datetime.datetime(1969, 12, 31, 23, 59, 59)
|
||||
log.date = d1
|
||||
log.save()
|
||||
log.reload()
|
||||
assert log.date == d1.date()
|
||||
assert log.date == d2.date()
|
||||
|
||||
def test_regular_usage(self):
|
||||
"""Tests for regular datetime fields"""
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
import datetime as dt
|
||||
|
||||
import pytest
|
||||
import six
|
||||
|
||||
try:
|
||||
import dateutil
|
||||
@ -98,17 +97,6 @@ class TestDateTimeField(MongoDBTestCase):
|
||||
assert log.date != d1
|
||||
assert log.date == d2
|
||||
|
||||
if not six.PY3:
|
||||
# Pre UTC dates microseconds below 1000 are dropped
|
||||
# This does not seem to be true in PY3
|
||||
d1 = dt.datetime(1969, 12, 31, 23, 59, 59, 999)
|
||||
d2 = dt.datetime(1969, 12, 31, 23, 59, 59)
|
||||
log.date = d1
|
||||
log.save()
|
||||
log.reload()
|
||||
assert log.date != d1
|
||||
assert log.date == d2
|
||||
|
||||
def test_regular_usage(self):
|
||||
"""Tests for regular datetime fields"""
|
||||
|
||||
@ -213,7 +201,7 @@ class TestDateTimeField(MongoDBTestCase):
|
||||
# make sure that passing a parsable datetime works
|
||||
dtd = DTDoc()
|
||||
dtd.date = date_str
|
||||
assert isinstance(dtd.date, six.string_types)
|
||||
assert isinstance(dtd.date, str)
|
||||
dtd.save()
|
||||
dtd.reload()
|
||||
|
||||
|
@ -75,7 +75,7 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
|
||||
# Test non exiting attribute
|
||||
with pytest.raises(InvalidQueryError) as exc_info:
|
||||
Person.objects(settings__notexist="bar").first()
|
||||
assert unicode(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||
assert str(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||
|
||||
with pytest.raises(LookUpError):
|
||||
Person.objects.only("settings.notexist")
|
||||
@ -111,7 +111,7 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
|
||||
# Test non exiting attribute
|
||||
with pytest.raises(InvalidQueryError) as exc_info:
|
||||
assert Person.objects(settings__notexist="bar").first().id == p.id
|
||||
assert unicode(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||
assert str(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||
|
||||
# Test existing attribute
|
||||
assert Person.objects(settings__base_foo="basefoo").first().id == p.id
|
||||
@ -319,7 +319,7 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
|
||||
# Test non exiting attribute
|
||||
with pytest.raises(InvalidQueryError) as exc_info:
|
||||
Person.objects(settings__notexist="bar").first()
|
||||
assert unicode(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||
assert str(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||
|
||||
with pytest.raises(LookUpError):
|
||||
Person.objects.only("settings.notexist")
|
||||
@ -347,7 +347,7 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
|
||||
# Test non exiting attribute
|
||||
with pytest.raises(InvalidQueryError) as exc_info:
|
||||
assert Person.objects(settings__notexist="bar").first().id == p.id
|
||||
assert unicode(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||
assert str(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||
|
||||
# Test existing attribute
|
||||
assert Person.objects(settings__base_foo="basefoo").first().id == p.id
|
||||
|
@ -3,14 +3,13 @@ import copy
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from io import BytesIO
|
||||
|
||||
import gridfs
|
||||
import pytest
|
||||
import six
|
||||
|
||||
from mongoengine import *
|
||||
from mongoengine.connection import get_db
|
||||
from mongoengine.python_support import StringIO
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
@ -30,7 +29,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)
|
||||
@ -58,7 +57,7 @@ class TestFileField(MongoDBTestCase):
|
||||
|
||||
PutFile.drop_collection()
|
||||
|
||||
text = six.b("Hello, World!")
|
||||
text = "Hello, World!".encode("latin-1")
|
||||
content_type = "text/plain"
|
||||
|
||||
putfile = PutFile()
|
||||
@ -80,7 +79,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)
|
||||
@ -101,8 +100,8 @@ class TestFileField(MongoDBTestCase):
|
||||
|
||||
StreamFile.drop_collection()
|
||||
|
||||
text = six.b("Hello, World!")
|
||||
more_text = six.b("Foo Bar")
|
||||
text = "Hello, World!".encode("latin-1")
|
||||
more_text = "Foo Bar".encode("latin-1")
|
||||
content_type = "text/plain"
|
||||
|
||||
streamfile = StreamFile()
|
||||
@ -137,8 +136,8 @@ class TestFileField(MongoDBTestCase):
|
||||
|
||||
StreamFile.drop_collection()
|
||||
|
||||
text = six.b("Hello, World!")
|
||||
more_text = six.b("Foo Bar")
|
||||
text = "Hello, World!".encode("latin-1")
|
||||
more_text = "Foo Bar".encode("latin-1")
|
||||
|
||||
streamfile = StreamFile()
|
||||
streamfile.save()
|
||||
@ -167,8 +166,8 @@ class TestFileField(MongoDBTestCase):
|
||||
class SetFile(Document):
|
||||
the_file = FileField()
|
||||
|
||||
text = six.b("Hello, World!")
|
||||
more_text = six.b("Foo Bar")
|
||||
text = "Hello, World!".encode("latin-1")
|
||||
more_text = "Foo Bar".encode("latin-1")
|
||||
|
||||
SetFile.drop_collection()
|
||||
|
||||
@ -196,7 +195,7 @@ class TestFileField(MongoDBTestCase):
|
||||
GridDocument.drop_collection()
|
||||
|
||||
with tempfile.TemporaryFile() as f:
|
||||
f.write(six.b("Hello World!"))
|
||||
f.write("Hello World!".encode("latin-1"))
|
||||
f.flush()
|
||||
|
||||
# Test without default
|
||||
@ -213,7 +212,7 @@ class TestFileField(MongoDBTestCase):
|
||||
assert doc_b.the_file.grid_id == doc_c.the_file.grid_id
|
||||
|
||||
# Test with default
|
||||
doc_d = GridDocument(the_file=six.b(""))
|
||||
doc_d = GridDocument(the_file="".encode("latin-1"))
|
||||
doc_d.save()
|
||||
|
||||
doc_e = GridDocument.objects.with_id(doc_d.id)
|
||||
@ -240,7 +239,7 @@ class TestFileField(MongoDBTestCase):
|
||||
# First instance
|
||||
test_file = TestFile()
|
||||
test_file.name = "Hello, World!"
|
||||
test_file.the_file.put(six.b("Hello, World!"))
|
||||
test_file.the_file.put("Hello, World!".encode("latin-1"))
|
||||
test_file.save()
|
||||
|
||||
# Second instance
|
||||
@ -297,7 +296,9 @@ class TestFileField(MongoDBTestCase):
|
||||
|
||||
test_file = TestFile()
|
||||
assert not bool(test_file.the_file)
|
||||
test_file.the_file.put(six.b("Hello, World!"), content_type="text/plain")
|
||||
test_file.the_file.put(
|
||||
"Hello, World!".encode("latin-1"), content_type="text/plain"
|
||||
)
|
||||
test_file.save()
|
||||
assert bool(test_file.the_file)
|
||||
|
||||
@ -319,7 +320,7 @@ class TestFileField(MongoDBTestCase):
|
||||
class TestFile(Document):
|
||||
the_file = FileField()
|
||||
|
||||
text = six.b("Hello, World!")
|
||||
text = "Hello, World!".encode("latin-1")
|
||||
content_type = "text/plain"
|
||||
|
||||
testfile = TestFile()
|
||||
@ -363,7 +364,7 @@ class TestFileField(MongoDBTestCase):
|
||||
testfile.the_file.put(text, content_type=content_type, filename="hello")
|
||||
testfile.save()
|
||||
|
||||
text = six.b("Bonjour, World!")
|
||||
text = "Bonjour, World!".encode("latin-1")
|
||||
testfile.the_file.replace(text, content_type=content_type, filename="hello")
|
||||
testfile.save()
|
||||
|
||||
@ -387,7 +388,7 @@ class TestFileField(MongoDBTestCase):
|
||||
TestImage.drop_collection()
|
||||
|
||||
with tempfile.TemporaryFile() as f:
|
||||
f.write(six.b("Hello World!"))
|
||||
f.write("Hello World!".encode("latin-1"))
|
||||
f.flush()
|
||||
|
||||
t = TestImage()
|
||||
@ -503,21 +504,21 @@ class TestFileField(MongoDBTestCase):
|
||||
# First instance
|
||||
test_file = TestFile()
|
||||
test_file.name = "Hello, World!"
|
||||
test_file.the_file.put(six.b("Hello, World!"), name="hello.txt")
|
||||
test_file.the_file.put("Hello, World!".encode("latin-1"), name="hello.txt")
|
||||
test_file.save()
|
||||
|
||||
data = get_db("test_files").macumba.files.find_one()
|
||||
assert data.get("name") == "hello.txt"
|
||||
|
||||
test_file = TestFile.objects.first()
|
||||
assert test_file.the_file.read() == six.b("Hello, World!")
|
||||
assert test_file.the_file.read() == "Hello, World!".encode("latin-1")
|
||||
|
||||
test_file = TestFile.objects.first()
|
||||
test_file.the_file = six.b("HELLO, WORLD!")
|
||||
test_file.the_file = "Hello, World!".encode("latin-1")
|
||||
test_file.save()
|
||||
|
||||
test_file = TestFile.objects.first()
|
||||
assert test_file.the_file.read() == six.b("HELLO, WORLD!")
|
||||
assert test_file.the_file.read() == "Hello, World!".encode("latin-1")
|
||||
|
||||
def test_copyable(self):
|
||||
class PutFile(Document):
|
||||
@ -525,7 +526,7 @@ class TestFileField(MongoDBTestCase):
|
||||
|
||||
PutFile.drop_collection()
|
||||
|
||||
text = six.b("Hello, World!")
|
||||
text = "Hello, World!".encode("latin-1")
|
||||
content_type = "text/plain"
|
||||
|
||||
putfile = PutFile()
|
||||
|
@ -1,6 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
import six
|
||||
|
||||
from mongoengine import *
|
||||
|
||||
@ -52,9 +51,8 @@ class TestFloatField(MongoDBTestCase):
|
||||
|
||||
big_person = BigPerson()
|
||||
|
||||
for value, value_type in enumerate(six.integer_types):
|
||||
big_person.height = value_type(value)
|
||||
big_person.validate()
|
||||
big_person.height = int(0)
|
||||
big_person.validate()
|
||||
|
||||
big_person.height = 2 ** 500
|
||||
big_person.validate()
|
||||
|
@ -1,11 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.int64 import Int64
|
||||
import pytest
|
||||
import six
|
||||
|
||||
try:
|
||||
from bson.int64 import Int64
|
||||
except ImportError:
|
||||
Int64 = long
|
||||
|
||||
from mongoengine import *
|
||||
from mongoengine.connection import get_db
|
||||
@ -28,7 +22,7 @@ class TestLongField(MongoDBTestCase):
|
||||
assert isinstance(
|
||||
db.test_long_field_considered_as_int64.find()[0]["some_long"], Int64
|
||||
)
|
||||
assert isinstance(doc.some_long, six.integer_types)
|
||||
assert isinstance(doc.some_long, int)
|
||||
|
||||
def test_long_validation(self):
|
||||
"""Ensure that invalid values cannot be assigned to long fields.
|
||||
|
@ -21,7 +21,7 @@ class TestSequenceField(MongoDBTestCase):
|
||||
assert c["next"] == 10
|
||||
|
||||
ids = [i.id for i in Person.objects]
|
||||
assert ids == range(1, 11)
|
||||
assert ids == list(range(1, 11))
|
||||
|
||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||
assert c["next"] == 10
|
||||
@ -76,7 +76,7 @@ class TestSequenceField(MongoDBTestCase):
|
||||
assert c["next"] == 10
|
||||
|
||||
ids = [i.id for i in Person.objects]
|
||||
assert ids == range(1, 11)
|
||||
assert ids == list(range(1, 11))
|
||||
|
||||
c = self.db["mongoengine.counters"].find_one({"_id": "jelly.id"})
|
||||
assert c["next"] == 10
|
||||
@ -101,10 +101,10 @@ class TestSequenceField(MongoDBTestCase):
|
||||
assert c["next"] == 10
|
||||
|
||||
ids = [i.id for i in Person.objects]
|
||||
assert ids == range(1, 11)
|
||||
assert ids == list(range(1, 11))
|
||||
|
||||
counters = [i.counter for i in Person.objects]
|
||||
assert counters == range(1, 11)
|
||||
assert counters == list(range(1, 11))
|
||||
|
||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||
assert c["next"] == 10
|
||||
@ -166,10 +166,10 @@ class TestSequenceField(MongoDBTestCase):
|
||||
assert c["next"] == 10
|
||||
|
||||
ids = [i.id for i in Person.objects]
|
||||
assert ids == range(1, 11)
|
||||
assert ids == list(range(1, 11))
|
||||
|
||||
id = [i.id for i in Animal.objects]
|
||||
assert id == range(1, 11)
|
||||
assert id == list(range(1, 11))
|
||||
|
||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||
assert c["next"] == 10
|
||||
@ -193,7 +193,7 @@ class TestSequenceField(MongoDBTestCase):
|
||||
assert c["next"] == 10
|
||||
|
||||
ids = [i.id for i in Person.objects]
|
||||
assert ids == map(str, range(1, 11))
|
||||
assert ids == [str(i) for i in range(1, 11)]
|
||||
|
||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||
assert c["next"] == 10
|
||||
@ -267,12 +267,12 @@ class TestSequenceField(MongoDBTestCase):
|
||||
foo = Foo(name="Foo")
|
||||
foo.save()
|
||||
|
||||
assert not (
|
||||
"base.counter" in self.db["mongoengine.counters"].find().distinct("_id")
|
||||
assert "base.counter" not in self.db["mongoengine.counters"].find().distinct(
|
||||
"_id"
|
||||
)
|
||||
assert ("foo.counter" and "bar.counter") in self.db[
|
||||
"mongoengine.counters"
|
||||
].find().distinct("_id")
|
||||
existing_counters = self.db["mongoengine.counters"].find().distinct("_id")
|
||||
assert "foo.counter" in existing_counters
|
||||
assert "bar.counter" in existing_counters
|
||||
assert foo.counter == bar.counter
|
||||
assert foo._fields["counter"].owner_document == Foo
|
||||
assert bar._fields["counter"].owner_document == Bar
|
||||
|
@ -2,7 +2,6 @@
|
||||
import pytest
|
||||
|
||||
from mongoengine import *
|
||||
|
||||
from tests.utils import MongoDBTestCase
|
||||
|
||||
|
||||
@ -35,7 +34,7 @@ class TestURLField(MongoDBTestCase):
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
link.validate()
|
||||
assert (
|
||||
unicode(exc_info.value)
|
||||
str(exc_info.value)
|
||||
== u"ValidationError (Link:None) (Invalid URL: http://\u043f\u0440\u0438\u0432\u0435\u0442.com: ['url'])"
|
||||
)
|
||||
|
||||
|
@ -10,8 +10,6 @@ import pymongo
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from pymongo.results import UpdateResult
|
||||
import pytest
|
||||
import six
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine import *
|
||||
from mongoengine.connection import get_db
|
||||
@ -110,7 +108,7 @@ class TestQueryset(unittest.TestCase):
|
||||
# Filter people by age
|
||||
people = self.Person.objects(age=20)
|
||||
assert people.count() == 1
|
||||
person = people.next()
|
||||
person = next(people)
|
||||
assert person == user_a
|
||||
assert person.name == "User A"
|
||||
assert person.age == 20
|
||||
@ -2783,7 +2781,7 @@ class TestQueryset(unittest.TestCase):
|
||||
)
|
||||
|
||||
# start a map/reduce
|
||||
cursor.next()
|
||||
next(cursor)
|
||||
|
||||
results = Person.objects.map_reduce(
|
||||
map_f=map_person,
|
||||
@ -4108,7 +4106,7 @@ class TestQueryset(unittest.TestCase):
|
||||
info = Comment.objects._collection.index_information()
|
||||
info = [
|
||||
(value["key"], value.get("unique", False), value.get("sparse", False))
|
||||
for key, value in iteritems(info)
|
||||
for key, value in info.items()
|
||||
]
|
||||
assert ([("_cls", 1), ("message", 1)], False, False) in info
|
||||
|
||||
@ -4410,7 +4408,7 @@ class TestQueryset(unittest.TestCase):
|
||||
# Use a query to filter the people found to just person1
|
||||
people = self.Person.objects(age=20).scalar("name")
|
||||
assert people.count() == 1
|
||||
person = people.next()
|
||||
person = next(people)
|
||||
assert person == "User A"
|
||||
|
||||
# Test limit
|
||||
@ -4460,24 +4458,14 @@ class TestQueryset(unittest.TestCase):
|
||||
"A0" == "%s" % self.Person.objects.order_by("name").scalar("name").first()
|
||||
)
|
||||
assert "A0" == "%s" % self.Person.objects.scalar("name").order_by("name")[0]
|
||||
if six.PY3:
|
||||
assert (
|
||||
"['A1', 'A2']"
|
||||
== "%s" % self.Person.objects.order_by("age").scalar("name")[1:3]
|
||||
)
|
||||
assert (
|
||||
"['A51', 'A52']"
|
||||
== "%s" % self.Person.objects.order_by("age").scalar("name")[51:53]
|
||||
)
|
||||
else:
|
||||
assert (
|
||||
"[u'A1', u'A2']"
|
||||
== "%s" % self.Person.objects.order_by("age").scalar("name")[1:3]
|
||||
)
|
||||
assert (
|
||||
"[u'A51', u'A52']"
|
||||
== "%s" % self.Person.objects.order_by("age").scalar("name")[51:53]
|
||||
)
|
||||
assert (
|
||||
"['A1', 'A2']"
|
||||
== "%s" % self.Person.objects.order_by("age").scalar("name")[1:3]
|
||||
)
|
||||
assert (
|
||||
"['A51', 'A52']"
|
||||
== "%s" % self.Person.objects.order_by("age").scalar("name")[51:53]
|
||||
)
|
||||
|
||||
# with_id and in_bulk
|
||||
person = self.Person.objects.order_by("name").first()
|
||||
@ -4485,10 +4473,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_fields(self):
|
||||
@ -5377,7 +5362,7 @@ class TestQueryset(unittest.TestCase):
|
||||
if not test:
|
||||
raise AssertionError("Cursor has data and returned False")
|
||||
|
||||
queryset.next()
|
||||
next(queryset)
|
||||
if not queryset:
|
||||
raise AssertionError(
|
||||
"Cursor has data and it must returns True, even in the last item."
|
||||
|
@ -1,7 +1,6 @@
|
||||
import unittest
|
||||
|
||||
import pytest
|
||||
from six import iterkeys
|
||||
|
||||
from mongoengine import Document
|
||||
from mongoengine.base.datastructures import BaseDict, BaseList, StrictDict
|
||||
@ -287,7 +286,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 +295,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]
|
||||
|
||||
@ -372,7 +371,7 @@ class TestStrictDict(unittest.TestCase):
|
||||
|
||||
def test_iterkeys(self):
|
||||
d = self.dtype(a=1)
|
||||
assert list(iterkeys(d)) == ["a"]
|
||||
assert list(d.keys()) == ["a"]
|
||||
|
||||
def test_len(self):
|
||||
d = self.dtype(a=1)
|
||||
|
@ -2,10 +2,8 @@
|
||||
import unittest
|
||||
|
||||
from bson import DBRef, ObjectId
|
||||
from six import iteritems
|
||||
|
||||
from mongoengine import *
|
||||
from mongoengine.connection import get_db
|
||||
from mongoengine.context_managers import query_counter
|
||||
|
||||
|
||||
@ -739,7 +737,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 2
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert isinstance(m, User)
|
||||
|
||||
# Document select_related
|
||||
@ -752,7 +750,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 2
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert isinstance(m, User)
|
||||
|
||||
# Queryset select_related
|
||||
@ -766,7 +764,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 2
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert isinstance(m, User)
|
||||
|
||||
User.drop_collection()
|
||||
@ -820,7 +818,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 4
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert "User" in m.__class__.__name__
|
||||
|
||||
# Document select_related
|
||||
@ -836,7 +834,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 4
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert "User" in m.__class__.__name__
|
||||
|
||||
# Queryset select_related
|
||||
@ -853,7 +851,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 4
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert "User" in m.__class__.__name__
|
||||
|
||||
Group.objects.delete()
|
||||
@ -910,7 +908,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 2
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert isinstance(m, UserA)
|
||||
|
||||
# Document select_related
|
||||
@ -926,7 +924,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 2
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert isinstance(m, UserA)
|
||||
|
||||
# Queryset select_related
|
||||
@ -943,7 +941,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 2
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert isinstance(m, UserA)
|
||||
|
||||
UserA.drop_collection()
|
||||
@ -997,7 +995,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 4
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert "User" in m.__class__.__name__
|
||||
|
||||
# Document select_related
|
||||
@ -1013,7 +1011,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 4
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert "User" in m.__class__.__name__
|
||||
|
||||
# Queryset select_related
|
||||
@ -1030,7 +1028,7 @@ class FieldTest(unittest.TestCase):
|
||||
[m for m in group_obj.members]
|
||||
assert q == 4
|
||||
|
||||
for k, m in iteritems(group_obj.members):
|
||||
for k, m in group_obj.members.items():
|
||||
assert "User" in m.__class__.__name__
|
||||
|
||||
Group.objects.delete()
|
||||
|
@ -58,7 +58,9 @@ class TestSignal(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def post_save(cls, sender, document, **kwargs):
|
||||
dirty_keys = document._delta()[0].keys() + document._delta()[1].keys()
|
||||
dirty_keys = list(document._delta()[0].keys()) + list(
|
||||
document._delta()[1].keys()
|
||||
)
|
||||
signal_output.append("post_save signal, %s" % document)
|
||||
signal_output.append("post_save dirty keys, %s" % dirty_keys)
|
||||
if kwargs.pop("created", False):
|
||||
|
Loading…
x
Reference in New Issue
Block a user