PYTHON-821 - Introduce results and options modules.

This commit is contained in:
Bernie Hackett 2015-02-11 13:39:45 -08:00
parent df4e5269b3
commit b9cd7b627b
10 changed files with 341 additions and 296 deletions

View File

@ -16,17 +16,7 @@
.. autodata:: pymongo.cursor.TAILABLE_AWAIT
.. autodata:: pymongo.cursor.EXHAUST
.. autoclass:: pymongo.collection.InsertOneResult
:members:
.. autoclass:: pymongo.collection.ReturnDocument
.. autoattribute:: Before
:annotation:
.. autoattribute:: After
:annotation:
.. autoclass:: pymongo.collection.Collection(database, name[, create=False[, **kwargs]]])
.. autoclass:: pymongo.collection.Collection(database, name[, create=False[, **kwargs]])
.. describe:: c[name] || c.name

View File

@ -40,8 +40,10 @@ Sub-modules:
message
mongo_client
mongo_replica_set_client
options
pool
read_preferences
results
son_manipulator
cursor_manager
uri_parser

View File

@ -0,0 +1,6 @@
:mod:`options` -- Option class definitions
==========================================
.. automodule:: pymongo.options
:synopsis: Option class definitions
:members:

View File

@ -0,0 +1,6 @@
:mod:`results` -- Result class definitions
==========================================
.. automodule:: pymongo.results
:synopsis: Result class definitions
:members:

View File

