PYTHON-1307 Remove SONManipulator APIs (#557)

This commit is contained in:
Shane Harvey 2021-01-25 16:22:00 -08:00 committed by GitHub
parent cf877e95c7
commit 7c1060cfec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 72 additions and 524 deletions

View File

@ -47,8 +47,8 @@
.. automethod:: aggregate
.. automethod:: aggregate_raw_batches
.. automethod:: watch
.. automethod:: find(filter=None, projection=None, skip=0, limit=0, no_cursor_timeout=False, cursor_type=CursorType.NON_TAILABLE, sort=None, allow_partial_results=False, oplog_replay=False, modifiers=None, batch_size=0, manipulate=True, collation=None, hint=None, max_scan=None, max_time_ms=None, max=None, min=None, return_key=False, show_record_id=False, snapshot=False, comment=None, session=None)
.. automethod:: find_raw_batches(filter=None, projection=None, skip=0, limit=0, no_cursor_timeout=False, cursor_type=CursorType.NON_TAILABLE, sort=None, allow_partial_results=False, oplog_replay=False, modifiers=None, batch_size=0, manipulate=True, collation=None, hint=None, max_scan=None, max_time_ms=None, max=None, min=None, return_key=False, show_record_id=False, snapshot=False, comment=None)
.. automethod:: find(filter=None, projection=None, skip=0, limit=0, no_cursor_timeout=False, cursor_type=CursorType.NON_TAILABLE, sort=None, allow_partial_results=False, oplog_replay=False, modifiers=None, batch_size=0, collation=None, hint=None, max_scan=None, max_time_ms=None, max=None, min=None, return_key=False, show_record_id=False, snapshot=False, comment=None, session=None, allow_disk_use=None)
.. automethod:: find_raw_batches(filter=None, projection=None, skip=0, limit=0, no_cursor_timeout=False, cursor_type=CursorType.NON_TAILABLE, sort=None, allow_partial_results=False, oplog_replay=False, modifiers=None, batch_size=0, collation=None, hint=None, max_scan=None, max_time_ms=None, max=None, min=None, return_key=False, show_record_id=False, snapshot=False, comment=None, allow_disk_use=None)
.. automethod:: find_one(filter=None, *args, **kwargs)
.. automethod:: find_one_and_delete
.. automethod:: find_one_and_replace(filter, replacement, projection=None, sort=None, return_document=ReturnDocument.BEFORE, hint=None, session=None, **kwargs)

View File

@ -15,7 +15,7 @@
.. autoattribute:: EXHAUST
:annotation:
.. autoclass:: pymongo.cursor.Cursor(collection, filter=None, projection=None, skip=0, limit=0, no_cursor_timeout=False, cursor_type=CursorType.NON_TAILABLE, sort=None, allow_partial_results=False, oplog_replay=False, modifiers=None, batch_size=0, manipulate=True, collation=None, hint=None, max_scan=None, max_time_ms=None, max=None, min=None, return_key=False, show_record_id=False, snapshot=False, comment=None)
.. autoclass:: pymongo.cursor.Cursor(collection, filter=None, projection=None, skip=0, limit=0, no_cursor_timeout=False, cursor_type=CursorType.NON_TAILABLE, sort=None, allow_partial_results=False, oplog_replay=False, modifiers=None, batch_size=0, collation=None, hint=None, max_scan=None, max_time_ms=None, max=None, min=None, return_key=False, show_record_id=False, snapshot=False, comment=None, session=None, allow_disk_use=None)
:members:
.. describe:: c[index]
@ -24,4 +24,4 @@
.. automethod:: __getitem__
.. autoclass:: pymongo.cursor.RawBatchCursor(collection, filter=None, projection=None, skip=0, limit=0, no_cursor_timeout=False, cursor_type=CursorType.NON_TAILABLE, sort=None, allow_partial_results=False, oplog_replay=False, modifiers=None, batch_size=0, collation=None, hint=None, max_scan=None, max_time_ms=None, max=None, min=None, return_key=False, show_record_id=False, snapshot=False, comment=None)
.. autoclass:: pymongo.cursor.RawBatchCursor(collection, filter=None, projection=None, skip=0, limit=0, no_cursor_timeout=False, cursor_type=CursorType.NON_TAILABLE, sort=None, allow_partial_results=False, oplog_replay=False, modifiers=None, batch_size=0, collation=None, hint=None, max_scan=None, max_time_ms=None, max=None, min=None, return_key=False, show_record_id=False, snapshot=False, comment=None, allow_disk_use=None)

View File

@ -47,7 +47,6 @@ Sub-modules:
read_concern
read_preferences
results
son_manipulator
server_api
uri_parser
write_concern

View File

@ -1,6 +0,0 @@
:mod:`son_manipulator` -- Manipulators that can edit SON documents as they are saved or retrieved
=================================================================================================
.. automodule:: pymongo.son_manipulator
:synopsis: Manipulators that can edit SON documents as they are saved or retrieved
:members:

View File

@ -30,11 +30,11 @@ Breaking Changes in 4.0
- Removed :meth:`pymongo.collection.Collection.parallel_scan`.
- Removed :meth:`pymongo.collection.Collection.ensure_index`.
- Removed :meth:`pymongo.collection.Collection.reindex`.
- Removed :meth:`pymongo.collection.Collection.save`
- Removed :meth:`pymongo.collection.Collection.insert`
- Removed :meth:`pymongo.collection.Collection.update`
- Removed :meth:`pymongo.collection.Collection.remove`
- Removed :meth:`pymongo.collection.Collection.find_and_modify`
- Removed :meth:`pymongo.collection.Collection.save`.
- Removed :meth:`pymongo.collection.Collection.insert`.
- Removed :meth:`pymongo.collection.Collection.update`.
- Removed :meth:`pymongo.collection.Collection.remove`.
- Removed :meth:`pymongo.collection.Collection.find_and_modify`.
- Removed :meth:`pymongo.mongo_client.MongoClient.close_cursor`. Use
:meth:`pymongo.cursor.Cursor.close` instead.
- Removed :meth:`pymongo.mongo_client.MongoClient.kill_cursors`.
@ -43,6 +43,21 @@ Breaking Changes in 4.0
- Removed :meth:`pymongo.mongo_client.MongoClient.set_cursor_manager`.
- Removed :mod:`pymongo.thread_util`.
- Removed :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`.
- Removed :mod:`pymongo.son_manipulator`,
:class:`pymongo.son_manipulator.SONManipulator`,
:class:`pymongo.son_manipulator.ObjectIdInjector`,
:class:`pymongo.son_manipulator.ObjectIdShuffler`,
:class:`pymongo.son_manipulator.AutoReference`,
:class:`pymongo.son_manipulator.NamespaceInjector`,
:meth:`pymongo.database.Database.add_son_manipulator`,
:attr:`pymongo.database.Database.outgoing_copying_manipulators`,
:attr:`pymongo.database.Database.outgoing_manipulators`,
:attr:`pymongo.database.Database.incoming_copying_manipulators`, and
:attr:`pymongo.database.Database.incoming_manipulators`.
- Removed the ``manipulate`` parameter from
:meth:`~pymongo.collection.Collection.find`,
:meth:`~pymongo.collection.Collection.find_one`, and
:meth:`~pymongo.cursor.Cursor`.
Notable improvements
....................

View File

@ -274,6 +274,38 @@ can be changed to this::
.. _reIndex command: https://docs.mongodb.com/manual/reference/command/reIndex/
SONManipulator is removed
-------------------------
Removed :mod:`pymongo.son_manipulator`,
:class:`pymongo.son_manipulator.SONManipulator`,
:class:`pymongo.son_manipulator.ObjectIdInjector`,
:class:`pymongo.son_manipulator.ObjectIdShuffler`,
:class:`pymongo.son_manipulator.AutoReference`,
:class:`pymongo.son_manipulator.NamespaceInjector`,
:meth:`pymongo.database.Database.add_son_manipulator`,
:attr:`pymongo.database.Database.outgoing_copying_manipulators`,
:attr:`pymongo.database.Database.outgoing_manipulators`,
:attr:`pymongo.database.Database.incoming_copying_manipulators`, and
:attr:`pymongo.database.Database.incoming_manipulators`.
Removed the ``manipulate`` parameter from
:meth:`~pymongo.collection.Collection.find`,
:meth:`~pymongo.collection.Collection.find_one`, and
:meth:`~pymongo.cursor.Cursor`.
The :class:`pymongo.son_manipulator.SONManipulator` API has limitations as a
technique for transforming your data and was deprecated in PyMongo 3.0.
Instead, it is more flexible and straightforward to transform outgoing
documents in your own code before passing them to PyMongo, and transform
incoming documents after receiving them from PyMongo.
Alternatively, if your application uses the ``SONManipulator`` API to convert
custom types to BSON, the :class:`~bson.codec_options.TypeCodec` and
:class:`~bson.codec_options.TypeRegistry` APIs may be a suitable alternative.
For more information, see the
:doc:`custom type example <examples/custom_type>`.
Removed features with no migration path
---------------------------------------

View File

@ -478,9 +478,9 @@ class _Bulk(object):
sock_info,
operation['q'],
doc,
operation['upsert'],
check_keys,
operation['multi'],
upsert=operation['upsert'],
check_keys=check_keys,
multi=operation['multi'],
write_concern=write_concern,
op_id=op_id,
ordered=self.ordered,

View File

@ -554,15 +554,9 @@ class Collection(common.BaseObject):
def _insert_one(
self, doc, ordered,
check_keys, manipulate, write_concern, op_id, bypass_doc_val,
check_keys, write_concern, op_id, bypass_doc_val,
session):
"""Internal helper for inserting a single document."""
if manipulate:
doc = self.__database._apply_incoming_manipulators(doc, self)
if not isinstance(doc, RawBSONDocument) and '_id' not in doc:
doc['_id'] = ObjectId()
doc = self.__database._apply_incoming_copying_manipulators(doc,
self)
write_concern = write_concern or self.write_concern
acknowledged = write_concern.acknowledged
command = SON([('insert', self.name),
@ -646,7 +640,7 @@ class Collection(common.BaseObject):
write_concern = self._write_concern_for(session)
return InsertOneResult(
self._insert_one(
document, ordered=True, check_keys=True, manipulate=False,
document, ordered=True, check_keys=True,
write_concern=write_concern, op_id=None,
bypass_doc_val=bypass_document_validation, session=session),
write_concern.acknowledged)
@ -712,14 +706,12 @@ class Collection(common.BaseObject):
return InsertManyResult(inserted_ids, write_concern.acknowledged)
def _update(self, sock_info, criteria, document, upsert=False,
check_keys=True, multi=False, manipulate=False,
check_keys=True, multi=False,
write_concern=None, op_id=None, ordered=True,
bypass_doc_val=False, collation=None, array_filters=None,
hint=None, session=None, retryable_write=False):
"""Internal update / replace helper."""
common.validate_boolean("upsert", upsert)
if manipulate:
document = self.__database._fix_incoming(document, self)
collation = validate_collation_or_none(collation)
write_concern = write_concern or self.write_concern
acknowledged = write_concern.acknowledged
@ -801,7 +793,7 @@ class Collection(common.BaseObject):
def _update_retryable(
self, criteria, document, upsert=False,
check_keys=True, multi=False, manipulate=False,
check_keys=True, multi=False,
write_concern=None, op_id=None, ordered=True,
bypass_doc_val=False, collation=None, array_filters=None,
hint=None, session=None):
@ -809,7 +801,7 @@ class Collection(common.BaseObject):
def _update(session, sock_info, retryable_write):
return self._update(
sock_info, criteria, document, upsert=upsert,
check_keys=check_keys, multi=multi, manipulate=manipulate,
check_keys=check_keys, multi=multi,
write_concern=write_concern, op_id=op_id, ordered=ordered,
bypass_doc_val=bypass_doc_val, collation=collation,
array_filters=array_filters, hint=hint, session=session,
@ -1346,8 +1338,6 @@ class Collection(common.BaseObject):
oplogReplay query flag. Default: False.
- `batch_size` (optional): Limits the number of documents returned in
a single batch.
- `manipulate` (optional): **DEPRECATED** - If True, apply any
outgoing SON manipulators before returning. Default: True.
- `collation` (optional): An instance of
:class:`~pymongo.collation.Collation`. This option is only supported
on MongoDB 3.4 and above.

View File

@ -268,8 +268,7 @@ class CommandCursor(object):
if not len(self.__data) and not self.__killed and get_more_allowed:
self._refresh()
if len(self.__data):
coll = self.__collection
return coll.database._fix_outgoing(self.__data.popleft(), coll)
return self.__data.popleft()
else:
return None

View File

@ -108,7 +108,7 @@ class Cursor(object):
limit=0, no_cursor_timeout=False,
cursor_type=CursorType.NON_TAILABLE,
sort=None, allow_partial_results=False, oplog_replay=False,
modifiers=None, batch_size=0, manipulate=True,
modifiers=None, batch_size=0,
collation=None, hint=None, max_scan=None, max_time_ms=None,
max=None, min=None, return_key=False, show_record_id=False,
snapshot=False, comment=None, session=None,
@ -181,7 +181,6 @@ class Cursor(object):
self.__max_await_time_ms = None
self.__max = max
self.__min = min
self.__manipulate = manipulate
self.__collation = validate_collation_or_none(collation)
self.__return_key = return_key
self.__show_record_id = show_record_id
@ -281,7 +280,7 @@ class Cursor(object):
values_to_clone = ("spec", "projection", "skip", "limit",
"max_time_ms", "max_await_time_ms", "comment",
"max", "min", "ordering", "explain", "hint",
"batch_size", "max_scan", "manipulate",
"batch_size", "max_scan",
"query_flags", "modifiers", "collation", "empty",
"show_record_id", "return_key", "allow_disk_use",
"snapshot", "exhaust")
@ -1198,12 +1197,7 @@ class Cursor(object):
if self.__empty:
raise StopIteration
if len(self.__data) or self._refresh():
if self.__manipulate:
_db = self.__collection.database
return _db._fix_outgoing(self.__data.popleft(),
self.__collection)
else:
return self.__data.popleft()
return self.__data.popleft()
else:
raise StopIteration
@ -1277,15 +1271,8 @@ class RawBatchCursor(Cursor):
.. mongodoc:: cursors
"""
manipulate = kwargs.get('manipulate')
kwargs['manipulate'] = False
super(RawBatchCursor, self).__init__(*args, **kwargs)
# Throw only after cursor's initialized, to prevent errors in __del__.
if manipulate:
raise InvalidOperation(
"Cannot use RawBatchCursor with manipulate=True")
def _unpack_response(self, response, cursor_id, codec_options,
user_fields=None, legacy_response=False):
return response.raw_response(cursor_id)

View File

@ -30,7 +30,6 @@ from pymongo.errors import (CollectionInvalid,
OperationFailure)
from pymongo.message import _first_batch
from pymongo.read_preferences import ReadPreference
from pymongo.son_manipulator import SONManipulator
_INDEX_REGEX = {"name": {"$regex": r"^(?!.*\$)"}}
@ -108,38 +107,6 @@ class Database(common.BaseObject):
self.__name = name
self.__client = client
self.__incoming_manipulators = []
self.__incoming_copying_manipulators = []
self.__outgoing_manipulators = []
self.__outgoing_copying_manipulators = []
def add_son_manipulator(self, manipulator):
"""Add a new son manipulator to this database.
**DEPRECATED** - `add_son_manipulator` is deprecated.
.. versionchanged:: 3.0
Deprecated add_son_manipulator.
"""
warnings.warn("add_son_manipulator is deprecated",
DeprecationWarning, stacklevel=2)
base = SONManipulator()
def method_overwritten(instance, method):
"""Test if this method has been overridden."""
return (getattr(
instance, method).__func__ != getattr(base, method).__func__)
if manipulator.will_copy():
if method_overwritten(manipulator, "transform_incoming"):
self.__incoming_copying_manipulators.insert(0, manipulator)
if method_overwritten(manipulator, "transform_outgoing"):
self.__outgoing_copying_manipulators.insert(0, manipulator)
else:
if method_overwritten(manipulator, "transform_incoming"):
self.__incoming_manipulators.insert(0, manipulator)
if method_overwritten(manipulator, "transform_outgoing"):
self.__outgoing_manipulators.insert(0, manipulator)
@property
def client(self):
"""The client instance for this :class:`Database`."""
@ -150,66 +117,6 @@ class Database(common.BaseObject):
"""The name of this :class:`Database`."""
return self.__name
@property
def incoming_manipulators(self):
"""**DEPRECATED**: All incoming SON manipulators.
.. versionchanged:: 3.5
Deprecated.
.. versionadded:: 2.0
"""
warnings.warn("Database.incoming_manipulators() is deprecated",
DeprecationWarning, stacklevel=2)
return [manipulator.__class__.__name__
for manipulator in self.__incoming_manipulators]
@property
def incoming_copying_manipulators(self):
"""**DEPRECATED**: All incoming SON copying manipulators.
.. versionchanged:: 3.5
Deprecated.
.. versionadded:: 2.0
"""
warnings.warn("Database.incoming_copying_manipulators() is deprecated",
DeprecationWarning, stacklevel=2)
return [manipulator.__class__.__name__
for manipulator in self.__incoming_copying_manipulators]
@property
def outgoing_manipulators(self):
"""**DEPRECATED**: All outgoing SON manipulators.
.. versionchanged:: 3.5
Deprecated.
.. versionadded:: 2.0
"""
warnings.warn("Database.outgoing_manipulators() is deprecated",
DeprecationWarning, stacklevel=2)
return [manipulator.__class__.__name__
for manipulator in self.__outgoing_manipulators]
@property
def outgoing_copying_manipulators(self):
"""**DEPRECATED**: All outgoing SON copying manipulators.
.. versionchanged:: 3.5
Deprecated.
.. versionadded:: 2.0
"""
warnings.warn("Database.outgoing_copying_manipulators() is deprecated",
DeprecationWarning, stacklevel=2)
return [manipulator.__class__.__name__
for manipulator in self.__outgoing_copying_manipulators]
def with_options(self, codec_options=None, read_preference=None,
write_concern=None, read_concern=None):
"""Get a clone of this database changing the specified settings.
@ -407,42 +314,6 @@ class Database(common.BaseObject):
read_preference, write_concern,
read_concern, session=s, **kwargs)
def _apply_incoming_manipulators(self, son, collection):
"""Apply incoming manipulators to `son`."""
for manipulator in self.__incoming_manipulators:
son = manipulator.transform_incoming(son, collection)
return son
def _apply_incoming_copying_manipulators(self, son, collection):
"""Apply incoming copying manipulators to `son`."""
for manipulator in self.__incoming_copying_manipulators:
son = manipulator.transform_incoming(son, collection)
return son
def _fix_incoming(self, son, collection):
"""Apply manipulators to an incoming SON object before it gets stored.
:Parameters:
- `son`: the son object going into the database
- `collection`: the collection the son object is being saved in
"""
son = self._apply_incoming_manipulators(son, collection)
son = self._apply_incoming_copying_manipulators(son, collection)
return son
def _fix_outgoing(self, son, collection):
"""Apply manipulators to a SON object as it comes out of the database.
:Parameters:
- `son`: the son object coming out of the database
- `collection`: the collection the son object was saved in
"""
for manipulator in reversed(self.__outgoing_manipulators):
son = manipulator.transform_outgoing(son, collection)
for manipulator in reversed(self.__outgoing_copying_manipulators):
son = manipulator.transform_outgoing(son, collection)
return son
def aggregate(self, pipeline, session=None, **kwargs):
"""Perform a database-level aggregation.

View File

@ -1,192 +0,0 @@
# Copyright 2009-present 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.
"""**DEPRECATED**: Manipulators that can edit SON objects as they enter and exit
a database.
The :class:`~pymongo.son_manipulator.SONManipulator` API has limitations as a
technique for transforming your data. Instead, it is more flexible and
straightforward to transform outgoing documents in your own code before passing
them to PyMongo, and transform incoming documents after receiving them from
PyMongo. SON Manipulators will be removed from PyMongo in 4.0.
PyMongo does **not** apply SON manipulators to documents passed to
the modern methods :meth:`~pymongo.collection.Collection.bulk_write`,
:meth:`~pymongo.collection.Collection.insert_one`,
:meth:`~pymongo.collection.Collection.insert_many`,
:meth:`~pymongo.collection.Collection.update_one`, or
:meth:`~pymongo.collection.Collection.update_many`. SON manipulators are
**not** applied to documents returned by the modern methods
:meth:`~pymongo.collection.Collection.find_one_and_delete`,
:meth:`~pymongo.collection.Collection.find_one_and_replace`, and
:meth:`~pymongo.collection.Collection.find_one_and_update`.
"""
from collections import abc
from bson.dbref import DBRef
from bson.objectid import ObjectId
from bson.son import SON
class SONManipulator(object):
"""A base son manipulator.
This manipulator just saves and restores objects without changing them.
"""
def will_copy(self):
"""Will this SON manipulator make a copy of the incoming document?
Derived classes that do need to make a copy should override this
method, returning True instead of False. All non-copying manipulators
will be applied first (so that the user's document will be updated
appropriately), followed by copying manipulators.
"""
return False
def transform_incoming(self, son, collection):
"""Manipulate an incoming SON object.
:Parameters:
- `son`: the SON object to be inserted into the database
- `collection`: the collection the object is being inserted into
"""
if self.will_copy():
return SON(son)
return son
def transform_outgoing(self, son, collection):
"""Manipulate an outgoing SON object.
:Parameters:
- `son`: the SON object being retrieved from the database
- `collection`: the collection this object was stored in
"""
if self.will_copy():
return SON(son)
return son
class ObjectIdInjector(SONManipulator):
"""A son manipulator that adds the _id field if it is missing.
.. versionchanged:: 2.7
ObjectIdInjector is no longer used by PyMongo, but remains in this
module for backwards compatibility.
"""
def transform_incoming(self, son, collection):
"""Add an _id field if it is missing.
"""
if not "_id" in son:
son["_id"] = ObjectId()
return son
# This is now handled during BSON encoding (for performance reasons),
# but I'm keeping this here as a reference for those implementing new
# SONManipulators.
class ObjectIdShuffler(SONManipulator):
"""A son manipulator that moves _id to the first position.
"""
def will_copy(self):
"""We need to copy to be sure that we are dealing with SON, not a dict.
"""
return True
def transform_incoming(self, son, collection):
"""Move _id to the front if it's there.
"""
if not "_id" in son:
return son
transformed = SON({"_id": son["_id"]})
transformed.update(son)
return transformed
class NamespaceInjector(SONManipulator):
"""A son manipulator that adds the _ns field.
"""
def transform_incoming(self, son, collection):
"""Add the _ns field to the incoming object
"""
son["_ns"] = collection.name
return son
class AutoReference(SONManipulator):
"""Transparently reference and de-reference already saved embedded objects.
This manipulator should probably only be used when the NamespaceInjector is
also being used, otherwise it doesn't make too much sense - documents can
only be auto-referenced if they have an *_ns* field.
NOTE: this will behave poorly if you have a circular reference.
TODO: this only works for documents that are in the same database. To fix
this we'll need to add a DatabaseInjector that adds *_db* and then make
use of the optional *database* support for DBRefs.
"""
def __init__(self, db):
self.database = db
def will_copy(self):
"""We need to copy so the user's document doesn't get transformed refs.
"""
return True
def transform_incoming(self, son, collection):
"""Replace embedded documents with DBRefs.
"""
def transform_value(value):
if isinstance(value, abc.MutableMapping):
if "_id" in value and "_ns" in value:
return DBRef(value["_ns"], transform_value(value["_id"]))
else:
return transform_dict(SON(value))
elif isinstance(value, list):
return [transform_value(v) for v in value]
return value
def transform_dict(object):
for (key, value) in object.items():
object[key] = transform_value(value)
return object
return transform_dict(SON(son))
def transform_outgoing(self, son, collection):
"""Replace DBRefs with embedded documents.
"""
def transform_value(value):
if isinstance(value, DBRef):
return self.database.dereference(value)
elif isinstance(value, list):
return [transform_value(v) for v in value]
elif isinstance(value, abc.MutableMapping):
return transform_dict(SON(value))
return value
def transform_dict(object):
for (key, value) in object.items():
object[key] = transform_value(value)
return object
return transform_dict(SON(son))

View File

@ -908,7 +908,7 @@ class TestCursor(IntegrationTest):
self.assertEqual(cursor, cursor.rewind())
# manipulate, oplog_reply, and snapshot are all deprecated.
# oplog_reply, and snapshot are all deprecated.
@ignore_deprecations
def test_clone(self):
self.db.test.insert_many([{"x": i} for i in range(1, 4)])
@ -956,7 +956,6 @@ class TestCursor(IntegrationTest):
allow_partial_results=True,
oplog_replay=True,
batch_size=123,
manipulate=False,
collation={'locale': 'en_US'},
hint=[("_id", 1)],
max_scan=100,
@ -1466,11 +1465,6 @@ class TestRawBatchCursor(IntegrationTest):
self.assertEqual(1, len(batches))
self.assertEqual(docs, decode_all(batches[0]))
def test_manipulate(self):
c = self.db.test
with self.assertRaises(InvalidOperation):
c.find_raw_batches(manipulate=True)
def test_explain(self):
c = self.db.test
c.insert_one({})

View File

@ -14,10 +14,7 @@
"""Test various legacy / deprecated API features."""
import itertools
import sys
import threading
import time
import uuid
sys.path[0:0] = [""]
@ -25,25 +22,16 @@ sys.path[0:0] = [""]
from bson.binary import PYTHON_LEGACY, STANDARD
from bson.code import Code
from bson.codec_options import CodecOptions
from bson.objectid import ObjectId
from bson.son import SON
from pymongo import ASCENDING, DESCENDING, GEOHAYSTACK
from pymongo import ASCENDING, GEOHAYSTACK
from pymongo.common import partition_node
from pymongo.errors import (BulkWriteError,
ConfigurationError,
DuplicateKeyError,
InvalidDocument,
InvalidOperation,
OperationFailure,
WriteConcernError,
WTimeoutError)
OperationFailure)
from pymongo.operations import IndexModel
from pymongo.son_manipulator import (AutoReference,
NamespaceInjector,
ObjectIdShuffler,
SONManipulator)
from pymongo.write_concern import WriteConcern
from test import client_context, qcheck, unittest, SkipTest
from test import client_context, unittest, SkipTest
from test.test_client import IntegrationTest
from test.test_bulk import BulkTestBase, BulkAuthorizationTestBase
from test.utils import (DeprecationFilter,
@ -64,11 +52,6 @@ class TestDeprecations(IntegrationTest):
def tearDownClass(cls):
cls.deprecation_filter.stop()
def test_add_son_manipulator_deprecation(self):
db = self.client.pymongo_test
self.assertRaises(DeprecationWarning,
lambda: db.add_son_manipulator(AutoReference(db)))
def test_geoHaystack_deprecation(self):
self.addCleanup(self.db.test.drop)
keys = [("pos", GEOHAYSTACK), ("type", ASCENDING)]

View File

@ -1,124 +0,0 @@
# Copyright 2009-present 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.
"""Tests for SONManipulators.
"""
import sys
import warnings
sys.path[0:0] = [""]
from bson.son import SON
from pymongo import MongoClient
from pymongo.son_manipulator import (NamespaceInjector,
ObjectIdInjector,
ObjectIdShuffler,
SONManipulator)
from test import client_context, qcheck, unittest
class TestSONManipulator(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.warn_context = warnings.catch_warnings()
cls.warn_context.__enter__()
warnings.simplefilter("ignore", DeprecationWarning)
client = MongoClient(
client_context.host, client_context.port, connect=False)
cls.db = client.pymongo_test
@classmethod
def tearDownClass(cls):
cls.warn_context.__exit__()
cls.warn_context = None
def test_basic(self):
manip = SONManipulator()
collection = self.db.test
def incoming_is_identity(son):
return son == manip.transform_incoming(son, collection)
qcheck.check_unittest(self, incoming_is_identity,
qcheck.gen_mongo_dict(3))
def outgoing_is_identity(son):
return son == manip.transform_outgoing(son, collection)
qcheck.check_unittest(self, outgoing_is_identity,
qcheck.gen_mongo_dict(3))
def test_id_injection(self):
manip = ObjectIdInjector()
collection = self.db.test
def incoming_adds_id(son):
son = manip.transform_incoming(son, collection)
assert "_id" in son
return True
qcheck.check_unittest(self, incoming_adds_id,
qcheck.gen_mongo_dict(3))
def outgoing_is_identity(son):
return son == manip.transform_outgoing(son, collection)
qcheck.check_unittest(self, outgoing_is_identity,
qcheck.gen_mongo_dict(3))
def test_id_shuffling(self):
manip = ObjectIdShuffler()
collection = self.db.test
def incoming_moves_id(son_in):
son = manip.transform_incoming(son_in, collection)
if not "_id" in son:
return True
for (k, v) in son.items():
self.assertEqual(k, "_id")
break
# Key order matters in SON equality test,
# matching collections.OrderedDict
if isinstance(son_in, SON):
return son_in.to_dict() == son.to_dict()
return son_in == son
self.assertTrue(incoming_moves_id({}))
self.assertTrue(incoming_moves_id({"_id": 12}))
self.assertTrue(incoming_moves_id({"hello": "world", "_id": 12}))
self.assertTrue(incoming_moves_id(SON([("hello", "world"),
("_id", 12)])))
def outgoing_is_identity(son):
return son == manip.transform_outgoing(son, collection)
qcheck.check_unittest(self, outgoing_is_identity,
qcheck.gen_mongo_dict(3))
def test_ns_injection(self):
manip = NamespaceInjector()
collection = self.db.test
def incoming_adds_ns(son):
son = manip.transform_incoming(son, collection)
assert "_ns" in son
return son["_ns"] == collection.name
qcheck.check_unittest(self, incoming_adds_ns,
qcheck.gen_mongo_dict(3))
def outgoing_is_identity(son):
return son == manip.transform_outgoing(son, collection)
qcheck.check_unittest(self, outgoing_is_identity,
qcheck.gen_mongo_dict(3))
if __name__ == "__main__":
unittest.main()