PYTHON-881 - Backport CodecOptions class from 3.x.

This commit is contained in:
Bernie Hackett 2015-05-26 19:23:40 -07:00
parent f2c01af265
commit d70578f650
17 changed files with 185 additions and 39 deletions

View File

@ -67,29 +67,53 @@ change to this in a future release.
.. versionadded:: 1.5
"""
JAVA_LEGACY = 5
"""Used with :attr:`pymongo.collection.Collection.uuid_subtype`
to specify that UUIDs should be stored in the legacy byte order
used by the Java driver.
STANDARD = UUID_SUBTYPE
"""The standard UUID representation.
:class:`uuid.UUID` instances will automatically be encoded
by :mod:`bson` using :data:`OLD_UUID_SUBTYPE`.
:class:`uuid.UUID` instances will automatically be encoded to
and decoded from BSON binary, using RFC-4122 byte order with
binary subtype :data:`UUID_SUBTYPE`.
.. versionadded:: 2.9
"""
PYTHON_LEGACY = OLD_UUID_SUBTYPE
"""The Python legacy UUID representation.
:class:`uuid.UUID` instances will automatically be encoded to
and decoded from BSON binary, using RFC-4122 byte order with
binary subtype :data:`OLD_UUID_SUBTYPE`.
.. versionadded:: 2.9
"""
JAVA_LEGACY = 5
"""The Java legacy UUID representation.
:class:`uuid.UUID` instances will automatically be encoded to
and decoded from BSON binary, using the Java driver's legacy
byte order with binary subtype :data:`OLD_UUID_SUBTYPE`.
.. versionadded:: 2.3
"""
CSHARP_LEGACY = 6
"""Used with :attr:`pymongo.collection.Collection.uuid_subtype`
to specify that UUIDs should be stored in the legacy byte order
used by the C# driver.
"""The C#/.net legacy UUID representation.
:class:`uuid.UUID` instances will automatically be encoded
by :mod:`bson` using :data:`OLD_UUID_SUBTYPE`.
:class:`uuid.UUID` instances will automatically be encoded to
and decoded from BSON binary, using the C# driver's legacy
byte order and binary subtype :data:`OLD_UUID_SUBTYPE`.
.. versionadded:: 2.3
"""
ALL_UUID_SUBTYPES = (OLD_UUID_SUBTYPE, UUID_SUBTYPE, JAVA_LEGACY, CSHARP_LEGACY)
ALL_UUID_REPRESENTATIONS = (STANDARD, PYTHON_LEGACY, JAVA_LEGACY, CSHARP_LEGACY)
UUID_REPRESENTATION_NAMES = {
PYTHON_LEGACY: 'PYTHON_LEGACY',
STANDARD: 'STANDARD',
JAVA_LEGACY: 'JAVA_LEGACY',
CSHARP_LEGACY: 'CSHARP_LEGACY'}
MD5_SUBTYPE = 5
"""BSON binary subtype for an MD5 hash.

77
bson/codec_options.py Normal file
View File

@ -0,0 +1,77 @@
# Copyright 2014-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.
"""Tools for specifying BSON codec options."""
from bson.binary import (ALL_UUID_REPRESENTATIONS,
PYTHON_LEGACY,
UUID_REPRESENTATION_NAMES)
class CodecOptions(tuple):
"""Encapsulates BSON options used in CRUD operations.
:Parameters:
- `document_class`: BSON documents returned in queries will be decoded
to an instance of this class.
- `tz_aware`: If ``True``, BSON datetimes will be decoded to timezone
aware instances of :class:`~datetime.datetime`. Otherwise they will be
naive. Defaults to ``False``.
- `uuid_representation`: The BSON representation to use when encoding
and decoding instances of :class:`~uuid.UUID`. Defaults to
:data:`~bson.binary.PYTHON_LEGACY`.
"""
__slots__ = ()
def __new__(cls, document_class=dict,
tz_aware=False, uuid_representation=PYTHON_LEGACY):
if not isinstance(tz_aware, bool):
raise TypeError("tz_aware must be True or False")
if uuid_representation not in ALL_UUID_REPRESENTATIONS:
raise ValueError("uuid_representation must be a value "
"from bson.binary.ALL_UUID_REPRESENTATIONS")
return tuple.__new__(
cls, (document_class, tz_aware, uuid_representation))
def __repr__(self):
if self.document_class is dict:
document_class_repr = 'dict'
else:
document_class_repr = repr(self.document_class)
uuid_rep_repr = UUID_REPRESENTATION_NAMES.get(self.uuid_representation,
self.uuid_representation)
return (
'CodecOptions(document_class=%s, tz_aware=%r, uuid_representation='
'%s)' % (document_class_repr, self.tz_aware, uuid_rep_repr))
def __getnewargs__(self):
return tuple(self)
@property
def document_class(self):
return self[0]
@property
def tz_aware(self):
return self[1]
@property
def uuid_representation(self):
return self[2]
DEFAULT_CODEC_OPTIONS = CodecOptions()