@ -42,243 +42,6 @@ _WRITE_CONCERN_ERROR = 64
_COMMANDS = ('insert', 'update', 'delete')
class _WriteOp(object):
"""Private base class for all write operations."""
__slots__ = ("_filter", "_doc", "_upsert")
def __init__(self, filter=None, doc=None, upsert=None):
if filter is not None and not isinstance(filter, collections.Mapping):
raise TypeError("filter must be a mapping type.")
if upsert is not None and not isinstance(upsert, bool):
raise TypeError("upsert must be True or False")
self._filter = filter
self._doc = doc
self._upsert = upsert
class InsertOne(_WriteOp):
"""Represents an insert_one operation."""
def __init__(self, document):
"""Create an InsertOne instance.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `document`: The document to insert. If the document is missing an
_id field one will be added.
"""
super(InsertOne, self).__init__(doc=document)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_insert(self._doc)
def __repr__(self):
return "InsertOne(%r)" % (self._doc,)
class DeleteOne(_WriteOp):
"""Represents a delete_one operation."""
def __init__(self, filter):
"""Create a DeleteOne instance.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `filter`: A query that matches the document to delete.
"""
super(DeleteOne, self).__init__(filter)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_delete(self._filter, 1)
def __repr__(self):
return "DeleteOne(%r)" % (self._filter,)
class DeleteMany(_WriteOp):
"""Represents a delete_many operation."""
def __init__(self, filter):
"""Create a DeleteMany instance.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `filter`: A query that matches the documents to delete.
"""
super(DeleteMany, self).__init__(filter)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_delete(self._filter, 0)
def __repr__(self):
return "DeleteMany(%r)" % (self._filter,)
class ReplaceOne(_WriteOp):
"""Represents a replace_one operation."""
def __init__(self, filter, replacement, upsert=False):
"""Create a ReplaceOne instance.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `filter`: A query that matches the document to replace.
- `replacement`: The new document.
- `upsert` (optional): If ``True``, perform an insert if no documents
match the filter.
"""
super(ReplaceOne, self).__init__(filter, replacement, upsert)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_replace(self._filter, self._doc, self._upsert)
def __repr__(self):
return "ReplaceOne(%r, %r, %r)" % (self._filter,
self._doc,
self._upsert)
class UpdateOne(_WriteOp):
"""Represents an update_one operation."""
def __init__(self, filter, update, upsert=False):
"""Represents an update_one operation.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `filter`: A query that matches the document to update.
- `update`: The modifications to apply.
- `upsert` (optional): If ``True``, perform an insert if no documents
match the filter.
"""
super(UpdateOne, self).__init__(filter, update, upsert)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_update(self._filter, self._doc, False, self._upsert)
def __repr__(self):
return "UpdateOne(%r, %r, %r)" % (self._filter,
self._doc,
self._upsert)
class UpdateMany(_WriteOp):
"""Represents an update_many operation."""
def __init__(self, filter, update, upsert=False):
"""Create an UpdateMany instance.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `filter`: A query that matches the documents to update.
- `update`: The modifications to apply.
- `upsert` (optional): If ``True``, perform an insert if no documents
match the filter.
"""
super(UpdateMany, self).__init__(filter, update, upsert)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_update(self._filter, self._doc, True, self._upsert)
def __repr__(self):
return "UpdateMany(%r, %r, %r)" % (self._filter,
self._doc,
self._upsert)
class BulkWriteResult(object):
"""An object wrapper for bulk API write results."""
__slots__ = ("__bulk_api_result", "__acknowledged")
def __init__(self, bulk_api_result, acknowledged):
"""Create a BulkWriteResult instance.
:Parameters:
- `bulk_api_result`: A result dict from the bulk API
- `acknowledged`: Was this write result acknowledged? If ``False``
then all properties of this object will raise
:exc:`~pymongo.errors.InvalidOperation`.
"""
self.__bulk_api_result = bulk_api_result
self.__acknowledged = acknowledged
def __raise_if_unacknowledged(self, property_name):
"""Raise an exception on property access if unacknowledged."""
if not self.__acknowledged:
raise InvalidOperation("A value for %s is not available when "
"the write is unacknowledged. Check the "
"acknowledged attribute to avoid this "
"error." % (property_name,))
@property
def bulk_api_result(self):
"""The raw bulk API result."""
return self.__bulk_api_result
@property
def acknowledged(self):
"""Is this the result of an acknowledged bulk write operation?"""
return self.__acknowledged
@property
def inserted_count(self):
"""The number of documents inserted."""
self.__raise_if_unacknowledged("inserted_count")
return self.__bulk_api_result.get("nInserted")
@property
def matched_count(self):
"""The number of documents matched for an update."""
self.__raise_if_unacknowledged("matched_count")
return self.__bulk_api_result.get("nMatched")
@property
def modified_count(self):
"""The number of documents modified.
.. note:: modified_count is only reported by MongoDB 2.6 and later.
When connected to an earlier server version, or in certain mixed
version sharding configurations, this attribute will be set to
``None``.
"""
self.__raise_if_unacknowledged("modified_count")
return self.__bulk_api_result.get("nModified")
@property
def deleted_count(self):
"""The number of documents deleted."""
self.__raise_if_unacknowledged("deleted_count")
return self.__bulk_api_result.get("nRemoved")
@property
def upserted_count(self):
"""The number of documents upserted."""
self.__raise_if_unacknowledged("upserted_count")
return self.__bulk_api_result.get("nUpserted")
@property
def upserted_ids(self):
"""A map of operation index to the _id of the upserted document."""
self.__raise_if_unacknowledged("upserted_ids")
if self.__bulk_api_result:
return dict((upsert["index"], upsert["_id"])
for upsert in self.bulk_api_result["upserted"])
class _Run(object):
"""Represents a batch of write operations.
"""

View File

