New write concern API PYTHON-427
This change deprecates get|set|unset_lasterror_options, replacing them with a write_concern attribute that can be accessed directly. See the write_concern docstring for an example of its use.
This commit is contained in:
parent
594a95740d
commit
8dd2670182
@ -23,12 +23,10 @@
|
||||
.. autoattribute:: read_preference
|
||||
.. autoattribute:: tag_sets
|
||||
.. autoattribute:: secondary_acceptable_latency_ms
|
||||
.. autoattribute:: write_concern
|
||||
.. autoattribute:: slave_okay
|
||||
.. autoattribute:: safe
|
||||
.. autoattribute:: uuid_subtype
|
||||
.. automethod:: get_lasterror_options
|
||||
.. automethod:: set_lasterror_options
|
||||
.. automethod:: unset_lasterror_options
|
||||
.. automethod:: insert(doc_or_docs[, manipulate=True[, safe=False[, check_keys=True[, continue_on_error=False[, **kwargs]]]]])
|
||||
.. automethod:: save(to_save[, manipulate=True[, safe=False[, check_keys=True[, **kwargs]]]])
|
||||
.. automethod:: update(spec, document[, upsert=False[, manipulate=False[, safe=False[, multi=False[, check_keys=True[, **kwargs]]]]]])
|
||||
@ -51,4 +49,7 @@
|
||||
.. automethod:: map_reduce
|
||||
.. automethod:: inline_map_reduce
|
||||
.. automethod:: find_and_modify
|
||||
.. automethod:: get_lasterror_options
|
||||
.. automethod:: set_lasterror_options
|
||||
.. automethod:: unset_lasterror_options
|
||||
|
||||
|
||||
@ -28,12 +28,10 @@
|
||||
.. autoattribute:: read_preference
|
||||
.. autoattribute:: tag_sets
|
||||
.. autoattribute:: secondary_acceptable_latency_ms
|
||||
.. autoattribute:: write_concern
|
||||
.. autoattribute:: slave_okay
|
||||
.. autoattribute:: safe
|
||||
.. autoattribute:: is_locked
|
||||
.. automethod:: get_lasterror_options
|
||||
.. automethod:: set_lasterror_options
|
||||
.. automethod:: unset_lasterror_options
|
||||
.. automethod:: database_names
|
||||
.. automethod:: drop_database
|
||||
.. automethod:: copy_database(from_name, to_name[, from_host=None[, username=None[, password=None]]])
|
||||
@ -45,3 +43,6 @@
|
||||
.. automethod:: set_cursor_manager
|
||||
.. automethod:: fsync
|
||||
.. automethod:: unlock
|
||||
.. automethod:: get_lasterror_options
|
||||
.. automethod:: set_lasterror_options
|
||||
.. automethod:: unset_lasterror_options
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
.. autoattribute:: read_preference
|
||||
.. autoattribute:: tag_sets
|
||||
.. autoattribute:: secondary_acceptable_latency_ms
|
||||
.. autoattribute:: write_concern
|
||||
.. autoattribute:: slave_okay
|
||||
.. autoattribute:: safe
|
||||
.. automethod:: get_lasterror_options
|
||||
|
||||
@ -29,11 +29,12 @@
|
||||
.. autoattribute:: read_preference
|
||||
.. autoattribute:: tag_sets
|
||||
.. autoattribute:: secondary_acceptable_latency_ms
|
||||
.. autoattribute:: write_concern
|
||||
.. autoattribute:: safe
|
||||
.. automethod:: get_lasterror_options
|
||||
.. automethod:: set_lasterror_options
|
||||
.. automethod:: unset_lasterror_options
|
||||
.. automethod:: database_names
|
||||
.. automethod:: drop_database
|
||||
.. automethod:: copy_database(from_name, to_name[, from_host=None[, username=None[, password=None]]])
|
||||
.. automethod:: close_cursor
|
||||
.. automethod:: get_lasterror_options
|
||||
.. automethod:: set_lasterror_options
|
||||
.. automethod:: unset_lasterror_options
|
||||
|
||||
@ -88,7 +88,7 @@ class Collection(common.BaseObject):
|
||||
secondary_acceptable_latency_ms=(
|
||||
database.secondary_acceptable_latency_ms),
|
||||
safe=database.safe,
|
||||
**(database.get_lasterror_options()))
|
||||
**database.write_concern)
|
||||
|
||||
if not isinstance(name, basestring):
|
||||
raise TypeError("name must be an instance "
|
||||
|
||||
@ -182,6 +182,22 @@ SAFE_OPTIONS = frozenset([
|
||||
])
|
||||
|
||||
|
||||
class WriteConcern(dict):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""A subclass of dict that overrides __setitem__ to
|
||||
validate write concern options.
|
||||
"""
|
||||
super(WriteConcern, self).__init__(*args, **kwargs)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if key not in SAFE_OPTIONS:
|
||||
raise ConfigurationError("%s is not a valid write "
|
||||
"concern option." % (key,))
|
||||
key, value = validate(key, value)
|
||||
super(WriteConcern, self).__setitem__(key, value)
|
||||
|
||||
|
||||
class BaseObject(object):
|
||||
"""A base class that provides attributes and methods common
|
||||
to multiple pymongo classes.
|
||||
@ -196,7 +212,7 @@ class BaseObject(object):
|
||||
self.__tag_sets = [{}]
|
||||
self.__secondary_acceptable_latency_ms = 15
|
||||
self.__safe = None
|
||||
self.__safe_opts = {}
|
||||
self.__write_concern = WriteConcern()
|
||||
self.__set_options(options)
|
||||
if (self.__read_pref == ReadPreference.PRIMARY
|
||||
and self.__tag_sets != [{}]
|
||||
@ -212,16 +228,14 @@ class BaseObject(object):
|
||||
"but write concerns have been set making safe True. "
|
||||
"Please set safe to True.", UserWarning)
|
||||
|
||||
def __set_safe_option(self, option, value, check=False):
|
||||
def __set_safe_option(self, option, value):
|
||||
"""Validates and sets getlasterror options for this
|
||||
object (Connection, Database, Collection, etc.)
|
||||
"""
|
||||
if value is None:
|
||||
self.__safe_opts.pop(option, None)
|
||||
self.__write_concern.pop(option, None)
|
||||
else:
|
||||
if check:
|
||||
option, value = validate(option, value)
|
||||
self.__safe_opts[option] = value
|
||||
self.__write_concern[option] = value
|
||||
self.__safe = True
|
||||
|
||||
def __set_options(self, options):
|
||||
@ -247,6 +261,47 @@ class BaseObject(object):
|
||||
else:
|
||||
self.__set_safe_option(option, value)
|
||||
|
||||
def __set_write_concern(self, value):
|
||||
"""Property setter for write_concern."""
|
||||
if not isinstance(value, dict):
|
||||
raise ConfigurationError("write_concern must be an "
|
||||
"instance of dict or a subclass.")
|
||||
# Make a copy here to avoid users accidentally setting the
|
||||
# same dict on multiple instances.
|
||||
wc = WriteConcern()
|
||||
for k, v in value.iteritems():
|
||||
# Make sure we validate each option.
|
||||
wc[k] = v
|
||||
self.__write_concern = wc
|
||||
|
||||
def __get_write_concern(self):
|
||||
"""The write concern for this instance.
|
||||
|
||||
Supports dict style access for getting/setting write concern
|
||||
options. Valid options include w=<int/string>, wtimeout=<int>,
|
||||
j=<bool>, fsync=<bool>.
|
||||
|
||||
>>> c.write_concern
|
||||
{}
|
||||
>>> c.write_concern = {'w': 2, 'wtimeout': 1000}
|
||||
>>> c.write_concern
|
||||
{'wtimeout': 1000, 'w': 2}
|
||||
>>> c.write_concern['j'] = True
|
||||
>>> c.write_concern
|
||||
{'wtimeout': 1000, 'j': True, 'w': 2}
|
||||
>>> c.write_concern = {'j': True}
|
||||
>>> c.write_concern
|
||||
{'j': True}
|
||||
|
||||
.. note:: Accessing :attr:`write_concern` returns its value
|
||||
(a subclass of :class:`dict`), not a copy.
|
||||
"""
|
||||
# To support dict style access we have to return the actual
|
||||
# WriteConcern here, not a copy.
|
||||
return self.__write_concern
|
||||
|
||||
write_concern = property(__get_write_concern, __set_write_concern)
|
||||
|
||||
def __get_slave_okay(self):
|
||||
"""DEPRECATED. Use `read_preference` instead.
|
||||
|
||||
@ -334,30 +389,43 @@ class BaseObject(object):
|
||||
safe = property(__get_safe, __set_safe)
|
||||
|
||||
def get_lasterror_options(self):
|
||||
"""Returns a dict of the getlasterror options set
|
||||
on this instance.
|
||||
"""DEPRECATED: Use :attr:`write_concern` instead.
|
||||
|
||||
Returns a dict of the getlasterror options set on this instance.
|
||||
|
||||
.. versionchanged:: 2.3+
|
||||
Deprecated get_lasterror_options.
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return self.__safe_opts.copy()
|
||||
warnings.warn("get_lasterror_options is deprecated. Please use "
|
||||
"write_concern instead.", DeprecationWarning)
|
||||
return self.__write_concern.copy()
|
||||
|
||||
def set_lasterror_options(self, **kwargs):
|
||||
"""Set getlasterror options for this instance.
|
||||
"""DEPRECATED: Use :attr:`write_concern` instead.
|
||||
|
||||
Valid options include j=<bool>, w=<int>, wtimeout=<int>,
|
||||
Set getlasterror options for this instance.
|
||||
|
||||
Valid options include j=<bool>, w=<int/string>, wtimeout=<int>,
|
||||
and fsync=<bool>. Implies safe=True.
|
||||
|
||||
:Parameters:
|
||||
- `**kwargs`: Options should be passed as keyword
|
||||
arguments (e.g. w=2, fsync=True)
|
||||
|
||||
.. versionchanged:: 2.3+
|
||||
Deprecated set_lasterror_options.
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
warnings.warn("set_lasterror_options is deprecated. Please use "
|
||||
"write_concern instead.", DeprecationWarning)
|
||||
for key, value in kwargs.iteritems():
|
||||
self.__set_safe_option(key, value, check=True)
|
||||
self.__set_safe_option(key, value)
|
||||
|
||||
def unset_lasterror_options(self, *options):
|
||||
"""Unset getlasterror options for this instance.
|
||||
"""DEPRECATED: Use :attr:`write_concern` instead.
|
||||
|
||||
Unset getlasterror options for this instance.
|
||||
|
||||
If no options are passed unsets all getlasterror options.
|
||||
This does not set `safe` to False.
|
||||
@ -365,13 +433,17 @@ class BaseObject(object):
|
||||
:Parameters:
|
||||
- `*options`: The list of options to unset.
|
||||
|
||||
.. versionchanged:: 2.3+
|
||||
Deprecated unset_lasterror_options.
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
warnings.warn("unset_lasterror_options is deprecated. Please use "
|
||||
"write_concern instead.", DeprecationWarning)
|
||||
if len(options):
|
||||
for option in options:
|
||||
self.__safe_opts.pop(option, None)
|
||||
self.__write_concern.pop(option, None)
|
||||
else:
|
||||
self.__safe_opts = {}
|
||||
self.__write_concern = WriteConcern()
|
||||
|
||||
def _get_safe_and_lasterror_options(self, safe=None, **options):
|
||||
"""Get the current safe mode and any getLastError options.
|
||||
@ -392,5 +464,5 @@ class BaseObject(object):
|
||||
if safe or options:
|
||||
safe = True
|
||||
if not options:
|
||||
options.update(self.get_lasterror_options())
|
||||
options.update(self.__write_concern)
|
||||
return safe, options
|
||||
|
||||
@ -67,7 +67,7 @@ class Database(common.BaseObject):
|
||||
secondary_acceptable_latency_ms=(
|
||||
connection.secondary_acceptable_latency_ms),
|
||||
safe=connection.safe,
|
||||
**(connection.get_lasterror_options()))
|
||||
**connection.write_concern)
|
||||
|
||||
if not isinstance(name, basestring):
|
||||
raise TypeError("name must be an instance "
|
||||
|
||||
@ -15,9 +15,13 @@
|
||||
"""Test the pymongo common module."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from bson.son import SON
|
||||
from pymongo.connection import Connection
|
||||
from pymongo.errors import ConfigurationError, OperationFailure
|
||||
from test.utils import drop_collections
|
||||
@ -106,14 +110,17 @@ class TestCommon(unittest.TestCase):
|
||||
self.assertTrue(c.safe)
|
||||
d = {'w': 1, 'wtimeout': 300, 'fsync': True, 'j': True}
|
||||
self.assertEqual(d, c.get_lasterror_options())
|
||||
self.assertEqual(d, c.write_concern)
|
||||
db = c.test
|
||||
self.assertTrue(db.slave_okay)
|
||||
self.assertTrue(db.safe)
|
||||
self.assertEqual(d, db.get_lasterror_options())
|
||||
self.assertEqual(d, db.write_concern)
|
||||
coll = db.test
|
||||
self.assertTrue(coll.slave_okay)
|
||||
self.assertTrue(coll.safe)
|
||||
self.assertEqual(d, coll.get_lasterror_options())
|
||||
self.assertEqual(d, coll.write_concern)
|
||||
cursor = coll.find()
|
||||
self.assertTrue(cursor._Cursor__slave_okay)
|
||||
cursor = coll.find(slave_okay=False)
|
||||
@ -127,14 +134,17 @@ class TestCommon(unittest.TestCase):
|
||||
c.slave_okay = False
|
||||
self.assertFalse(c.slave_okay)
|
||||
self.assertEqual({}, c.get_lasterror_options())
|
||||
self.assertEqual({}, c.write_concern)
|
||||
db = c.test
|
||||
self.assertFalse(db.slave_okay)
|
||||
self.assertFalse(db.safe)
|
||||
self.assertEqual({}, db.get_lasterror_options())
|
||||
self.assertEqual({}, db.write_concern)
|
||||
coll = db.test
|
||||
self.assertFalse(coll.slave_okay)
|
||||
self.assertFalse(coll.safe)
|
||||
self.assertEqual({}, coll.get_lasterror_options())
|
||||
self.assertEqual({}, coll.write_concern)
|
||||
cursor = coll.find()
|
||||
self.assertFalse(cursor._Cursor__slave_okay)
|
||||
cursor = coll.find(slave_okay=True)
|
||||
@ -142,15 +152,21 @@ class TestCommon(unittest.TestCase):
|
||||
|
||||
coll.set_lasterror_options(j=True)
|
||||
self.assertEqual({'j': True}, coll.get_lasterror_options())
|
||||
self.assertEqual({'j': True}, coll.write_concern)
|
||||
self.assertEqual({}, db.get_lasterror_options())
|
||||
self.assertEqual({}, db.write_concern)
|
||||
self.assertFalse(db.safe)
|
||||
self.assertEqual({}, c.get_lasterror_options())
|
||||
self.assertEqual({}, c.write_concern)
|
||||
self.assertFalse(c.safe)
|
||||
|
||||
db.set_lasterror_options(w='majority')
|
||||
self.assertEqual({'j': True}, coll.get_lasterror_options())
|
||||
self.assertEqual({'j': True}, coll.write_concern)
|
||||
self.assertEqual({'w': 'majority'}, db.get_lasterror_options())
|
||||
self.assertEqual({'w': 'majority'}, db.write_concern)
|
||||
self.assertEqual({}, c.get_lasterror_options())
|
||||
self.assertEqual({}, c.write_concern)
|
||||
self.assertFalse(c.safe)
|
||||
db.slave_okay = True
|
||||
self.assertTrue(db.slave_okay)
|
||||
@ -179,6 +195,51 @@ class TestCommon(unittest.TestCase):
|
||||
|
||||
warnings.resetwarnings()
|
||||
|
||||
def test_write_concern(self):
|
||||
c = Connection(pair)
|
||||
|
||||
self.assertEqual({}, c.write_concern)
|
||||
wc = {'w': 2, 'wtimeout': 1000}
|
||||
c.write_concern = wc
|
||||
self.assertEqual(wc, c.write_concern)
|
||||
wc = {'w': 3, 'wtimeout': 1000}
|
||||
c.write_concern['w'] = 3
|
||||
self.assertEqual(wc, c.write_concern)
|
||||
wc = {'w': 3}
|
||||
del c.write_concern['wtimeout']
|
||||
self.assertEqual(wc, c.write_concern)
|
||||
|
||||
wc = {'w': 3, 'wtimeout': 1000}
|
||||
c = Connection(w=3, wtimeout=1000)
|
||||
self.assertEqual(wc, c.write_concern)
|
||||
wc = {'w': 2, 'wtimeout': 1000}
|
||||
c.write_concern = wc
|
||||
self.assertEqual(wc, c.write_concern)
|
||||
|
||||
db = c.test
|
||||
self.assertEqual(wc, db.write_concern)
|
||||
coll = db.test
|
||||
self.assertEqual(wc, coll.write_concern)
|
||||
coll.write_concern = {'j': True}
|
||||
self.assertEqual({'j': True}, coll.write_concern)
|
||||
self.assertEqual(wc, db.write_concern)
|
||||
|
||||
wc = SON([('w', 2)])
|
||||
coll.write_concern = wc
|
||||
self.assertEqual(wc.to_dict(), coll.write_concern)
|
||||
|
||||
def f():
|
||||
c.write_concern = {'foo': 'bar'}
|
||||
self.assertRaises(ConfigurationError, f)
|
||||
|
||||
def f():
|
||||
c.write_concern['foo'] = 'bar'
|
||||
self.assertRaises(ConfigurationError, f)
|
||||
|
||||
def f():
|
||||
c.write_concern = [('foo', 'bar')]
|
||||
self.assertRaises(ConfigurationError, f)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user