PYTHON-674 python 2/3 single-source for the pymongo module

This commit is contained in:
Luke Lovett 2014-04-23 17:58:25 +00:00
parent f5c71a5376
commit d494105aa4
17 changed files with 262 additions and 208 deletions

View File

@ -152,7 +152,8 @@ def _json_convert(obj):
"""
if hasattr(obj, 'iteritems') or hasattr(obj, 'items'): # PY3 support
return SON(((k, _json_convert(v)) for k, v in iteritems(obj)))
elif hasattr(obj, '__iter__') and not isinstance(obj, string_types):
elif hasattr(obj, '__iter__') and not isinstance(obj, (text_type,
binary_type)):
return list((_json_convert(v) for v in obj))
try:
return default(obj)

View File

@ -40,17 +40,26 @@ if PY3:
return bytes.fromhex(h)
def iteritems(d):
return d.items()
return iter(d.items())
def itervalues(d):
return iter(d.values())
def reraise(exctype, value, trace=None):
raise exctype(str(value)).with_traceback(trace)
def _unicode(s):
return s
binary_type = bytes
text_type = str
string_type = str
integer_types = int
next_item = "__next__"
# TODO: remove when gridfs module is made single-source
next_item = '__next__'
string_types = (bytes, text_type)
else:
try:
from cStringIO import StringIO
@ -73,15 +82,21 @@ else:
def iteritems(d):
return d.iteritems()
def itervalues(d):
return d.itervalues()
# "raise x, y, z" raises SyntaxError in Python 3
exec("""def reraise(exctype, value, trace=None):
raise exctype, str(value), trace
""")
_unicode = unicode
binary_type = str
string_type = basestring
text_type = unicode
integer_types = (int, long)
next_item = "next"
string_types = (binary_type, text_type)
# TODO: remove when gridfs module is made single-source
next_item = 'next'
string_types = (bytes, text_type)

View File

@ -14,7 +14,6 @@
"""Python driver for MongoDB."""
ASCENDING = 1
"""Ascending sort order."""
DESCENDING = -1
@ -70,7 +69,7 @@ ALL = 2
version_tuple = (3, 0, ".dev0")
def get_version_string():
if isinstance(version_tuple[-1], basestring):
if isinstance(version_tuple[-1], str):
return '.'.join(map(str, version_tuple[:-1])) + version_tuple[-1]
return '.'.join(map(str, version_tuple))

View File

@ -14,15 +14,9 @@
"""Authentication helpers."""
from __future__ import unicode_literals
import hmac
try:
import hashlib
_MD5 = hashlib.md5
_DMOD = _MD5
except ImportError: # for Python < 2.5
import md5
_MD5 = md5.new
_DMOD = md5
HAVE_KERBEROS = True
try:
@ -30,8 +24,10 @@ try:
except ImportError:
HAVE_KERBEROS = False
from hashlib import md5
from bson.binary import Binary
from bson.py3compat import b
from bson.py3compat import b, string_type, _unicode
from bson.son import SON
from pymongo.errors import ConfigurationError, OperationFailure
@ -55,29 +51,29 @@ def _build_credentials_tuple(mech, source, user, passwd, extra):
def _password_digest(username, password):
"""Get a password digest to use for authentication.
"""
if not isinstance(password, basestring):
raise TypeError("password must be an instance "
"of %s" % (basestring.__name__,))
if not isinstance(password, string_type):
raise TypeError("password must be an "
"instance of %s" % (string_type.__name__,))
if len(password) == 0:
raise ValueError("password can't be empty")
if not isinstance(username, basestring):
raise TypeError("username must be an instance "
"of %s" % (basestring.__name__,))
if not isinstance(username, string_type):
raise TypeError("password must be an "
"instance of %s" % (string_type.__name__,))
md5hash = _MD5()
md5hash = md5()
data = "%s:mongo:%s" % (username, password)
md5hash.update(data.encode('utf-8'))
return unicode(md5hash.hexdigest())
return _unicode(md5hash.hexdigest())
def _auth_key(nonce, username, password):
"""Get an auth key to use for authentication.
"""
digest = _password_digest(username, password)
md5hash = _MD5()
data = "%s%s%s" % (nonce, unicode(username), digest)
md5hash = md5()
data = "%s%s%s" % (nonce, _unicode(username), digest)
md5hash.update(data.encode('utf-8'))
return unicode(md5hash.hexdigest())
return _unicode(md5hash.hexdigest())
def _authenticate_gssapi(credentials, sock_info, cmd_func):
@ -114,7 +110,7 @@ def _authenticate_gssapi(credentials, sock_info, cmd_func):
response, _ = cmd_func(sock_info, '$external', cmd)
# Limit how many times we loop to catch protocol / library issues
for _ in xrange(10):
for _ in range(10):
result = kerberos.authGSSClientStep(ctx,
str(response['payload']))
if result == -1:
@ -156,7 +152,7 @@ def _authenticate_gssapi(credentials, sock_info, cmd_func):
finally:
kerberos.authGSSClientClean(ctx)
except kerberos.KrbError, exc:
except kerberos.KrbError as exc:
raise OperationFailure(str(exc))
@ -181,14 +177,14 @@ def _authenticate_cram_md5(credentials, sock_info, cmd_func):
passwd = _password_digest(username, password)
cmd = SON([('saslStart', 1),
('mechanism', 'CRAM-MD5'),
('payload', Binary(b(''))),
('payload', Binary(b'')),
('autoAuthorize', 1)])
response, _ = cmd_func(sock_info, source, cmd)
# MD5 as implicit default digest for digestmod is deprecated
# in python 3.4
mac = hmac.HMAC(key=passwd.encode('utf-8'), digestmod=_DMOD)
mac = hmac.HMAC(key=passwd.encode('utf-8'), digestmod=md5)
mac.update(response['payload'])
challenge = username.encode('utf-8') + b(' ') + b(mac.hexdigest())
challenge = username.encode('utf-8') + b' ' + b(mac.hexdigest())
cmd = SON([('saslContinue', 1),
('conversationId', response['conversationId']),
('payload', Binary(challenge))])

View File

@ -17,6 +17,8 @@
.. versionadded:: 2.7
"""
from __future__ import unicode_literals
from bson.objectid import ObjectId
from bson.son import SON
from pymongo.errors import (BulkWriteError,
@ -72,10 +74,10 @@ def _make_error(index, code, errmsg, operation):
"""Create and return an error document.
"""
return {
u"index": index,
u"code": code,
u"errmsg": errmsg,
u"op": operation
"index": index,
"code": code,
"errmsg": errmsg,
"op": operation
}
@ -113,7 +115,7 @@ def _merge_legacy(run, full_result, result, index):
full_result['nInserted'] += 1
elif run.op_type == _UPDATE:
if "upserted" in result:
doc = {u"index": run.index(index), u"_id": result["upserted"]}
doc = {"index": run.index(index), "_id": result["upserted"]}
full_result["upserted"].append(doc)
full_result['nUpserted'] += affected
else:
@ -146,7 +148,7 @@ def _merge_command(run, full_result, results):
else:
n_upserted = 1
index = run.index(offset)
doc = {u"index": index, u"_id": upserted}
doc = {"index": index, "_id": upserted}
full_result["upserted"].append(doc)
full_result["nUpserted"] += n_upserted
full_result["nMatched"] += (affected - n_upserted)
@ -168,7 +170,7 @@ def _merge_command(run, full_result, results):
idx = doc["index"] + offset
doc["index"] = run.index(idx)
# Add the failed operation to the error document.
doc[u"op"] = run.ops[idx]
doc["op"] = run.ops[idx]
full_result["writeErrors"].extend(write_errors)
wc_error = result.get("writeConcernError")
@ -207,7 +209,7 @@ class _Bulk(object):
# Update can not be {}
if not update:
raise ValueError('update only works with $ operators')
first = iter(update).next()
first = next(iter(update))
if not first.startswith('$'):
raise ValueError('update only works with $ operators')
cmd = SON([('q', selector), ('u', update),
@ -221,7 +223,7 @@ class _Bulk(object):
raise TypeError('replacement must be an instance of dict')
# Replacement can be {}
if replacement:
first = iter(replacement).next()
first = next(iter(replacement))
if first.startswith('$'):
raise ValueError('replacement can not include $ operators')
cmd = SON([('q', selector), ('u', replacement),
@ -378,7 +380,7 @@ class _Bulk(object):
multi=(not operation['limit']),
**write_concern)
_merge_legacy(run, full_result, result, idx)
except DocumentTooLarge, exc:
except DocumentTooLarge as exc:
# MongoDB 2.6 uses error code 2 for "too large".
error = _make_error(
run.index(idx), _BAD_VALUE, str(exc), operation)
@ -386,7 +388,7 @@ class _Bulk(object):
if self.ordered:
stop = True
break
except OperationFailure, exc:
except OperationFailure as exc:
if not exc.details:
# Some error not related to the write operation
# (e.g. kerberos failure). Re-raise immediately.

View File

@ -14,10 +14,15 @@
"""Collection level utilities for Mongo."""
from __future__ import unicode_literals
import warnings
from bson.code import Code
from bson.objectid import ObjectId
from bson.py3compat import (_unicode,
integer_types,
string_type)
from bson.son import SON
from pymongo import (bulk,
common,
@ -40,7 +45,7 @@ except ImportError:
def _gen_index_name(keys):
"""Generate an index name from the set of fields it is over.
"""
return u"_".join([u"%s_%s" % item for item in keys])
return "_".join(["%s_%s" % item for item in keys])
class Collection(common.BaseObject):
@ -90,9 +95,9 @@ class Collection(common.BaseObject):
uuidrepresentation=database.uuid_subtype,
**database.write_concern)
if not isinstance(name, basestring):
if not isinstance(name, string_type):
raise TypeError("name must be an instance "
"of %s" % (basestring.__name__,))
"of %s" % (string_type.__name__,))
if not name or ".." in name:
raise InvalidName("collection names cannot be empty")
@ -108,8 +113,8 @@ class Collection(common.BaseObject):
"null character")
self.__database = database
self.__name = unicode(name)
self.__full_name = u"%s.%s" % (self.__database.name, self.__name)
self.__name = _unicode(name)
self.__full_name = "%s.%s" % (self.__database.name, self.__name)
if create or kwargs:
self.__create(kwargs)
@ -132,7 +137,7 @@ class Collection(common.BaseObject):
:Parameters:
- `name`: the name of the collection to get
"""
return Collection(self.__database, u"%s.%s" % (self.__name, name))
return Collection(self.__database, "%s.%s" % (self.__name, name))
def __getitem__(self, name):
return self.__getattr__(name)
@ -543,7 +548,7 @@ class Collection(common.BaseObject):
# we check here. Passing a document with a mix of top level keys
# starting with and without a '$' is invalid and the server will
# raise an appropriate exception.
first = (document.iterkeys()).next()
first = next(iter(document))
if first.startswith('$'):
check_keys = False
@ -1018,7 +1023,8 @@ class Collection(common.BaseObject):
DeprecationWarning, stacklevel=2)
# The types supported by datetime.timedelta. 2to3 removes long.
if not isinstance(cache_for, (int, long, float)):
if not (isinstance(cache_for, integer_types) or
isinstance(cache_for, float)):
raise TypeError("cache_for must be an integer or float.")
keys = helpers._index_list(key_or_list)
@ -1037,7 +1043,7 @@ class Collection(common.BaseObject):
try:
self.__database.command('createIndexes', self.name, indexes=[index])
except OperationFailure, exc:
except OperationFailure as exc:
if exc.code in (59, None):
index["ns"] = self.__full_name
self.__database.system.indexes.insert(index, manipulate=False,
@ -1152,7 +1158,7 @@ class Collection(common.BaseObject):
"""
self.__database.connection._purge_index(self.__database.name,
self.__name)
self.drop_index(u"*")
self.drop_index("*")
def drop_index(self, index_or_name):
"""Drops the specified index on this collection.
@ -1177,7 +1183,7 @@ class Collection(common.BaseObject):
if isinstance(index_or_name, list):
name = _gen_index_name(index_or_name)
if not isinstance(name, basestring):
if not isinstance(name, string_type):
raise TypeError("index_or_name must be an index name or list")
self.__database.connection._purge_index(self.__database.name,
@ -1357,7 +1363,7 @@ class Collection(common.BaseObject):
"""
group = {}
if isinstance(key, basestring):
if isinstance(key, string_type):
group["$keyf"] = Code(key)
elif key is not None:
group = {"key": helpers._fields_list_to_dict(key)}
@ -1392,9 +1398,9 @@ class Collection(common.BaseObject):
.. versionadded:: 1.7
support for accepting keyword arguments for rename options
"""
if not isinstance(new_name, basestring):
raise TypeError("new_name must be an instance "
"of %s" % (basestring.__name__,))
if not isinstance(new_name, string_type):
raise TypeError("new_name must be an "
"instance of %s" % (string_type.__name__,))
if not new_name or ".." in new_name:
raise InvalidName("collection names cannot be empty")
@ -1469,9 +1475,9 @@ class Collection(common.BaseObject):
.. mongodoc:: mapreduce
"""
if not isinstance(out, (basestring, dict)):
if not isinstance(out, (string_type, dict)):
raise TypeError("'out' must be an instance of "
"%s or dict" % (basestring.__name__,))
"%s or dict" % (string_type.__name__,))
mode = read_preference or self.read_preference
response = self.__database.command("mapreduce", self.__name,
@ -1642,6 +1648,8 @@ class Collection(common.BaseObject):
def next(self):
raise TypeError("'Collection' object is not iterable")
__next__ = next
def __call__(self, *args, **kwargs):
"""This is only here so that some API misusages are easier to debug.
"""

View File

@ -16,6 +16,7 @@
from collections import deque
from bson.py3compat import integer_types
from pymongo import helpers, message
from pymongo.errors import AutoReconnect, CursorNotFound
@ -80,7 +81,7 @@ class CommandCursor(object):
:Parameters:
- `batch_size`: The size of each batch of results requested.
"""
if not isinstance(batch_size, (int, long)):
if not isinstance(batch_size, integer_types):
raise TypeError("batch_size must be an integer")
if batch_size < 0:
raise ValueError("batch_size must be >= 0")
@ -168,6 +169,8 @@ class CommandCursor(object):
else:
raise StopIteration
__next__ = next
def __enter__(self):
return self

View File

@ -22,6 +22,7 @@ from pymongo.errors import ConfigurationError
from pymongo.write_concern import WriteConcern
from bson.binary import (OLD_UUID_SUBTYPE, UUID_SUBTYPE,
JAVA_LEGACY, CSHARP_LEGACY)
from bson.py3compat import string_type, integer_types, iteritems
HAS_SSL = True
try:
@ -66,7 +67,7 @@ def validate_boolean(option, value):
"""
if isinstance(value, bool):
return value
elif isinstance(value, basestring):
elif isinstance(value, string_type):
if value not in ('true', 'false'):
raise ConfigurationError("The value of %s must be "
"'true' or 'false'" % (option,))
@ -77,9 +78,9 @@ def validate_boolean(option, value):
def validate_integer(option, value):
"""Validates that 'value' is an integer (or basestring representation).
"""
if isinstance(value, (int, long)):
if isinstance(value, integer_types):
return value
elif isinstance(value, basestring):
elif isinstance(value, string_type):
if not value.isdigit():
raise ConfigurationError("The value of %s must be "
"an integer" % (option,))
@ -102,7 +103,7 @@ def validate_readable(option, value):
"""
# First make sure its a string py3.3 open(True, 'r') succeeds
# Used in ssl cert checking due to poor ssl module error reporting
value = validate_basestring(option, value)
value = validate_string(option, value)
open(value, 'r').close()
return value
@ -132,21 +133,22 @@ def validate_positive_integer_or_none(option, value):
return validate_positive_integer(option, value)
def validate_basestring(option, value):
"""Validates that 'value' is an instance of `basestring`.
def validate_string(option, value):
"""Validates that 'value' is an instance of `basestring` for Python 2
or `str` for Python 3.
"""
if isinstance(value, basestring):
if isinstance(value, string_type):
return value
raise TypeError("Wrong type for %s, value must be an "
"instance of %s" % (option, basestring.__name__))
raise TypeError("Wrong type for %s, value must be "
"an instance of %s" % (option, string_type.__name__))
def validate_int_or_basestring(option, value):
"""Validates that 'value' is an integer or string.
"""
if isinstance(value, (int, long)):
if isinstance(value, integer_types):
return value
elif isinstance(value, basestring):
elif isinstance(value, string_type):
if value.isdigit():
return int(value)
return value
@ -214,10 +216,10 @@ def validate_auth_mechanism(option, value):
def validate_uuid_representation(dummy, value):
"""Validate the uuid representation option selected in the URI.
"""
if value not in _UUID_SUBTYPES.keys():
if value not in _UUID_SUBTYPES:
raise ConfigurationError("%s is an invalid UUID representation. "
"Must be one of "
"%s" % (value, _UUID_SUBTYPES.keys()))
"%s" % (value, list(_UUID_SUBTYPES)))
return _UUID_SUBTYPES[value]
@ -249,7 +251,7 @@ def validate_read_preference_tags(name, value):
# journal is an alias for j,
# wtimeoutms is an alias for wtimeout,
VALIDATORS = {
'replicaset': validate_basestring,
'replicaset': validate_string,
'w': validate_int_or_basestring,
'wtimeout': validate_integer,
'wtimeoutms': validate_integer,
@ -272,8 +274,8 @@ VALIDATORS = {
'auto_start_request': validate_boolean,
'use_greenlets': validate_boolean,
'authmechanism': validate_auth_mechanism,
'authsource': validate_basestring,
'gssapiservicename': validate_basestring,
'authsource': validate_string,
'gssapiservicename': validate_string,
'uuidrepresentation': validate_uuid_representation,
}
@ -327,7 +329,7 @@ class BaseObject(object):
def __set_options(self, options):
"""Validates and sets all options passed to this object."""
wc_opts = {}
for option, value in options.iteritems():
for option, value in iteritems(options):
if option == 'read_preference':
self.__read_pref = validate_read_preference(option, value)
elif option == 'readpreference':

View File

@ -18,6 +18,9 @@ from collections import deque
from bson import RE_TYPE
from bson.code import Code
from bson.py3compat import (iteritems,
integer_types,
string_type)
from bson.son import SON
from pymongo import helpers, message, read_preferences
from pymongo.read_preferences import ReadPreference, SECONDARY_OK_COMMANDS
@ -233,7 +236,7 @@ class Cursor(object):
"batch_size", "max_scan", "as_class",
"manipulate", "read_preference",
"uuid_subtype", "compile_re", "query_flags")
data = dict((k, v) for k, v in self.__dict__.iteritems()
data = dict((k, v) for k, v in iteritems(self.__dict__)
if k.startswith('_Cursor__') and k[9:] in values_to_clone)
if deepcopy:
data = self._deepcopy(data)
@ -318,7 +321,7 @@ class Cursor(object):
# by db.command or calling find_one on $cmd directly
if self.collection.name == "$cmd":
# Don't change commands that can't be sent to secondaries
command_name = spec and spec.keys()[0].lower() or ""
command_name = spec and next(iter(spec)).lower() or ""
if command_name not in SECONDARY_OK_COMMANDS:
return spec
elif command_name == 'mapreduce':
@ -345,7 +348,8 @@ class Cursor(object):
# Checking spec.keys()[0] covers the case that the spec
# was passed as an instance of SON or OrderedDict.
elif ("query" in self.__spec and
(len(self.__spec) == 1 or self.__spec.keys()[0] == "query")):
(len(self.__spec) == 1 or
next(iter(self.__spec)) == "query")):
return SON({"$query": self.__spec})
return self.__spec
@ -406,7 +410,7 @@ class Cursor(object):
.. mongodoc:: limit
"""
if not isinstance(limit, (int, long)):
if not isinstance(limit, integer_types):
raise TypeError("limit must be an integer")
if self.__exhaust:
raise InvalidOperation("Can't use limit and exhaust together.")
@ -437,7 +441,7 @@ class Cursor(object):
.. versionadded:: 1.9
"""
if not isinstance(batch_size, (int, long)):
if not isinstance(batch_size, integer_types):
raise TypeError("batch_size must be an integer")
if batch_size < 0:
raise ValueError("batch_size must be >= 0")
@ -458,7 +462,7 @@ class Cursor(object):
:Parameters:
- `skip`: the number of results to skip
"""
if not isinstance(skip, (int, long)):
if not isinstance(skip, integer_types):
raise TypeError("skip must be an integer")
if skip < 0:
raise ValueError("skip must be >= 0")
@ -480,7 +484,8 @@ class Cursor(object):
:Parameters:
- `max_time_ms`: the time limit after which the operation is aborted
"""
if not isinstance(max_time_ms, (int, long)) and max_time_ms is not None:
if (not isinstance(max_time_ms, integer_types)
and max_time_ms is not None):
raise TypeError("max_time_ms must be an integer or None")
self.__check_okay_to_chain()
@ -543,7 +548,7 @@ class Cursor(object):
self.__limit = limit
return self
if isinstance(index, (int, long)):
if isinstance(index, integer_types):
if index < 0:
raise IndexError("Cursor instances do not support negative"
"indices")
@ -727,9 +732,9 @@ class Cursor(object):
.. versionadded:: 1.2
"""
if not isinstance(key, basestring):
raise TypeError("key must be an instance "
"of %s" % (basestring.__name__,))
if not isinstance(key, string_type):
raise TypeError("key must be an "
"instance of %s" % (string_type.__name__,))
options = {"key": key}
if self.__spec:
@ -758,7 +763,7 @@ class Cursor(object):
# always use a hard limit for explains
if c.__limit:
c.__limit = -abs(c.__limit)
return c.next()
return next(c)
def hint(self, index):
"""Adds a 'hint', telling Mongo the proper index to use for the query.
@ -989,6 +994,8 @@ class Cursor(object):
else:
raise StopIteration
__next__ = next
def __enter__(self):
return self
@ -1018,7 +1025,7 @@ class Cursor(object):
if not hasattr(x, 'items'):
y, is_list, iterator = [], True, enumerate(x)
else:
y, is_list, iterator = {}, False, x.iteritems()
y, is_list, iterator = {}, False, iteritems(x)
if memo is None:
memo = {}

View File

@ -24,6 +24,7 @@ installed on a connection by calling
"""
import weakref
from bson.py3compat import integer_types
class CursorManager(object):
@ -48,8 +49,8 @@ class CursorManager(object):
:Parameters:
- `cursor_id`: cursor id to close
"""
if not isinstance(cursor_id, (int, long)):
raise TypeError("cursor_id must be an instance of (int, long)")
if not isinstance(cursor_id, integer_types):
raise TypeError("cursor_id must be an integer")
self.__connection().kill_cursors([cursor_id])
@ -83,8 +84,8 @@ class BatchCursorManager(CursorManager):
:Parameters:
- `cursor_id`: cursor id to close
"""
if not isinstance(cursor_id, (int, long)):
raise TypeError("cursor_id must be an instance of (int, long)")
if not isinstance(cursor_id, integer_types):
raise TypeError("cursor_id must be an integer")
self.__dying_cursors.append(cursor_id)

View File

@ -19,6 +19,7 @@ import warnings
from bson.binary import OLD_UUID_SUBTYPE
from bson.code import Code
from bson.dbref import DBRef
from bson.py3compat import iteritems, string_type, _unicode
from bson.son import SON
from pymongo import auth, common, helpers
from pymongo.collection import Collection
@ -65,14 +66,14 @@ class Database(common.BaseObject):
uuidrepresentation=connection.uuid_subtype,
**connection.write_concern)
if not isinstance(name, basestring):
if not isinstance(name, string_type):
raise TypeError("name must be an instance "
"of %s" % (basestring.__name__,))
"of %s" % (string_type.__name__,))
if name != '$external':
_check_name(name)
self.__name = unicode(name)
self.__name = _unicode(name)
self.__connection = connection
self.__incoming_manipulators = []
@ -274,11 +275,11 @@ class Database(common.BaseObject):
"""Internal command helper.
"""
if isinstance(command, basestring):
if isinstance(command, string_type):
command_name = command.lower()
command = SON([(command, value)])
else:
command_name = command.keys()[0].lower()
command_name = next(iter(command)).lower()
as_class = kwargs.pop('as_class', None)
fields = kwargs.pop('fields', None)
@ -435,13 +436,13 @@ class Database(common.BaseObject):
if isinstance(name, Collection):
name = name.name
if not isinstance(name, basestring):
raise TypeError("name_or_collection must be an instance of "
"%s or Collection" % (basestring.__name__,))
if not isinstance(name, string_type):
raise TypeError("name_or_collection must be an "
"instance of %s" % (string_type.__name__,))
self.__connection._purge_index(self.__name, name)
self.command("drop", unicode(name), allowable_errors=["ns not found"])
self.command("drop", _unicode(name), allowable_errors=["ns not found"])
def validate_collection(self, name_or_collection,
scandata=False, full=False):
@ -474,11 +475,11 @@ class Database(common.BaseObject):
if isinstance(name, Collection):
name = name.name
if not isinstance(name, basestring):
if not isinstance(name, string_type):
raise TypeError("name_or_collection must be an instance of "
"%s or Collection" % (basestring.__name__,))
"%s or Collection" % (string_type.__name__,))
result = self.command("validate", unicode(name),
result = self.command("validate", _unicode(name),
scandata=scandata, full=full)
valid = True
@ -489,7 +490,7 @@ class Database(common.BaseObject):
raise CollectionInvalid("%s invalid: %s" % (name, info))
# Sharded results
elif "raw" in result:
for _, res in result["raw"].iteritems():
for _, res in iteritems(result["raw"]):
if "result" in res:
info = res["result"]
if (info.find("exception") != -1 or
@ -626,6 +627,8 @@ class Database(common.BaseObject):
def next(self):
raise TypeError("'Database' object is not iterable")
__next__ = next
def _default_role(self, read_only):
if self.name == "admin":
if read_only:
@ -686,7 +689,7 @@ class Database(common.BaseObject):
try:
self.system.users.save(user, **self._get_wc_override())
except OperationFailure, exc:
except OperationFailure as exc:
# First admin user add fails gle in MongoDB >= 2.1.2
# See SERVER-4225 for more information.
if 'login' in str(exc):
@ -722,13 +725,13 @@ class Database(common.BaseObject):
.. versionadded:: 1.4
"""
if not isinstance(name, basestring):
raise TypeError("name must be an instance "
"of %s" % (basestring.__name__,))
if not isinstance(name, string_type):
raise TypeError("name must be an "
"instance of %s" % (string_type.__name__,))
if password is not None:
if not isinstance(password, basestring):
raise TypeError("password must be an instance "
"of %s or None" % (basestring.__name__,))
if not isinstance(password, string_type):
raise TypeError("password must be an "
"instance of %s" % (string_type.__name__,))
if len(password) == 0:
raise ValueError("password can't be empty")
if read_only is not None:
@ -739,7 +742,7 @@ class Database(common.BaseObject):
try:
uinfo = self.command("usersInfo", name)
except OperationFailure, exc:
except OperationFailure as exc:
# MongoDB >= 2.5.3 requires the use of commands to manage
# users. "No such command" error didn't return an error
# code (59) before MongoDB 2.4.7 so we assume that an error
@ -769,7 +772,7 @@ class Database(common.BaseObject):
try:
self.command("dropUser", name,
writeConcern=self._get_wc_override())
except OperationFailure, exc:
except OperationFailure as exc:
# See comment in add_user try / except above.
if exc.code in (59, None):
self.system.users.remove({"user": name},
@ -827,25 +830,25 @@ class Database(common.BaseObject):
.. mongodoc:: authenticate
"""
if not isinstance(name, basestring):
raise TypeError("name must be an instance "
"of %s" % (basestring.__name__,))
if password is not None and not isinstance(password, basestring):
raise TypeError("password must be an instance "
"of %s" % (basestring.__name__,))
if source is not None and not isinstance(source, basestring):
raise TypeError("source must be an instance "
"of %s" % (basestring.__name__,))
if not isinstance(name, string_type):
raise TypeError("name must be an "
"instance of %s" % (string_type.__name__,))
if password is not None and not isinstance(password, string_type):
raise TypeError("password must be an "
"instance of %s" % (string_type.__name__,))
if source is not None and not isinstance(source, string_type):
raise TypeError("source must be an "
"instance of %s" % (string_type.__name__,))
common.validate_auth_mechanism('mechanism', mechanism)
validated_options = {}
for option, value in kwargs.iteritems():
for option, value in iteritems(kwargs):
normalized, val = common.validate_auth_option(option, value)
validated_options[normalized] = val
credentials = auth._build_credentials_tuple(mechanism,
source or self.name, unicode(name),
password and unicode(password) or None,
source or self.name, _unicode(name),
password and _unicode(password) or None,
validated_options)
self.connection._cache_credentials(self.name, credentials)
return True

View File

@ -21,6 +21,7 @@ import bson
import pymongo
from bson.binary import OLD_UUID_SUBTYPE
from bson.py3compat import itervalues, string_type
from bson.son import SON
from pymongo.errors import (AutoReconnect,
CursorNotFound,
@ -38,7 +39,7 @@ def _index_list(key_or_list, direction=None):
if direction is not None:
return [(key_or_list, direction)]
else:
if isinstance(key_or_list, basestring):
if isinstance(key_or_list, string_type):
return [(key_or_list, pymongo.ASCENDING)]
elif not isinstance(key_or_list, (list, tuple)):
raise TypeError("if no direction is specified, "
@ -54,7 +55,7 @@ def _index_document(index_list):
if isinstance(index_list, dict):
raise TypeError("passing a dict to sort/create_index/hint is not "
"allowed - use a list of tuples instead. did you "
"mean %r?" % list(index_list.iteritems()))
"mean %r?" % list(iteritems(index_list)))
elif not isinstance(index_list, (list, tuple)):
raise TypeError("must use a list of (key, direction) pairs, "
"not: " + repr(index_list))
@ -63,9 +64,9 @@ def _index_document(index_list):
index = SON()
for (key, value) in index_list:
if not isinstance(key, basestring):
if not isinstance(key, string_type):
raise TypeError("first item in each key pair must be a string")
if not isinstance(value, (basestring, int, dict)):
if not isinstance(value, (string_type, int, dict)):
raise TypeError("second item in each key pair must be 1, -1, "
"'2d', 'geoHaystack', or another valid MongoDB "
"index specifier.")
@ -142,7 +143,7 @@ def _check_command_response(response, reset, msg=None, allowable_errors=None):
# Mongos returns the error details in a 'raw' object
# for some errors.
if "raw" in response:
for shard in response["raw"].itervalues():
for shard in itervalues(response["raw"]):
if not shard.get("ok"):
# Just grab the first error...
details = shard
@ -216,9 +217,9 @@ def _fields_list_to_dict(fields):
"""
as_dict = {}
for field in fields:
if not isinstance(field, basestring):
if not isinstance(field, string_type):
raise TypeError("fields must be a list of key names, "
"each an instance of %s" % (basestring.__name__,))
"each an instance of %s" % (string_type.__name__,))
as_dict[field] = 1
return as_dict

View File

@ -44,17 +44,17 @@ _INSERT = 0
_UPDATE = 1
_DELETE = 2
_EMPTY = b('')
_BSONOBJ = b('\x03')
_ZERO_8 = b('\x00')
_ZERO_16 = b('\x00\x00')
_ZERO_32 = b('\x00\x00\x00\x00')
_ZERO_64 = b('\x00\x00\x00\x00\x00\x00\x00\x00')
_SKIPLIM = b('\x00\x00\x00\x00\xff\xff\xff\xff')
_EMPTY = b''
_BSONOBJ = b'\x03'
_ZERO_8 = b'\x00'
_ZERO_16 = b'\x00\x00'
_ZERO_32 = b'\x00\x00\x00\x00'
_ZERO_64 = b'\x00\x00\x00\x00\x00\x00\x00\x00'
_SKIPLIM = b'\x00\x00\x00\x00\xff\xff\xff\xff'
_OP_MAP = {
_INSERT: b('\x04documents\x00\x00\x00\x00\x00'),
_UPDATE: b('\x04updates\x00\x00\x00\x00\x00'),
_DELETE: b('\x04deletes\x00\x00\x00\x00\x00'),
_INSERT: b'\x04documents\x00\x00\x00\x00\x00',
_UPDATE: b'\x04updates\x00\x00\x00\x00\x00',
_DELETE: b'\x04deletes\x00\x00\x00\x00\x00',
}
@ -242,7 +242,7 @@ def _do_batched_insert(collection_name, docs, check_keys,
send_safe), send_safe)
# Exception type could be OperationFailure or a subtype
# (e.g. DuplicateKeyError)
except OperationFailure, exc:
except OperationFailure as exc:
# Like it says, continue on error...
if continue_on_error:
# Store exception details to re-raise after the final batch.
@ -294,7 +294,7 @@ def _do_batched_write_command(namespace, operation, command,
# Save space for message length and request id
buf.write(_ZERO_64)
# responseTo, opCode
buf.write(b("\x00\x00\x00\x00\xd4\x07\x00\x00"))
buf.write(b"\x00\x00\x00\x00\xd4\x07\x00\x00")
# No options
buf.write(_ZERO_32)
# Namespace as C string
@ -380,7 +380,7 @@ def _do_batched_write_command(namespace, operation, command,
buf.truncate()
idx_offset += idx
idx = 0
key = b('0')
key = b'0'
buf.write(_BSONOBJ)
buf.write(key)
buf.write(_ZERO_8)

View File

@ -40,7 +40,11 @@ import threading
import time
import warnings
from bson.py3compat import b
from bson.py3compat import (_unicode,
integer_types,
iteritems,
itervalues,
string_type)
from pymongo import (auth,
common,
database,
@ -59,7 +63,7 @@ from pymongo.errors import (AutoReconnect,
InvalidURI,
OperationFailure)
from pymongo.member import Member
EMPTY = b("")
EMPTY = b""
def _partition_node(node):
@ -227,7 +231,7 @@ class MongoClient(common.BaseObject):
"""
if host is None:
host = self.HOST
if isinstance(host, basestring):
if isinstance(host, string_type):
host = [host]
if port is None:
port = self.PORT
@ -274,7 +278,7 @@ class MongoClient(common.BaseObject):
event_class = kwargs.pop('_event_class', None)
options = {}
for option, value in kwargs.iteritems():
for option, value in iteritems(kwargs):
option, value = common.validate(option, value)
options[option] = value
options.update(opts)
@ -299,7 +303,7 @@ class MongoClient(common.BaseObject):
self.__ssl_cert_reqs = options.get('ssl_cert_reqs', None)
self.__ssl_ca_certs = options.get('ssl_ca_certs', None)
ssl_kwarg_keys = [k for k in kwargs.keys() if k.startswith('ssl_')]
ssl_kwarg_keys = [k for k in kwargs if k.startswith('ssl_')]
if self.__use_ssl == False and ssl_kwarg_keys:
raise ConfigurationError("ssl has not been enabled but the "
"following ssl parameters have been set: "
@ -355,7 +359,7 @@ class MongoClient(common.BaseObject):
if _connect:
try:
self._ensure_connected(True)
except AutoReconnect, e:
except AutoReconnect as e:
# ConnectionFailure makes more sense here than AutoReconnect
raise ConnectionFailure(str(e))
@ -368,12 +372,12 @@ class MongoClient(common.BaseObject):
credentials = auth._build_credentials_tuple(mechanism,
source,
unicode(username),
unicode(password),
_unicode(username),
_unicode(password),
options)
try:
self._cache_credentials(source, credentials, _connect)
except OperationFailure, exc:
except OperationFailure as exc:
raise ConfigurationError(str(exc))
def _cached(self, dbname, coll, index):
@ -479,7 +483,7 @@ class MongoClient(common.BaseObject):
"""Authenticate using cached database credentials.
"""
if self.__auth_credentials or sock_info.authset:
cached = set(self.__auth_credentials.itervalues())
cached = set(itervalues(self.__auth_credentials))
authset = sock_info.authset.copy()
@ -806,7 +810,7 @@ class MongoClient(common.BaseObject):
try:
member, nodes = self.__find_node()
return member
except Exception, e:
except Exception as e:
exc = e
raise
finally:
@ -882,7 +886,7 @@ class MongoClient(common.BaseObject):
# The server is available but something failed, e.g. auth,
# wrong replica set name, or incompatible wire protocol.
raise
except Exception, why:
except Exception as why:
errors.append(str(why))
if len(mongos_candidates):
@ -910,7 +914,7 @@ class MongoClient(common.BaseObject):
connection_pool.start_request()
sock_info = connection_pool.get_socket()
except socket.error, why:
except socket.error as why:
self.disconnect()
# Check if a unix domain socket
@ -1130,7 +1134,7 @@ class MongoClient(common.BaseObject):
return rv
except OperationFailure:
raise
except (ConnectionFailure, socket.error), e:
except (ConnectionFailure, socket.error) as e:
self.disconnect()
raise AutoReconnect(str(e))
except:
@ -1196,7 +1200,7 @@ class MongoClient(common.BaseObject):
try:
response = self.__send_and_receive(message, sock_info)
return (None, (response, sock_info, member.pool))
except (ConnectionFailure, socket.error), e:
except (ConnectionFailure, socket.error) as e:
self.disconnect()
raise AutoReconnect(str(e))
finally:
@ -1315,7 +1319,7 @@ class MongoClient(common.BaseObject):
:Parameters:
- `cursor_id`: id of cursor to close
"""
if not isinstance(cursor_id, (int, long)):
if not isinstance(cursor_id, integer_types):
raise TypeError("cursor_id must be an instance of (int, long)")
self.__cursor_manager.close(cursor_id)
@ -1360,9 +1364,9 @@ class MongoClient(common.BaseObject):
if isinstance(name, database.Database):
name = name.name
if not isinstance(name, basestring):
raise TypeError("name_or_database must be an instance of "
"%s or Database" % (basestring.__name__,))
if not isinstance(name, string_type):
raise TypeError("name_or_database must be an instance "
"of %s or a Database" % (string_type.__name__,))
self._purge_index(name)
self[name].command("dropDatabase")
@ -1394,12 +1398,12 @@ class MongoClient(common.BaseObject):
.. versionadded:: 1.5
"""
if not isinstance(from_name, basestring):
raise TypeError("from_name must be an instance "
"of %s" % (basestring.__name__,))
if not isinstance(to_name, basestring):
raise TypeError("to_name must be an instance "
"of %s" % (basestring.__name__,))
if not isinstance(from_name, string_type):
raise TypeError("from_name must be an "
"instance of %s" % (string_type.__name__,))
if not isinstance(to_name, string_type):
raise TypeError("to_name must be an "
"instance of %s" % (string_type.__name__,))
database._check_name(to_name)
@ -1487,3 +1491,5 @@ class MongoClient(common.BaseObject):
def next(self):
raise TypeError("'MongoClient' object is not iterable")
__next__ = next

View File

@ -39,7 +39,11 @@ import threading
import time
import weakref
from bson.py3compat import b
from bson.py3compat import (_unicode,
integer_types,
iteritems,
itervalues,
string_type)
from pymongo import (auth,
common,
database,
@ -60,7 +64,7 @@ from pymongo.errors import (AutoReconnect,
InvalidOperation)
from pymongo.thread_util import DummyLock
EMPTY = b("")
EMPTY = b""
MAX_RETRY = 3
MONITORS = set()
@ -292,7 +296,8 @@ class RSState(object):
def __str__(self):
return '<RSState [%s] writer="%s">' % (
', '.join(str(member) for member in self._host_to_member.itervalues()),
', '.join(str(member)
for member in itervalues(self._host_to_member)),
self.writer and '%s:%s' % self.writer or None)
@ -610,7 +615,7 @@ class MongoReplicaSetClient(common.BaseObject):
self.pool_class = kwargs.pop('_pool_class', pool.Pool)
self.__monitor_class = kwargs.pop('_monitor_class', None)
for option, value in kwargs.iteritems():
for option, value in iteritems(kwargs):
option, value = common.validate(option, value)
self.__opts[option] = value
self.__opts.update(options)
@ -645,7 +650,7 @@ class MongoReplicaSetClient(common.BaseObject):
self.__ssl_cert_reqs = self.__opts.get('ssl_cert_reqs', None)
self.__ssl_ca_certs = self.__opts.get('ssl_ca_certs', None)
ssl_kwarg_keys = [k for k in kwargs.keys() if k.startswith('ssl_')]
ssl_kwarg_keys = [k for k in kwargs if k.startswith('ssl_')]
if self.__use_ssl is False and ssl_kwarg_keys:
raise ConfigurationError("ssl has not been enabled but the "
"following ssl parameters have been set: "
@ -673,7 +678,7 @@ class MongoReplicaSetClient(common.BaseObject):
if _connect:
try:
self.refresh(initial=True)
except AutoReconnect, e:
except AutoReconnect as e:
# ConnectionFailure makes more sense here than AutoReconnect
raise ConnectionFailure(str(e))
@ -686,12 +691,12 @@ class MongoReplicaSetClient(common.BaseObject):
credentials = auth._build_credentials_tuple(mechanism,
source,
unicode(username),
unicode(password),
_unicode(username),
_unicode(password),
options)
try:
self._cache_credentials(source, credentials, _connect)
except OperationFailure, exc:
except OperationFailure as exc:
raise ConfigurationError(str(exc))
# Start the monitor after we know the configuration is correct.
@ -813,7 +818,7 @@ class MongoReplicaSetClient(common.BaseObject):
"""Authenticate using cached database credentials.
"""
if self.__auth_credentials or sock_info.authset:
cached = set(self.__auth_credentials.itervalues())
cached = set(itervalues(self.__auth_credentials))
authset = sock_info.authset.copy()
@ -1104,7 +1109,7 @@ class MongoReplicaSetClient(common.BaseObject):
rs_state = self.__rs_state
try:
self.__rs_state = self.__create_rs_state(rs_state, initial)
except ConfigurationError, e:
except ConfigurationError as e:
self.__rs_state = rs_state.clone_with_error(e)
raise
@ -1170,7 +1175,7 @@ class MongoReplicaSetClient(common.BaseObject):
if response['ismaster']:
writer = node
except (ConnectionFailure, socket.error), why:
except (ConnectionFailure, socket.error) as why:
if member:
member.pool.discard_socket(sock_info)
errors.append("%s:%d: %s" % (node[0], node[1], str(why)))
@ -1242,7 +1247,7 @@ class MongoReplicaSetClient(common.BaseObject):
if writer:
response = members[writer].ismaster_response
elif members:
response = members.values()[0].ismaster_response
response = next(itervalues(members)).ismaster_response
else:
response = {}
@ -1509,7 +1514,7 @@ class MongoReplicaSetClient(common.BaseObject):
return rv
except OperationFailure:
raise
except(ConnectionFailure, socket.error), why:
except(ConnectionFailure, socket.error) as why:
member.pool.discard_socket(sock_info)
if _connection_to_use in (None, -1):
self.disconnect()
@ -1549,11 +1554,11 @@ class MongoReplicaSetClient(common.BaseObject):
"""
try:
return self.__send_and_receive(member, msg, **kwargs)
except socket.timeout, e:
except socket.timeout as e:
# Could be one slow query, don't refresh.
host, port = member.host
raise AutoReconnect("%s:%d: %s" % (host, port, e))
except (socket.error, ConnectionFailure), why:
except (socket.error, ConnectionFailure) as why:
# Try to replace our RSState with a clone where this member is
# marked "down", to reduce exceptions on other threads, or repeated
# exceptions on this thread. We accept that there's a race
@ -1629,7 +1634,7 @@ class MongoReplicaSetClient(common.BaseObject):
return (
pinned_member.host,
self.__try_read(pinned_member, msg, **kwargs))
except AutoReconnect, why:
except AutoReconnect as why:
if pref == ReadPreference.PRIMARY:
self.disconnect()
raise
@ -1661,7 +1666,7 @@ class MongoReplicaSetClient(common.BaseObject):
# unless read preference changes
rs_state.pin_host(member.host, pref)
return member.host, response
except AutoReconnect, why:
except AutoReconnect as why:
if pref == ReadPreference.PRIMARY:
raise
@ -1801,7 +1806,7 @@ class MongoReplicaSetClient(common.BaseObject):
:Parameters:
- `cursor_id`: id of cursor to close
"""
if not isinstance(cursor_id, (int, long)):
if not isinstance(cursor_id, integer_types):
raise TypeError("cursor_id must be an instance of (int, long)")
self._send_message(message.kill_cursors([cursor_id]),
@ -1833,9 +1838,9 @@ class MongoReplicaSetClient(common.BaseObject):
if isinstance(name, database.Database):
name = name.name
if not isinstance(name, basestring):
raise TypeError("name_or_database must be an instance of "
"%s or Database" % (basestring.__name__,))
if not isinstance(name, string_type):
raise TypeError("name_or_database must be an instance "
"of %s or a Database" % (string_type.__name__,))
self._purge_index(name)
self[name].command("dropDatabase")
@ -1865,12 +1870,12 @@ class MongoReplicaSetClient(common.BaseObject):
.. note:: Specifying `username` and `password` requires server
version **>= 1.3.3+**.
"""
if not isinstance(from_name, basestring):
raise TypeError("from_name must be an instance "
"of %s" % (basestring.__name__,))
if not isinstance(to_name, basestring):
raise TypeError("to_name must be an instance "
"of %s" % (basestring.__name__,))
if not isinstance(from_name, string_type):
raise TypeError("from_name must be an "
"instance of %s" % (string_type.__name__,))
if not isinstance(to_name, string_type):
raise TypeError("to_name must be an "
"instance of %s" % (string_type.__name__,))
database._check_name(to_name)

View File

@ -221,7 +221,7 @@ class Pool:
try:
sock.connect(host)
return sock
except socket.error, e:
except socket.error as e:
if sock is not None:
sock.close()
raise e
@ -243,7 +243,7 @@ class Pool:
sock.settimeout(self.conn_timeout or 20.0)
sock.connect(sa)
return sock
except socket.error, e:
except socket.error as e:
err = e
if sock is not None:
sock.close()

View File

@ -15,7 +15,12 @@
"""Tools to parse and validate a MongoDB URI."""
from urllib import unquote_plus
from bson.py3compat import PY3, iteritems, string_type
if PY3:
from urllib.parse import unquote_plus
else:
from urllib import unquote_plus
from pymongo.common import validate
from pymongo.errors import (ConfigurationError,
@ -133,7 +138,7 @@ def parse_host(entity, default_port=DEFAULT_PORT):
"address literal must be enclosed in '[' "
"and ']' according to RFC 2732.")
host, port = host.split(':', 1)
if isinstance(port, basestring):
if isinstance(port, string_type):
if not port.isdigit():
raise ConfigurationError("Port number must be an integer.")
port = int(port)
@ -149,7 +154,7 @@ def validate_options(opts):
- `opts`: A dict of MongoDB URI options.
"""
normalized = {}
for option, value in opts.iteritems():
for option, value in iteritems(opts):
option, value = validate(option, value)
# str(option) to ensure that a unicode URI results in plain 'str'
# option names. 'normalized' is then suitable to be passed as kwargs
@ -322,6 +327,6 @@ if __name__ == '__main__':
import sys
try:
pprint.pprint(parse_uri(sys.argv[1]))
except (InvalidURI, UnsupportedOption), e:
print e
except (InvalidURI, UnsupportedOption) as e:
print(e)
sys.exit(0)