@ -29,16 +29,15 @@ from bson.son import SON
from pymongo import (common,
helpers,
message)
from pymongo.bulk import (BulkOperationBuilder,
BulkWriteResult,
_Bulk,
_WriteOp)
from pymongo.bulk import (BulkOperationBuilder, _Bulk)
from pymongo.command_cursor import CommandCursor
from pymongo.cursor import Cursor
from pymongo.errors import ConfigurationError, InvalidName, OperationFailure
from pymongo.helpers import _check_write_command_response, _command
from pymongo.message import _INSERT, _UPDATE, _DELETE
from pymongo.options import ReturnDocument, _WriteOp
from pymongo.read_preferences import ReadPreference
from pymongo.results import InsertOneResult, BulkWriteResult
from pymongo.write_concern import WriteConcern
@ -51,38 +50,6 @@ except ImportError:
_NO_OBJ_ERROR = "No matching object found"
class InsertOneResult(object):
"""The return type for :meth:`Collection.insert_one`."""
__slots__ = ("__inserted_id", "__acknowledged")
def __init__(self, inserted_id, acknowledged):
self.__inserted_id = inserted_id
self.__acknowledged = acknowledged
@property
def inserted_id(self):
"""The inserted document's _id."""
return self.__inserted_id
@property
def acknowledged(self):
"""Is this the result of an acknowledged write operation?"""
return self.__acknowledged
class ReturnDocument(object):
"""An enum used with :meth:`Collection.find_one_and_replace` and
:meth:`Collection.find_one_and_update`.
"""
Before = False
"""Return the original document before it was updated/replaced, or
``None`` if no document matches the query.
"""
After = True
"""Return the updated/replaced or inserted document."""
def _gen_index_name(keys):
"""Generate an index name from the set of fields it is over.
"""
@ -321,11 +288,11 @@ class Collection(common.BaseObject):
the :meth:`initialize_ordered_bulk_op` and
:meth:`initialize_unordered_bulk_op` methods. Write operations are
passed as a list using the write operation classes from the
:mod:`~pymongo.bulk` module::
:mod:`~pymongo.options` module::
>>> # DeleteOne, UpdateOne, and UpdateMany are also available.
...
>>> from pymongo.bulk import InsertOne, DeleteMany, ReplaceOne
>>> from pymongo.options import InsertOne, DeleteMany, ReplaceOne
>>> requests = [InsertOne({'foo': 1}), DeleteMany({'bar': 2}),
... ReplaceOne({'bar': 1}, {'bim': 2}, upsert=True)]
>>> coll.bulk_write(requests)
@ -339,7 +306,7 @@ class Collection(common.BaseObject):
parallel, and all operations will be attempted.
:Returns:
An instance of :class:`~pymongo.bulk.BulkWriteResult`.
An instance of :class:`~pymongo.results.BulkWriteResult`.
.. versionadded:: 3.0
"""
@ -519,7 +486,7 @@ class Collection(common.BaseObject):
added automatically.
:Returns:
- An instance of :class:`InsertOneResult`.
- An instance of :class:`~pymongo.results.InsertOneResult`.
"""
if not isinstance(document, collections.MutableMapping):
raise TypeError("document must be a mutable mapping type")
@ -1909,10 +1876,12 @@ class Collection(common.BaseObject):
match the query, they are sorted and the first is replaced.
- `upsert` (optional): When ``True``, inserts a new document if no
document matches the query. Defaults to ``False``.
- `return_document`: If :attr:`ReturnDocument.Before` (the default),
- `return_document`: If
:attr:`~pymongo.options.ReturnDocument.Before` (the default),
returns the original document before it was replaced, or ``None``
if no document matches. If :attr:`ReturnDocument.After`, returns
the replaced or inserted document.
if no document matches. If
:attr:`~pymongo.options.ReturnDocument.After`, returns the replaced
or inserted document.
- `**kwargs` (optional): additional command arguments can be passed
as keyword arguments (for example maxTimeMS can be used with
recent server versions).
@ -1947,10 +1916,12 @@ class Collection(common.BaseObject):
match the query, they are sorted and the first is updated.
- `upsert` (optional): When ``True``, inserts a new document if no
document matches the query. Defaults to ``False``.
- `return_document`: If :attr:`ReturnDocument.Before` (the default),
- `return_document`: If
:attr:`~pymongo.options.ReturnDocument.Before` (the default),
returns the original document before it was updated, or ``None``
if no document matches. If :attr:`ReturnDocument.After`, returns
the updated or inserted document.
if no document matches. If
:attr:`~pymongo.options.ReturnDocument.After`, returns the updated
or inserted document.
- `**kwargs` (optional): additional command arguments can be passed
as keyword arguments (for example maxTimeMS can be used with
recent server versions).

188
pymongo/options.py Normal file
View File