View File

@ -9,6 +9,8 @@
.. autodata:: OLD_BINARY_SUBTYPE
.. autodata:: OLD_UUID_SUBTYPE
.. autodata:: UUID_SUBTYPE
.. autodata:: STANDARD
.. autodata:: PYTHON_LEGACY
.. autodata:: JAVA_LEGACY
.. autodata:: CSHARP_LEGACY
.. autodata:: MD5_SUBTYPE

View File

@ -0,0 +1,6 @@
:mod:`codec_options` -- Tools for specifying BSON codec options
===============================================================
.. automodule:: bson.codec_options
:synopsis: Tools for specifying BSON codec options.
:members:

View File

@ -11,14 +11,15 @@ Sub-modules:
:maxdepth: 2
binary
regex
code
codec_options
dbref
errors
json_util
max_key
min_key
objectid
regex
son
timestamp
tz_util

View File

@ -24,6 +24,7 @@
.. autoattribute:: full_name
.. autoattribute:: name
.. autoattribute:: database
.. autoattribute:: codec_options
.. autoattribute:: read_preference
.. autoattribute:: tag_sets
.. autoattribute:: secondary_acceptable_latency_ms

View File

@ -29,6 +29,7 @@
.. autoattribute:: max_message_size
.. autoattribute:: min_wire_version
.. autoattribute:: max_wire_version
.. autoattribute:: codec_options
.. autoattribute:: read_preference
.. autoattribute:: tag_sets
.. autoattribute:: secondary_acceptable_latency_ms

View File

@ -23,6 +23,7 @@
.. note:: Use dictionary style access if `collection_name` is an
attribute of the :class:`Database` class eg: db[`collection_name`].
.. autoattribute:: codec_options
.. autoattribute:: read_preference
.. autoattribute:: tag_sets
.. autoattribute:: secondary_acceptable_latency_ms

View File

@ -29,6 +29,7 @@
.. autoattribute:: max_message_size
.. autoattribute:: min_wire_version
.. autoattribute:: max_wire_version
.. autoattribute:: codec_options
.. autoattribute:: read_preference
.. autoattribute:: tag_sets
.. autoattribute:: secondary_acceptable_latency_ms

View File

@ -30,6 +30,7 @@
.. autoattribute:: min_wire_version
.. autoattribute:: max_wire_version
.. autoattribute:: auto_start_request
.. autoattribute:: codec_options
.. autoattribute:: read_preference
.. autoattribute:: tag_sets
.. autoattribute:: secondary_acceptable_latency_ms

View File

@ -30,6 +30,7 @@
.. autoattribute:: min_wire_version
.. autoattribute:: max_wire_version
.. autoattribute:: auto_start_request
.. autoattribute:: codec_options
.. autoattribute:: read_preference
.. autoattribute:: tag_sets
.. autoattribute:: secondary_acceptable_latency_ms

View File

@ -93,7 +93,7 @@ class Collection(common.BaseObject):
secondary_acceptable_latency_ms=(
database.secondary_acceptable_latency_ms),
safe=database.safe,
uuidrepresentation=database.uuid_subtype,
codec_options=database.codec_options,
**database.write_concern)
if not isinstance(name, basestring):

View File

