diff --git a/mongoengine/__init__.py b/mongoengine/__init__.py index e0474776..552cbf29 100644 --- a/mongoengine/__init__.py +++ b/mongoengine/__init__.py @@ -30,8 +30,6 @@ def get_version(): """Return the VERSION as a string, e.g. for VERSION == (0, 10, 7), return '0.10.7'. """ - if isinstance(VERSION[-1], basestring): - return '.'.join(map(str, VERSION[:-1])) + VERSION[-1] return '.'.join(map(str, VERSION)) diff --git a/mongoengine/base/document.py b/mongoengine/base/document.py index 15c5c851..a2acae00 100644 --- a/mongoengine/base/document.py +++ b/mongoengine/base/document.py @@ -762,7 +762,7 @@ class BaseDocument(object): def _build_index_spec(cls, spec): """Build a PyMongo index spec from a MongoEngine index spec. """ - if isinstance(spec, basestring): + if isinstance(spec, six.string_types): spec = {'fields': [spec]} elif isinstance(spec, (list, tuple)): spec = {'fields': list(spec)} @@ -856,7 +856,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, basestring): + if isinstance(field.unique_with, six.string_types): field.unique_with = [field.unique_with] # Convert unique_with field names to real field names diff --git a/mongoengine/base/fields.py b/mongoengine/base/fields.py index b836458c..ba001b23 100644 --- a/mongoengine/base/fields.py +++ b/mongoengine/base/fields.py @@ -299,7 +299,7 @@ class ComplexBaseField(BaseField): def to_python(self, value): """Convert a MongoDB-compatible type to a Python type. """ - if isinstance(value, basestring): + if isinstance(value, six.string_types): return value if hasattr(value, 'to_python'): @@ -345,7 +345,7 @@ class ComplexBaseField(BaseField): EmbeddedDocument = _import_class("EmbeddedDocument") GenericReferenceField = _import_class("GenericReferenceField") - if isinstance(value, basestring): + if isinstance(value, six.string_types): return value if hasattr(value, 'to_mongo'): diff --git a/mongoengine/connection.py b/mongoengine/connection.py index 826b617a..14faba92 100644 --- a/mongoengine/connection.py +++ b/mongoengine/connection.py @@ -160,7 +160,7 @@ def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False): # Discard port since it can't be used on MongoReplicaSetClient conn_settings.pop('port', None) # Discard replicaSet if not base string - if not isinstance(conn_settings['replicaSet'], basestring): + if not isinstance(conn_settings['replicaSet'], six.string_types): conn_settings.pop('replicaSet', None) if not IS_PYMONGO_3: connection_class = MongoReplicaSetClient diff --git a/mongoengine/dereference.py b/mongoengine/dereference.py index c5157d50..8f30435e 100644 --- a/mongoengine/dereference.py +++ b/mongoengine/dereference.py @@ -25,7 +25,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, basestring): + if items is None or isinstance(items, six.string_types): return items # cheapest way to convert a queryset to a list diff --git a/mongoengine/document.py b/mongoengine/document.py index 40c2a6cf..2663be6f 100644 --- a/mongoengine/document.py +++ b/mongoengine/document.py @@ -33,7 +33,7 @@ def includes_cls(fields): first_field = None if len(fields): - if isinstance(fields[0], basestring): + if isinstance(fields[0], six.string_types): first_field = fields[0] elif isinstance(fields[0], (list, tuple)) and len(fields[0]): first_field = fields[0][0] @@ -146,21 +146,17 @@ class Document(BaseDocument): __slots__ = ('__objects',) - def pk(): - """Primary key alias - """ + @property + def pk(self): + """Get the primary key.""" + if 'id_field' not in self._meta: + return None + return getattr(self, self._meta['id_field']) - def fget(self): - if 'id_field' not in self._meta: - return None - return getattr(self, self._meta['id_field']) - - def fset(self, value): - return setattr(self, self._meta['id_field'], value) - - return property(fget, fset) - - pk = pk() + @pk.setter + def pk(self, value): + """Set the primary key.""" + return setattr(self, self._meta['id_field'], value) @classmethod def _get_db(cls): @@ -208,7 +204,7 @@ class Document(BaseDocument): cls.ensure_indexes() return cls._collection - def modify(self, query={}, **update): + def modify(self, query=None, **update): """Perform an atomic update of the document in the database and reload the document object using updated version. @@ -222,6 +218,8 @@ class Document(BaseDocument): database matches the query :param update: Django-style update keyword arguments """ + if query is None: + query = {} if self.pk is None: raise InvalidDocumentError("The document does not have a primary key.") @@ -412,9 +410,10 @@ class Document(BaseDocument): self._created = False return self - def cascade_save(self, *args, **kwargs): - """Recursively saves any references / - generic references on the document""" + def cascade_save(self, **kwargs): + """Recursively save any references and generic references on the + document. + """ _refs = kwargs.get('_refs', []) or [] ReferenceField = _import_class('ReferenceField') @@ -441,16 +440,17 @@ class Document(BaseDocument): @property def _qs(self): - """ - Returns the queryset to use for updating / reloading / deletions - """ + """Return the queryset to use for updating / reloading / deletions.""" if not hasattr(self, '__objects'): self.__objects = QuerySet(self, self._get_collection()) return self.__objects @property def _object_key(self): - """Dict to identify object in collection + """Get the query dict that can be used to fetch this object from + the database. Most of the time it's a simple PK lookup, but in + case of a sharded collection with a compound shard key, it can + contain a more complex query. """ select_dict = {'pk': self.pk} shard_key = self.__class__._meta.get('shard_key', tuple()) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 3e24dd1d..2c3f302a 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -78,7 +78,7 @@ class StringField(BaseField): return value def validate(self, value): - if not isinstance(value, basestring): + if not isinstance(value, six.string_types): self.error('StringField only accepts string values') if self.max_length is not None and len(value) > self.max_length: @@ -94,7 +94,7 @@ class StringField(BaseField): return None def prepare_query_value(self, op, value): - if not isinstance(op, basestring): + if not isinstance(op, six.string_types): return value if op.lstrip('i') in ('startswith', 'endswith', 'contains', 'exact'): @@ -349,7 +349,7 @@ class DecimalField(BaseField): def validate(self, value): if not isinstance(value, decimal.Decimal): - if not isinstance(value, basestring): + if not isinstance(value, six.string_types): value = six.text_type(value) try: value = decimal.Decimal(value) @@ -413,7 +413,7 @@ class DateTimeField(BaseField): if callable(value): return value() - if not isinstance(value, basestring): + if not isinstance(value, six.string_types): return None # Attempt to parse a datetime: @@ -540,16 +540,19 @@ class EmbeddedDocumentField(BaseField): """ def __init__(self, document_type, **kwargs): - if not isinstance(document_type, basestring): - if not issubclass(document_type, EmbeddedDocument): - self.error('Invalid embedded document class provided to an ' - 'EmbeddedDocumentField') + if ( + not isinstance(document_type, six.string_types) and + not issubclass(document_type, EmbeddedDocument) + ): + self.error('Invalid embedded document class provided to an ' + 'EmbeddedDocumentField') + self.document_type_obj = document_type super(EmbeddedDocumentField, self).__init__(**kwargs) @property def document_type(self): - if isinstance(self.document_type_obj, basestring): + if isinstance(self.document_type_obj, six.string_types): if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT: self.document_type_obj = self.owner_document else: @@ -634,7 +637,7 @@ class DynamicField(BaseField): """Convert a Python type to a MongoDB compatible type. """ - if isinstance(value, basestring): + if isinstance(value, six.string_types): return value if hasattr(value, 'to_mongo'): @@ -677,7 +680,7 @@ class DynamicField(BaseField): return member_name def prepare_query_value(self, op, value): - if isinstance(value, basestring): + if isinstance(value, six.string_types): return StringField().prepare_query_value(op, value) return super(DynamicField, self).prepare_query_value(op, self.to_mongo(value)) @@ -705,14 +708,14 @@ class ListField(ComplexBaseField): """Make sure that a list of valid fields is being used. """ if (not isinstance(value, (list, tuple, QuerySet)) or - isinstance(value, basestring)): + isinstance(value, six.string_types)): self.error('Only lists and tuples may be used in a list field') super(ListField, self).validate(value) def prepare_query_value(self, op, value): if self.field: if op in ('set', 'unset', None) and ( - not isinstance(value, basestring) and + not isinstance(value, six.string_types) and not isinstance(value, BaseDocument) and hasattr(value, '__iter__')): return [self.field.prepare_query_value(op, v) for v in value] @@ -782,7 +785,7 @@ def key_not_string(d): not a string. """ for k, v in d.items(): - if not isinstance(k, basestring) or (isinstance(v, dict) and key_not_string(v)): + if not isinstance(k, six.string_types) or (isinstance(v, dict) and key_not_string(v)): return True @@ -838,7 +841,7 @@ class DictField(ComplexBaseField): 'istartswith', 'endswith', 'iendswith', 'exact', 'iexact'] - if op in match_operators and isinstance(value, basestring): + if op in match_operators and isinstance(value, six.string_types): return StringField().prepare_query_value(op, value) if hasattr(self.field, 'field'): @@ -914,10 +917,12 @@ class ReferenceField(BaseField): A reference to an abstract document type is always stored as a :class:`~pymongo.dbref.DBRef`, regardless of the value of `dbref`. """ - if not isinstance(document_type, basestring): - if not issubclass(document_type, (Document, basestring)): - self.error('Argument to ReferenceField constructor must be a ' - 'document class or a string') + if ( + not isinstance(document_type, six.string_types) and + not issubclass(document_type, Document) + ): + self.error('Argument to ReferenceField constructor must be a ' + 'document class or a string') self.dbref = dbref self.document_type_obj = document_type @@ -926,7 +931,7 @@ class ReferenceField(BaseField): @property def document_type(self): - if isinstance(self.document_type_obj, basestring): + if isinstance(self.document_type_obj, six.string_types): if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT: self.document_type_obj = self.owner_document else: @@ -1039,8 +1044,10 @@ class CachedReferenceField(BaseField): :param fields: A list of fields to be cached in document :param auto_sync: if True documents are auto updated. """ - if not isinstance(document_type, basestring) and \ - not issubclass(document_type, (Document, basestring)): + if ( + not isinstance(document_type, six.string_types) and + not issubclass(document_type, Document) + ): self.error('Argument to CachedReferenceField constructor must be a' ' document class or a string') @@ -1080,7 +1087,7 @@ class CachedReferenceField(BaseField): @property def document_type(self): - if isinstance(self.document_type_obj, basestring): + if isinstance(self.document_type_obj, six.string_types): if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT: self.document_type_obj = self.owner_document else: @@ -1194,13 +1201,13 @@ class GenericReferenceField(BaseField): # Keep the choices as a list of allowed Document class names if choices: for choice in choices: - if isinstance(choice, basestring): + if isinstance(choice, six.string_types): self.choices.append(choice) elif isinstance(choice, type) and issubclass(choice, Document): self.choices.append(choice._class_name) else: self.error('Invalid choices provided: must be a list of' - 'Document subclasses and/or basestrings') + 'Document subclasses and/or six.string_typess') def _validate_choices(self, value): if isinstance(value, dict): @@ -1866,7 +1873,7 @@ class UUIDField(BaseField): if not self._binary: original_value = value try: - if not isinstance(value, basestring): + if not isinstance(value, six.string_types): value = six.text_type(value) return uuid.UUID(value) except Exception: @@ -1876,7 +1883,7 @@ class UUIDField(BaseField): def to_mongo(self, value): if not self._binary: return six.text_type(value) - elif isinstance(value, basestring): + elif isinstance(value, six.string_types): return uuid.UUID(value) return value @@ -1887,7 +1894,7 @@ class UUIDField(BaseField): def validate(self, value): if not isinstance(value, uuid.UUID): - if not isinstance(value, basestring): + if not isinstance(value, six.string_types): value = str(value) try: uuid.UUID(value) diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py index d07b6d20..051081ef 100644 --- a/mongoengine/queryset/base.py +++ b/mongoengine/queryset/base.py @@ -1188,7 +1188,7 @@ class BaseQuerySet(object): else: map_reduce_function = 'map_reduce' - if isinstance(output, basestring): + if isinstance(output, six.string_types): mr_args['out'] = output elif isinstance(output, dict): diff --git a/mongoengine/queryset/transform.py b/mongoengine/queryset/transform.py index 83eb5b74..230f3ec0 100644 --- a/mongoengine/queryset/transform.py +++ b/mongoengine/queryset/transform.py @@ -2,6 +2,7 @@ from collections import defaultdict from bson import SON import pymongo +import six from mongoengine.base.fields import UPDATE_OPERATORS from mongoengine.common import _import_class @@ -66,7 +67,7 @@ def query(_doc_cls=None, **kwargs): cleaned_fields = [] for field in fields: append_field = True - if isinstance(field, basestring): + if isinstance(field, six.string_types): parts.append(field) append_field = False # is last and CachedReferenceField @@ -84,9 +85,9 @@ def query(_doc_cls=None, **kwargs): singular_ops = [None, 'ne', 'gt', 'gte', 'lt', 'lte', 'not'] singular_ops += STRING_OPERATORS if op in singular_ops: - if isinstance(field, basestring): + if isinstance(field, six.string_types): if (op in STRING_OPERATORS and - isinstance(value, basestring)): + isinstance(value, six.string_types)): StringField = _import_class('StringField') value = StringField.prepare_query_value(op, value) else: @@ -231,7 +232,7 @@ def update(_doc_cls=None, **update): appended_sub_field = False for field in fields: append_field = True - if isinstance(field, basestring): + if isinstance(field, six.string_types): # Convert the S operator to $ if field == 'S': field = '$' diff --git a/setup.py b/setup.py index 816a6de9..7870d688 100644 --- a/setup.py +++ b/setup.py @@ -21,8 +21,9 @@ except Exception: def get_version(version_tuple): - if not isinstance(version_tuple[-1], int): - return '.'.join(map(str, version_tuple[:-1])) + version_tuple[-1] + """Return the version tuple as a string, e.g. for (0, 10, 7), + return '0.10.7'. + """ return '.'.join(map(str, version_tuple))