@ -0,0 +1,188 @@
# Copyright 2015 MongoDB, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Option class definitions."""
import collections
class ReturnDocument(object):
"""An enum used with
:meth:`~pymongo.collection.Collection.find_one_and_replace` and
:meth:`~pymongo.collection.Collection.find_one_and_update`.
"""
Before = False
"""Return the original document before it was updated/replaced, or
``None`` if no document matches the query.
"""
After = True
"""Return the updated/replaced or inserted document."""
class _WriteOp(object):
"""Private base class for all write operations."""
__slots__ = ("_filter", "_doc", "_upsert")
def __init__(self, filter=None, doc=None, upsert=None):
if filter is not None and not isinstance(filter, collections.Mapping):
raise TypeError("filter must be a mapping type.")
if upsert is not None and not isinstance(upsert, bool):
raise TypeError("upsert must be True or False")
self._filter = filter
self._doc = doc
self._upsert = upsert
class InsertOne(_WriteOp):
"""Represents an insert_one operation."""
def __init__(self, document):
"""Create an InsertOne instance.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `document`: The document to insert. If the document is missing an
_id field one will be added.
"""
super(InsertOne, self).__init__(doc=document)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_insert(self._doc)
def __repr__(self):
return "InsertOne(%r)" % (self._doc,)
class DeleteOne(_WriteOp):
"""Represents a delete_one operation."""
def __init__(self, filter):
"""Create a DeleteOne instance.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `filter`: A query that matches the document to delete.
"""
super(DeleteOne, self).__init__(filter)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_delete(self._filter, 1)
def __repr__(self):
return "DeleteOne(%r)" % (self._filter,)
class DeleteMany(_WriteOp):
"""Represents a delete_many operation."""
def __init__(self, filter):
"""Create a DeleteMany instance.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `filter`: A query that matches the documents to delete.
"""
super(DeleteMany, self).__init__(filter)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_delete(self._filter, 0)
def __repr__(self):
return "DeleteMany(%r)" % (self._filter,)
class ReplaceOne(_WriteOp):
"""Represents a replace_one operation."""
def __init__(self, filter, replacement, upsert=False):
"""Create a ReplaceOne instance.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `filter`: A query that matches the document to replace.
- `replacement`: The new document.
- `upsert` (optional): If ``True``, perform an insert if no documents
match the filter.
"""
super(ReplaceOne, self).__init__(filter, replacement, upsert)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_replace(self._filter, self._doc, self._upsert)
def __repr__(self):
return "ReplaceOne(%r, %r, %r)" % (self._filter,
self._doc,
self._upsert)
class UpdateOne(_WriteOp):
"""Represents an update_one operation."""
def __init__(self, filter, update, upsert=False):
"""Represents an update_one operation.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `filter`: A query that matches the document to update.
- `update`: The modifications to apply.
- `upsert` (optional): If ``True``, perform an insert if no documents
match the filter.
"""
super(UpdateOne, self).__init__(filter, update, upsert)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_update(self._filter, self._doc, False, self._upsert)
def __repr__(self):
return "UpdateOne(%r, %r, %r)" % (self._filter,
self._doc,
self._upsert)
class UpdateMany(_WriteOp):
"""Represents an update_many operation."""
def __init__(self, filter, update, upsert=False):
"""Create an UpdateMany instance.
For use with :meth:`~pymongo.collection.Collection.bulk_write`.
:Parameters:
- `filter`: A query that matches the documents to update.
- `update`: The modifications to apply.
- `upsert` (optional): If ``True``, perform an insert if no documents
match the filter.
"""
super(UpdateMany, self).__init__(filter, update, upsert)
def _add_to_bulk(self, bulkobj):
"""Add this operation to the _Bulk instance `bulkobj`."""
bulkobj.add_update(self._filter, self._doc, True, self._upsert)
def __repr__(self):
return "UpdateMany(%r, %r, %r)" % (self._filter,
self._doc,
self._upsert)

117
pymongo/results.py Normal file
View File