@ -16,13 +16,14 @@
"""Functions and classes common to multiple pymongo modules."""
import sys
import warnings
from pymongo import read_preferences
from bson.binary import (OLD_UUID_SUBTYPE, UUID_SUBTYPE,
JAVA_LEGACY, CSHARP_LEGACY)
from bson.codec_options import CodecOptions
from pymongo import read_preferences
from pymongo.auth import MECHANISMS
from pymongo.read_preferences import ReadPreference
from pymongo.errors import ConfigurationError
from bson.binary import (OLD_UUID_SUBTYPE, UUID_SUBTYPE,
JAVA_LEGACY, CSHARP_LEGACY)
HAS_SSL = True
try:
@ -395,12 +396,12 @@ class BaseObject(object):
def __init__(self, **options):
self._codec_options = None
self.__slave_okay = False
self.__read_pref = ReadPreference.PRIMARY
self.__tag_sets = [{}]
self.__secondary_acceptable_latency_ms = 15
self.__safe = None
self.__uuid_subtype = OLD_UUID_SUBTYPE
self.__write_concern = WriteConcern()
self.__set_options(options)
if (self.__read_pref == ReadPreference.PRIMARY
@ -444,8 +445,8 @@ class BaseObject(object):
self.__read_pref = validate_read_preference(option, value)
elif option in ('tag_sets', 'readpreferencetags'):
self.__tag_sets = validate_tag_sets(option, value)
elif option == 'uuidrepresentation':
self.__uuid_subtype = validate_uuid_subtype(option, value)
elif option == 'codec_options':
self._codec_options = value
elif option in ('secondaryacceptablelatencyms',
'secondary_acceptable_latency_ms'):
self.__secondary_acceptable_latency_ms = (
@ -458,6 +459,15 @@ class BaseObject(object):
else:
self.__set_safe_option(option, value)
@property
def codec_options(self):
"""Read only access to the :class:`~bson.codec_options.CodecOptions`
of this instance.
.. versionadded:: 2.9
"""
return self._codec_options
def __set_write_concern(self, value):
"""Property setter for write_concern."""
if not isinstance(value, dict):
@ -622,11 +632,14 @@ class BaseObject(object):
subtype 4. It can also be used to force legacy byte order and subtype
compatibility with the Java and C# drivers. See the :mod:`bson.binary`
module for all options."""
return self.__uuid_subtype
return self._codec_options.uuid_representation
def __set_uuid_subtype(self, value):
"""Sets the BSON Binary subtype to be used when storing UUIDs."""
self.__uuid_subtype = validate_uuid_subtype("uuid_subtype", value)
as_class = self._codec_options.document_class
tz_aware = self._codec_options.tz_aware
uuid_rep = validate_uuid_subtype("uuid_subtype", value)
self._codec_options = CodecOptions(as_class, tz_aware, uuid_rep)
uuid_subtype = property(__get_uuid_subtype, __set_uuid_subtype)

View File

@ -57,7 +57,7 @@ class Database(common.BaseObject):
secondary_acceptable_latency_ms=(
connection.secondary_acceptable_latency_ms),
safe=connection.safe,
uuidrepresentation=connection.uuid_subtype,
codec_options=connection.codec_options,
**connection.write_concern)
if not isinstance(name, basestring):

View File

@ -33,9 +33,10 @@ directly to the master and each slave with instances of
import warnings
from bson.codec_options import CodecOptions
from pymongo import helpers, thread_util
from pymongo import ReadPreference
from pymongo.common import BaseObject
from pymongo.common import BaseObject, validate_boolean
from pymongo.mongo_client import MongoClient
from pymongo.database import Database
from pymongo.errors import AutoReconnect
@ -85,15 +86,16 @@ class MasterSlaveConnection(BaseObject):
" removed in PyMongo 3.0.",
DeprecationWarning, stacklevel=2)
validate_boolean('tz_aware', tz_aware)
codec_options = CodecOptions(document_class, tz_aware)
super(MasterSlaveConnection,
self).__init__(read_preference=ReadPreference.SECONDARY,
safe=master.safe,
codec_options=codec_options,
**master.write_concern)
self.__master = master
self.__slaves = slaves
self.__document_class = document_class
self.__tz_aware = tz_aware
self.__request_counter = thread_util.Counter(master.use_greenlets)
@property
@ -122,10 +124,12 @@ class MasterSlaveConnection(BaseObject):
return self.master.use_greenlets
def get_document_class(self):
return self.__document_class
return self._codec_options.document_class
def set_document_class(self, klass):
self.__document_class = klass
tz_aware = self._codec_options.tz_aware
uuid_rep = self._codec_options.uuid_representation
self._codec_options = CodecOptions(klass, tz_aware, uuid_rep)
document_class = property(get_document_class, set_document_class,
doc="""Default class to use for documents
@ -133,7 +137,7 @@ class MasterSlaveConnection(BaseObject):
@property
def tz_aware(self):
return self.__tz_aware
return self._codec_options.tz_aware
@property
def max_bson_size(self):

View File

@ -42,6 +42,8 @@ import threading
import time
import warnings
from bson.binary import PYTHON_LEGACY
from bson.codec_options import CodecOptions
from bson.py3compat import b
from pymongo import (auth,
common,
@ -292,6 +294,11 @@ class MongoClient(common.BaseObject):
options[option] = value
options.update(opts)
common.validate_boolean('tz_aware', tz_aware)
uuid_representation = options.pop('uuidrepresentation', PYTHON_LEGACY)
options['codec_options'] = CodecOptions(
document_class, tz_aware, uuid_representation)
self.__max_pool_size = common.validate_positive_integer_or_none(
'max_pool_size', max_pool_size)
@ -356,8 +363,6 @@ class MongoClient(common.BaseObject):
self.__event_class = event_class
self.__future_member = None
self.__document_class = document_class
self.__tz_aware = common.validate_boolean('tz_aware', tz_aware)
self.__auto_start_request = options.get('auto_start_request', False)
# cache of existing indexes used by ensure_index ops
@ -606,10 +611,12 @@ class MongoClient(common.BaseObject):
return self.__auto_start_request
def get_document_class(self):
return self.__document_class
return self._codec_options.document_class
def set_document_class(self, klass):
self.__document_class = klass
tz_aware = self._codec_options.tz_aware
uuid_rep = self._codec_options.uuid_representation
self._codec_options = CodecOptions(klass, tz_aware, uuid_rep)
document_class = property(get_document_class, set_document_class,
doc="""Default class to use for documents
@ -624,7 +631,7 @@ class MongoClient(common.BaseObject):
.. versionadded:: 1.8
"""
return self.__tz_aware
return self._codec_options.tz_aware
@property
def max_bson_size(self):

View File

@ -40,6 +40,8 @@ import time
import warnings
import weakref
from bson.binary import PYTHON_LEGACY
from bson.codec_options import CodecOptions
from bson.py3compat import b
from pymongo import (auth,
common,
@ -59,7 +61,6 @@ from pymongo.errors import (AutoReconnect,
DuplicateKeyError,
OperationFailure,
InvalidOperation)
from pymongo.read_preferences import ReadPreference
from pymongo.thread_util import DummyLock
EMPTY = b("")
@ -593,8 +594,6 @@ class MongoReplicaSetClient(common.BaseObject):
self.__max_pool_size = common.validate_positive_integer_or_none(
'max_pool_size', max_pool_size)
self.__tz_aware = common.validate_boolean('tz_aware', tz_aware)
self.__document_class = document_class
self.__monitor = None
self.__closed = False
@ -631,6 +630,11 @@ class MongoReplicaSetClient(common.BaseObject):
self.__opts[option] = value
self.__opts.update(options)
common.validate_boolean('tz_aware', tz_aware)
uuid_representation = options.pop('uuidrepresentation', PYTHON_LEGACY)
self.__opts['codec_options'] = CodecOptions(
document_class, tz_aware, uuid_representation)
self.__use_greenlets = self.__opts.get('use_greenlets', False)
if self.__use_greenlets and not have_gevent:
raise ConfigurationError(
@ -927,11 +931,13 @@ class MongoReplicaSetClient(common.BaseObject):
def get_document_class(self):
"""document_class getter"""
return self.__document_class
return self._codec_options.document_class
def set_document_class(self, klass):
"""document_class setter"""
self.__document_class = klass
tz_aware = self._codec_options.tz_aware
uuid_rep = self._codec_options.uuid_representation
self._codec_options = CodecOptions(klass, tz_aware, uuid_rep)
document_class = property(get_document_class, set_document_class,
doc="""Default class to use for documents
@ -942,7 +948,7 @@ class MongoReplicaSetClient(common.BaseObject):
def tz_aware(self):
"""Does this client return timezone-aware datetimes?
"""
return self.__tz_aware
return self._codec_options.tz_aware
@property
def max_bson_size(self):