@ -0,0 +1,117 @@
# Copyright 2015 MongoDB, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Result class definitions."""
from pymongo.errors import InvalidOperation
class InsertOneResult(object):
"""The return type for :meth:`Collection.insert_one`."""
__slots__ = ("__inserted_id", "__acknowledged")
def __init__(self, inserted_id, acknowledged):
self.__inserted_id = inserted_id
self.__acknowledged = acknowledged
@property
def inserted_id(self):
"""The inserted document's _id."""
return self.__inserted_id
@property
def acknowledged(self):
"""Is this the result of an acknowledged write operation?"""
return self.__acknowledged
class BulkWriteResult(object):
"""An object wrapper for bulk API write results."""
__slots__ = ("__bulk_api_result", "__acknowledged")
def __init__(self, bulk_api_result, acknowledged):
"""Create a BulkWriteResult instance.
:Parameters:
- `bulk_api_result`: A result dict from the bulk API
- `acknowledged`: Was this write result acknowledged? If ``False``
then all properties of this object will raise
:exc:`~pymongo.errors.InvalidOperation`.
"""
self.__bulk_api_result = bulk_api_result
self.__acknowledged = acknowledged
def __raise_if_unacknowledged(self, property_name):
"""Raise an exception on property access if unacknowledged."""
if not self.__acknowledged:
raise InvalidOperation("A value for %s is not available when "
"the write is unacknowledged. Check the "
"acknowledged attribute to avoid this "
"error." % (property_name,))
@property
def bulk_api_result(self):
"""The raw bulk API result."""
return self.__bulk_api_result
@property
def acknowledged(self):
"""Is this the result of an acknowledged bulk write operation?"""
return self.__acknowledged
@property
def inserted_count(self):
"""The number of documents inserted."""
self.__raise_if_unacknowledged("inserted_count")
return self.__bulk_api_result.get("nInserted")
@property
def matched_count(self):
"""The number of documents matched for an update."""
self.__raise_if_unacknowledged("matched_count")
return self.__bulk_api_result.get("nMatched")
@property
def modified_count(self):
"""The number of documents modified.
.. note:: modified_count is only reported by MongoDB 2.6 and later.
When connected to an earlier server version, or in certain mixed
version sharding configurations, this attribute will be set to
``None``.
"""
self.__raise_if_unacknowledged("modified_count")
return self.__bulk_api_result.get("nModified")
@property
def deleted_count(self):
"""The number of documents deleted."""
self.__raise_if_unacknowledged("deleted_count")
return self.__bulk_api_result.get("nRemoved")
@property
def upserted_count(self):
"""The number of documents upserted."""
self.__raise_if_unacknowledged("upserted_count")
return self.__bulk_api_result.get("nUpserted")
@property
def upserted_ids(self):
"""A map of operation index to the _id of the upserted document."""
self.__raise_if_unacknowledged("upserted_ids")
if self.__bulk_api_result:
return dict((upsert["index"], upsert["_id"])
for upsert in self.bulk_api_result["upserted"])

View File

@ -22,7 +22,7 @@ from bson import InvalidDocument, SON
from bson.objectid import ObjectId
from bson.py3compat import string_type
from pymongo import MongoClient
from pymongo.bulk import *
from pymongo.options import *
from pymongo.common import partition_node
from pymongo.errors import BulkWriteError, InvalidOperation, OperationFailure
from pymongo.write_concern import WriteConcern

View File

@ -35,7 +35,7 @@ from bson.son import SON
from pymongo import (ASCENDING, DESCENDING, GEO2D,
GEOHAYSTACK, GEOSPHERE, HASHED, TEXT)
from pymongo import MongoClient
from pymongo.collection import Collection, ReturnDocument, InsertOneResult
from pymongo.collection import Collection
from pymongo.command_cursor import CommandCursor
from pymongo.cursor import EXHAUST
from pymongo.errors import (ConfigurationError,
@ -46,7 +46,9 @@ from pymongo.errors import (ConfigurationError,
InvalidOperation,
OperationFailure,
WTimeoutError)
from pymongo.options import ReturnDocument
from pymongo.read_preferences import ReadPreference
from pymongo.results import InsertOneResult
from pymongo.son_manipulator import SONManipulator
from pymongo.write_concern import WriteConcern
from test.test_client import IntegrationTest