PYTHON-2133 Fix up docs
And finish deleting python 2 specific code.
This commit is contained in:
parent
c65b89d8a1
commit
521f7b9af4
@ -19,7 +19,7 @@ that might not be of interest or that has already been addressed.
|
||||
Supported Interpreters
|
||||
----------------------
|
||||
|
||||
PyMongo supports CPython 2.7, 3.4+, PyPy, and PyPy3.5+. Language
|
||||
PyMongo supports CPython 3.4+ and PyPy3.5+. Language
|
||||
features not supported by all interpreters can not be used.
|
||||
|
||||
Style Guide
|
||||
|
||||
@ -160,9 +160,9 @@ Here's a basic example (for more see the *examples* section of the docs):
|
||||
>>> client = pymongo.MongoClient("localhost", 27017)
|
||||
>>> db = client.test
|
||||
>>> db.name
|
||||
u'test'
|
||||
'test'
|
||||
>>> db.my_collection
|
||||
Collection(Database(MongoClient('localhost', 27017), u'test'), u'my_collection')
|
||||
Collection(Database(MongoClient('localhost', 27017), 'test'), 'my_collection')
|
||||
>>> db.my_collection.insert_one({"x": 10}).inserted_id
|
||||
ObjectId('4aba15ebe23f6b53b0000000')
|
||||
>>> db.my_collection.insert_one({"x": 8}).inserted_id
|
||||
@ -170,7 +170,7 @@ Here's a basic example (for more see the *examples* section of the docs):
|
||||
>>> db.my_collection.insert_one({"x": 11}).inserted_id
|
||||
ObjectId('4aba160ee23f6b543e000002')
|
||||
>>> db.my_collection.find_one()
|
||||
{u'x': 10, u'_id': ObjectId('4aba15ebe23f6b53b0000000')}
|
||||
{'x': 10, '_id': ObjectId('4aba15ebe23f6b53b0000000')}
|
||||
>>> for item in db.my_collection.find():
|
||||
... print(item["x"])
|
||||
...
|
||||
@ -178,7 +178,7 @@ Here's a basic example (for more see the *examples* section of the docs):
|
||||
8
|
||||
11
|
||||
>>> db.my_collection.create_index("x")
|
||||
u'x_1'
|
||||
'x_1'
|
||||
>>> for item in db.my_collection.find().sort("x", pymongo.ASCENDING):
|
||||
... print(item["x"])
|
||||
...
|
||||
|
||||
@ -22,11 +22,9 @@ Python Type BSON Type Supported Direction
|
||||
None null both
|
||||
bool boolean both
|
||||
int [#int]_ int32 / int64 py -> bson
|
||||
long int64 py -> bson
|
||||
`bson.int64.Int64` int64 both
|
||||
float number (real) both
|
||||
string string py -> bson
|
||||
unicode string both
|
||||
str string both
|
||||
list array both
|
||||
dict / `SON` object both
|
||||
datetime.datetime [#dt]_ [#dt2]_ date both
|
||||
@ -36,17 +34,11 @@ compiled re [#re]_ regex py -> bson
|
||||
`bson.objectid.ObjectId` oid both
|
||||
`bson.dbref.DBRef` dbref both
|
||||
None undefined bson -> py
|
||||
unicode code bson -> py
|
||||
`bson.code.Code` code py -> bson
|
||||
unicode symbol bson -> py
|
||||
bytes (Python 3) [#bytes]_ binary both
|
||||
`bson.code.Code` code both
|
||||
str symbol bson -> py
|
||||
bytes [#bytes]_ binary both
|
||||
======================================= ============= ===================
|
||||
|
||||
Note that, when using Python 2.x, to save binary data it must be wrapped as
|
||||
an instance of `bson.binary.Binary`. Otherwise it will be saved as a BSON
|
||||
string and retrieved as unicode. Users of Python 3.x can use the Python bytes
|
||||
type.
|
||||
|
||||
.. [#int] A Python int will be saved as a BSON int32 or BSON int64 depending
|
||||
on its size. A BSON int32 will always decode to a Python int. A BSON
|
||||
int64 will always decode to a :class:`~bson.int64.Int64`.
|
||||
@ -58,10 +50,8 @@ type.
|
||||
objects from ``re.compile()`` are both saved as BSON regular expressions.
|
||||
BSON regular expressions are decoded as :class:`~bson.regex.Regex`
|
||||
instances.
|
||||
.. [#bytes] The bytes type from Python 3.x is encoded as BSON binary with
|
||||
subtype 0. In Python 3.x it will be decoded back to bytes. In Python 2.x
|
||||
it will be decoded to an instance of :class:`~bson.binary.Binary` with
|
||||
subtype 0.
|
||||
.. [#bytes] The bytes type is encoded as BSON binary with
|
||||
subtype 0. It will be decoded back to bytes.
|
||||
"""
|
||||
|
||||
import calendar
|
||||
@ -161,7 +151,7 @@ def _get_int(data, view, position, dummy0, dummy1, dummy2):
|
||||
|
||||
|
||||
def _get_c_string(data, view, position, opts):
|
||||
"""Decode a BSON 'C' string to python unicode string."""
|
||||
"""Decode a BSON 'C' string to python str."""
|
||||
end = data.index(b"\x00", position)
|
||||
return _utf_8_decode(view[position:end],
|
||||
opts.unicode_decode_error_handler, True)[0], end + 1
|
||||
@ -173,7 +163,7 @@ def _get_float(data, view, position, dummy0, dummy1, dummy2):
|
||||
|
||||
|
||||
def _get_string(data, view, position, obj_end, opts, dummy):
|
||||
"""Decode a BSON string to python unicode string."""
|
||||
"""Decode a BSON string to python str."""
|
||||
length = _UNPACK_INT_FROM(data, position)[0]
|
||||
position += 4
|
||||
if length < 1 or obj_end - position < length:
|
||||
@ -573,7 +563,7 @@ def _encode_list(name, value, check_keys, opts):
|
||||
|
||||
|
||||
def _encode_text(name, value, dummy0, dummy1):
|
||||
"""Encode a python unicode (python 2.x) / str (python 3.x)."""
|
||||
"""Encode a python str."""
|
||||
value = _utf_8_encode(value)[0]
|
||||
return b"\x02" + name + _PACK_INT(len(value) + 1) + value + b"\x00"
|
||||
|
||||
@ -616,12 +606,11 @@ def _encode_none(name, dummy0, dummy1, dummy2):
|
||||
def _encode_regex(name, value, dummy0, dummy1):
|
||||
"""Encode a python regex or bson.regex.Regex."""
|
||||
flags = value.flags
|
||||
# Python 2 common case
|
||||
if flags == 0:
|
||||
return b"\x0B" + name + _make_c_string_check(value.pattern) + b"\x00"
|
||||
# Python 3 common case
|
||||
elif flags == re.UNICODE:
|
||||
if flags == re.UNICODE:
|
||||
return b"\x0B" + name + _make_c_string_check(value.pattern) + b"u\x00"
|
||||
elif flags == 0:
|
||||
return b"\x0B" + name + _make_c_string_check(value.pattern) + b"\x00"
|
||||
else:
|
||||
sflags = b""
|
||||
if flags & re.IGNORECASE:
|
||||
|
||||
@ -57,7 +57,7 @@ class DBRef(object):
|
||||
|
||||
@property
|
||||
def collection(self):
|
||||
"""Get the name of this DBRef's collection as unicode.
|
||||
"""Get the name of this DBRef's collection.
|
||||
"""
|
||||
return self.__collection
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ Example usage (deserialization):
|
||||
|
||||
>>> from bson.json_util import loads
|
||||
>>> loads('[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$scope": {}, "$code": "function x() { return 1; }"}}, {"bin": {"$type": "80", "$binary": "AQIDBA=="}}]')
|
||||
[{u'foo': [1, 2]}, {u'bar': {u'hello': u'world'}}, {u'code': Code('function x() { return 1; }', {})}, {u'bin': Binary('...', 128)}]
|
||||
[{'foo': [1, 2]}, {'bar': {'hello': 'world'}}, {'code': Code('function x() { return 1; }', {})}, {'bin': Binary(b'...', 128)}]
|
||||
|
||||
Example usage (serialization):
|
||||
|
||||
@ -855,7 +855,6 @@ def default(obj, json_options=DEFAULT_JSON_OPTIONS):
|
||||
return {'$numberDouble': representation}
|
||||
elif json_options.json_mode == JSONMode.CANONICAL:
|
||||
# repr() will return the shortest string guaranteed to produce the
|
||||
# original value, when float() is called on it. str produces a
|
||||
# shorter string in Python 2.
|
||||
# original value, when float() is called on it.
|
||||
return {'$numberDouble': str(repr(obj))}
|
||||
raise TypeError("%r is not JSON serializable" % obj)
|
||||
|
||||
@ -70,7 +70,7 @@ class ObjectId(object):
|
||||
|
||||
By default, ``ObjectId()`` creates a new unique identifier. The
|
||||
optional parameter `oid` can be an :class:`ObjectId`, or any 12
|
||||
:class:`bytes` or, in Python 2, any 12-character :class:`str`.
|
||||
:class:`bytes`.
|
||||
|
||||
For example, the 12 bytes b'foo-bar-quux' do not follow the ObjectId
|
||||
specification but they are acceptable input::
|
||||
@ -78,14 +78,10 @@ class ObjectId(object):
|
||||
>>> ObjectId(b'foo-bar-quux')
|
||||
ObjectId('666f6f2d6261722d71757578')
|
||||
|
||||
`oid` can also be a :class:`unicode` or :class:`str` of 24 hex digits::
|
||||
`oid` can also be a :class:`str` of 24 hex digits::
|
||||
|
||||
>>> ObjectId('0123456789ab0123456789ab')
|
||||
ObjectId('0123456789ab0123456789ab')
|
||||
>>>
|
||||
>>> # A u-prefixed unicode literal:
|
||||
>>> ObjectId(u'0123456789ab0123456789ab')
|
||||
ObjectId('0123456789ab0123456789ab')
|
||||
|
||||
Raises :class:`~bson.errors.InvalidId` if `oid` is not 12 bytes nor
|
||||
24 hex digits, or :class:`TypeError` if `oid` is not an accepted type.
|
||||
@ -201,7 +197,6 @@ class ObjectId(object):
|
||||
"""
|
||||
if isinstance(oid, ObjectId):
|
||||
self.__id = oid.binary
|
||||
# bytes or unicode in python 2, str in python 3
|
||||
elif isinstance(oid, str):
|
||||
if len(oid) == 24:
|
||||
try:
|
||||
|
||||
@ -33,7 +33,7 @@ class SON(dict):
|
||||
|
||||
A subclass of dict that maintains ordering of keys and provides a
|
||||
few extra niceties for dealing with SON. SON provides an API
|
||||
similar to collections.OrderedDict from Python 2.7+.
|
||||
similar to collections.OrderedDict.
|
||||
"""
|
||||
|
||||
def __init__(self, data=None, **kwargs):
|
||||
|
||||
@ -27,8 +27,8 @@ source_suffix = '.rst'
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'PyMongo'
|
||||
copyright = u'MongoDB, Inc. 2008-present. MongoDB, Mongo, and the leaf logo are registered trademarks of MongoDB, Inc'
|
||||
project = 'PyMongo'
|
||||
copyright = 'MongoDB, Inc. 2008-present. MongoDB, Mongo, and the leaf logo are registered trademarks of MongoDB, Inc'
|
||||
html_show_sphinx = False
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
@ -152,8 +152,8 @@ htmlhelp_basename = 'PyMongo' + release.replace('.', '_')
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'PyMongo.tex', u'PyMongo Documentation',
|
||||
u'Michael Dirolf', 'manual'),
|
||||
('index', 'PyMongo.tex', 'PyMongo Documentation',
|
||||
'Michael Dirolf', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
|
||||
@ -58,15 +58,15 @@ eg "$sort":
|
||||
... ]
|
||||
>>> import pprint
|
||||
>>> pprint.pprint(list(db.things.aggregate(pipeline)))
|
||||
[{u'_id': u'cat', u'count': 3},
|
||||
{u'_id': u'dog', u'count': 2},
|
||||
{u'_id': u'mouse', u'count': 1}]
|
||||
[{'_id': 'cat', 'count': 3},
|
||||
{'_id': 'dog', 'count': 2},
|
||||
{'_id': 'mouse', 'count': 1}]
|
||||
|
||||
To run an explain plan for this aggregation use the
|
||||
:meth:`~pymongo.database.Database.command` method::
|
||||
|
||||
>>> db.command('aggregate', 'things', pipeline=pipeline, explain=True)
|
||||
{u'ok': 1.0, u'stages': [...]}
|
||||
{'ok': 1.0, 'stages': [...]}
|
||||
|
||||
As well as simple aggregations the aggregation framework provides projection
|
||||
capabilities to reshape the returned data. Using projections and aggregation,
|
||||
@ -123,9 +123,9 @@ iterate over the result collection:
|
||||
>>> for doc in result.find().sort("_id"):
|
||||
... pprint.pprint(doc)
|
||||
...
|
||||
{u'_id': u'cat', u'value': 3.0}
|
||||
{u'_id': u'dog', u'value': 2.0}
|
||||
{u'_id': u'mouse', u'value': 1.0}
|
||||
{'_id': 'cat', 'value': 3.0}
|
||||
{'_id': 'dog', 'value': 2.0}
|
||||
{'_id': 'mouse', 'value': 1.0}
|
||||
|
||||
Advanced Map/Reduce
|
||||
-------------------
|
||||
@ -140,7 +140,7 @@ response to the map/reduce command, rather than just the result collection:
|
||||
|
||||
>>> pprint.pprint(
|
||||
... db.things.map_reduce(mapper, reducer, "myresults", full_response=True))
|
||||
{...u'ok': 1.0,... u'result': u'myresults'...}
|
||||
{...'ok': 1.0,... 'result': 'myresults'...}
|
||||
|
||||
All of the optional map/reduce parameters are also supported, simply pass them
|
||||
as keyword arguments. In this example we use the `query` parameter to limit the
|
||||
@ -153,8 +153,8 @@ documents that will be mapped over:
|
||||
>>> for doc in results.find().sort("_id"):
|
||||
... pprint.pprint(doc)
|
||||
...
|
||||
{u'_id': u'cat', u'value': 1.0}
|
||||
{u'_id': u'dog', u'value': 1.0}
|
||||
{'_id': 'cat', 'value': 1.0}
|
||||
{'_id': 'dog', 'value': 1.0}
|
||||
|
||||
You can use :class:`~bson.son.SON` or :class:`collections.OrderedDict` to
|
||||
specify a different database to store the result collection:
|
||||
@ -168,6 +168,6 @@ specify a different database to store the result collection:
|
||||
... reducer,
|
||||
... out=SON([("replace", "results"), ("db", "outdb")]),
|
||||
... full_response=True))
|
||||
{...u'ok': 1.0,... u'result': {u'collection': u'results', u'db': u'outdb'}...}
|
||||
{...'ok': 1.0,... 'result': {'collection': 'results', 'db': 'outdb'}...}
|
||||
|
||||
.. seealso:: The full list of options for MongoDB's `map reduce engine <http://www.mongodb.org/display/DOCS/MapReduce>`_
|
||||
|
||||
@ -11,8 +11,7 @@ Percent-Escaping Username and Password
|
||||
--------------------------------------
|
||||
|
||||
Username and password must be percent-escaped with
|
||||
:meth:`urllib.parse.quote_plus` in Python 3, or :meth:`urllib.quote_plus` in
|
||||
Python 2, to be used in a MongoDB URI. For example, in Python 3::
|
||||
:meth:`urllib.parse.quote_plus`, to be used in a MongoDB URI. For example::
|
||||
|
||||
>>> from pymongo import MongoClient
|
||||
>>> import urllib.parse
|
||||
|
||||
@ -70,7 +70,7 @@ of operations performed.
|
||||
'nModified': 2,
|
||||
'nRemoved': 10000,
|
||||
'nUpserted': 1,
|
||||
'upserted': [{u'_id': 4, u'index': 5}],
|
||||
'upserted': [{'_id': 4, 'index': 5}],
|
||||
'writeConcernErrors': [],
|
||||
'writeErrors': []}
|
||||
|
||||
@ -107,10 +107,10 @@ the failure.
|
||||
'nUpserted': 0,
|
||||
'upserted': [],
|
||||
'writeConcernErrors': [],
|
||||
'writeErrors': [{u'code': 11000,
|
||||
u'errmsg': u'...E11000...duplicate key error...',
|
||||
u'index': 1,...
|
||||
u'op': {'_id': 4}}]}
|
||||
'writeErrors': [{'code': 11000,
|
||||
'errmsg': '...E11000...duplicate key error...',
|
||||
'index': 1,...
|
||||
'op': {'_id': 4}}]}
|
||||
|
||||
.. _unordered_bulk:
|
||||
|
||||
@ -145,14 +145,14 @@ and fourth operations succeed.
|
||||
'nUpserted': 0,
|
||||
'upserted': [],
|
||||
'writeConcernErrors': [],
|
||||
'writeErrors': [{u'code': 11000,
|
||||
u'errmsg': u'...E11000...duplicate key error...',
|
||||
u'index': 0,...
|
||||
u'op': {'_id': 1}},
|
||||
{u'code': 11000,
|
||||
u'errmsg': u'...E11000...duplicate key error...',
|
||||
u'index': 2,...
|
||||
u'op': {'_id': 3}}]}
|
||||
'writeErrors': [{'code': 11000,
|
||||
'errmsg': '...E11000...duplicate key error...',
|
||||
'index': 0,...
|
||||
'op': {'_id': 1}},
|
||||
{'code': 11000,
|
||||
'errmsg': '...E11000...duplicate key error...',
|
||||
'index': 2,...
|
||||
'op': {'_id': 3}}]}
|
||||
|
||||
Write Concern
|
||||
.............
|
||||
@ -177,7 +177,7 @@ after all operations are attempted, regardless of execution order.
|
||||
'nRemoved': 0,
|
||||
'nUpserted': 0,
|
||||
'upserted': [],
|
||||
'writeConcernErrors': [{u'code': 64...
|
||||
u'errInfo': {u'wtimeout': True},
|
||||
u'errmsg': u'waiting for replication timed out'}],
|
||||
'writeConcernErrors': [{'code': 64...
|
||||
'errInfo': {'wtimeout': True},
|
||||
'errmsg': 'waiting for replication timed out'}],
|
||||
'writeErrors': []}
|
||||
|
||||
@ -138,7 +138,7 @@ Now, we can seamlessly encode and decode instances of
|
||||
>>> mydoc = collection.find_one()
|
||||
>>> import pprint
|
||||
>>> pprint.pprint(mydoc)
|
||||
{u'_id': ObjectId('...'), u'num': Decimal('45.321')}
|
||||
{'_id': ObjectId('...'), 'num': Decimal('45.321')}
|
||||
|
||||
|
||||
We can see what's actually being saved to the database by creating a fresh
|
||||
@ -149,7 +149,7 @@ MongoDB:
|
||||
|
||||
>>> vanilla_collection = db.get_collection('test')
|
||||
>>> pprint.pprint(vanilla_collection.find_one())
|
||||
{u'_id': ObjectId('...'), u'num': Decimal128('45.321')}
|
||||
{'_id': ObjectId('...'), 'num': Decimal128('45.321')}
|
||||
|
||||
|
||||
Encoding Subtypes
|
||||
@ -217,7 +217,7 @@ object, we can seamlessly encode instances of ``DecimalInt``:
|
||||
<pymongo.results.InsertOneResult object at ...>
|
||||
>>> mydoc = collection.find_one()
|
||||
>>> pprint.pprint(mydoc)
|
||||
{u'_id': ObjectId('...'), u'num': Decimal('45.321')}
|
||||
{'_id': ObjectId('...'), 'num': Decimal('45.321')}
|
||||
|
||||
Note that the ``transform_bson`` method of the base codec class results in
|
||||
these values being decoded as ``Decimal`` (and not ``DecimalInt``).
|
||||
@ -310,7 +310,7 @@ We can now seamlessly encode instances of :py:class:`~decimal.Decimal`:
|
||||
<pymongo.results.InsertOneResult object at ...>
|
||||
>>> mydoc = collection.find_one()
|
||||
>>> pprint.pprint(mydoc)
|
||||
{u'_id': ObjectId('...'), u'num': Decimal128('45.321')}
|
||||
{'_id': ObjectId('...'), 'num': Decimal128('45.321')}
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
@ -22,7 +22,7 @@ Creating a geospatial index in pymongo is easy:
|
||||
>>> from pymongo import MongoClient, GEO2D
|
||||
>>> db = MongoClient().geo_example
|
||||
>>> db.places.create_index([("loc", GEO2D)])
|
||||
u'loc_2d'
|
||||
'loc_2d'
|
||||
|
||||
Inserting Places
|
||||
----------------
|
||||
@ -53,9 +53,9 @@ Using the geospatial index we can find documents near another point:
|
||||
>>> for doc in db.places.find({"loc": {"$near": [3, 6]}}).limit(3):
|
||||
... pprint.pprint(doc)
|
||||
...
|
||||
{u'_id': ObjectId('...'), u'loc': [2, 5]}
|
||||
{u'_id': ObjectId('...'), u'loc': [4, 4]}
|
||||
{u'_id': ObjectId('...'), u'loc': [1, 2]}
|
||||
{'_id': ObjectId('...'), 'loc': [2, 5]}
|
||||
{'_id': ObjectId('...'), 'loc': [4, 4]}
|
||||
{'_id': ObjectId('...'), 'loc': [1, 2]}
|
||||
|
||||
.. note:: If using :data:`pymongo.GEOSPHERE`, using $nearSphere is recommended.
|
||||
|
||||
@ -68,9 +68,9 @@ The $maxDistance operator requires the use of :class:`~bson.son.SON`:
|
||||
>>> for doc in db.places.find(query).limit(3):
|
||||
... pprint.pprint(doc)
|
||||
...
|
||||
{u'_id': ObjectId('...'), u'loc': [2, 5]}
|
||||
{u'_id': ObjectId('...'), u'loc': [4, 4]}
|
||||
{u'_id': ObjectId('...'), u'loc': [1, 2]}
|
||||
{'_id': ObjectId('...'), 'loc': [2, 5]}
|
||||
{'_id': ObjectId('...'), 'loc': [4, 4]}
|
||||
{'_id': ObjectId('...'), 'loc': [1, 2]}
|
||||
|
||||
It's also possible to query for all items within a given rectangle
|
||||
(specified by lower-left and upper-right coordinates):
|
||||
@ -80,8 +80,8 @@ It's also possible to query for all items within a given rectangle
|
||||
>>> query = {"loc": {"$within": {"$box": [[2, 2], [5, 6]]}}}
|
||||
>>> for doc in db.places.find(query).sort('_id'):
|
||||
... pprint.pprint(doc)
|
||||
{u'_id': ObjectId('...'), u'loc': [2, 5]}
|
||||
{u'_id': ObjectId('...'), u'loc': [4, 4]}
|
||||
{'_id': ObjectId('...'), 'loc': [2, 5]}
|
||||
{'_id': ObjectId('...'), 'loc': [4, 4]}
|
||||
|
||||
Or circle (specified by center point and radius):
|
||||
|
||||
@ -91,15 +91,15 @@ Or circle (specified by center point and radius):
|
||||
>>> for doc in db.places.find(query).sort('_id'):
|
||||
... pprint.pprint(doc)
|
||||
...
|
||||
{u'_id': ObjectId('...'), u'loc': [2, 5]}
|
||||
{u'_id': ObjectId('...'), u'loc': [1, 2]}
|
||||
{u'_id': ObjectId('...'), u'loc': [4, 4]}
|
||||
{'_id': ObjectId('...'), 'loc': [2, 5]}
|
||||
{'_id': ObjectId('...'), 'loc': [1, 2]}
|
||||
{'_id': ObjectId('...'), 'loc': [4, 4]}
|
||||
|
||||
geoNear queries are also supported using :class:`~bson.son.SON`::
|
||||
|
||||
>>> from bson.son import SON
|
||||
>>> db.command(SON([('geoNear', 'places'), ('near', [1, 2])]))
|
||||
{u'ok': 1.0, u'stats': ...}
|
||||
{'ok': 1.0, 'stats': ...}
|
||||
|
||||
.. warning:: Starting in MongoDB version 4.0, MongoDB deprecates the **geoNear** command. Use one of the following operations instead.
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ file:
|
||||
.. doctest::
|
||||
|
||||
>>> fs.get(a).read()
|
||||
'hello world'
|
||||
b'hello world'
|
||||
|
||||
:meth:`~gridfs.GridFS.get` returns a file-like object, so we get the
|
||||
file's contents by calling :meth:`~gridfs.grid_file.GridOut.read`.
|
||||
@ -68,11 +68,11 @@ keyword arguments:
|
||||
>>> b = fs.put(fs.get(a), filename="foo", bar="baz")
|
||||
>>> out = fs.get(b)
|
||||
>>> out.read()
|
||||
'hello world'
|
||||
b'hello world'
|
||||
>>> out.filename
|
||||
u'foo'
|
||||
'foo'
|
||||
>>> out.bar
|
||||
u'baz'
|
||||
'baz'
|
||||
>>> out.upload_date
|
||||
datetime.datetime(...)
|
||||
|
||||
|
||||
@ -111,7 +111,7 @@ set::
|
||||
>>> from time import sleep
|
||||
>>> c = MongoClient(replicaset='foo'); print(c.nodes); sleep(0.1); print(c.nodes)
|
||||
frozenset([])
|
||||
frozenset([(u'localhost', 27019), (u'localhost', 27017), (u'localhost', 27018)])
|
||||
frozenset([('localhost', 27019), ('localhost', 27017), ('localhost', 27018)])
|
||||
|
||||
You need not wait for replica set discovery in your application, however.
|
||||
If you need to do any operation with a MongoClient, such as a
|
||||
@ -132,7 +132,7 @@ connect to the replica set and perform a couple of basic operations::
|
||||
>>> db.test.insert_one({"x": 1}).inserted_id
|
||||
ObjectId('...')
|
||||
>>> db.test.find_one()
|
||||
{u'x': 1, u'_id': ObjectId('...')}
|
||||
{'x': 1, '_id': ObjectId('...')}
|
||||
|
||||
By checking the host and port, we can see that we're connected to
|
||||
*localhost:27017*, which is the current primary::
|
||||
@ -162,7 +162,7 @@ general). At that point the driver will connect to the new primary and
|
||||
the operation will succeed::
|
||||
|
||||
>>> db.test.find_one()
|
||||
{u'x': 1, u'_id': ObjectId('...')}
|
||||
{'x': 1, '_id': ObjectId('...')}
|
||||
>>> db.client.address
|
||||
('localhost', 27018)
|
||||
|
||||
|
||||
@ -16,30 +16,6 @@ command::
|
||||
|
||||
$ python -m pip install pymongo[tls]
|
||||
|
||||
Starting with PyMongo 3.11 this installs `PyOpenSSL
|
||||
<https://pypi.org/project/pyOpenSSL/>`_, `requests`_
|
||||
and `service_identity
|
||||
<https://pypi.org/project/service_identity/>`_
|
||||
for users of Python versions older than 2.7.9. PyOpenSSL supports SNI for these
|
||||
old Python versions allowing applictions to connect to Altas free and shared
|
||||
tier instances.
|
||||
|
||||
Earlier versions of PyMongo require you to manually install the dependencies
|
||||
listed below.
|
||||
|
||||
Python 2.x
|
||||
``````````
|
||||
The `ipaddress`_ module is required on all platforms.
|
||||
|
||||
When using CPython < 2.7.9 or PyPy < 2.5.1:
|
||||
|
||||
- On Windows, the `wincertstore`_ module is required.
|
||||
- On all other platforms, the `certifi`_ module is required.
|
||||
|
||||
.. _ipaddress: https://pypi.python.org/pypi/ipaddress
|
||||
.. _wincertstore: https://pypi.python.org/pypi/wincertstore
|
||||
.. _certifi: https://pypi.python.org/pypi/certifi
|
||||
|
||||
.. warning:: Industry best practices recommend, and some regulations require,
|
||||
the use of TLS 1.1 or newer. Though no application changes are required for
|
||||
PyMongo to make use of the newest protocols, some operating systems or
|
||||
@ -128,8 +104,7 @@ Or, in the URI::
|
||||
Specifying a certificate revocation list
|
||||
........................................
|
||||
|
||||
Python 2.7.9+ (pypy 2.5.1+) and 3.4+ provide support for certificate revocation
|
||||
lists. The ``tlsCRLFile`` option takes a path to a CRL file. It can be passed
|
||||
The ``tlsCRLFile`` option takes a path to a CRL file. It can be passed
|
||||
as a keyword argument::
|
||||
|
||||
>>> client = pymongo.MongoClient('example.com',
|
||||
@ -161,9 +136,8 @@ the ``ssl_keyfile`` option::
|
||||
... tlsCertificateKeyFile='/path/to/client.pem',
|
||||
... ssl_keyfile='/path/to/key.pem')
|
||||
|
||||
Python 2.7.9+ (pypy 2.5.1+) and 3.3+ support providing a password or passphrase
|
||||
to decrypt encrypted private keys. Use the ``tlsCertificateKeyFilePassword``
|
||||
option::
|
||||
Python supports providing a password or passphrase to decrypt encrypted
|
||||
private keys. Use the ``tlsCertificateKeyFilePassword`` option::
|
||||
|
||||
>>> client = pymongo.MongoClient('example.com',
|
||||
... tls=True,
|
||||
|
||||
@ -238,7 +238,7 @@ Therefore, Python dicts are not guaranteed to show keys in the order they are
|
||||
stored in BSON. Here, "a" is shown before "b":
|
||||
|
||||
>>> print(collection.find_one())
|
||||
{u'_id': 1.0, u'subdocument': {u'a': 1.0, u'b': 1.0}}
|
||||
{'_id': 1.0, 'subdocument': {'a': 1.0, 'b': 1.0}}
|
||||
|
||||
To preserve order when reading BSON, use the :class:`~bson.son.SON` class,
|
||||
which is a dict that remembers its key order. First, get a handle to the
|
||||
@ -264,7 +264,7 @@ Now, documents and subdocuments in query results are represented with
|
||||
.. doctest:: key-order
|
||||
|
||||
>>> print(collection_son.find_one())
|
||||
SON([(u'_id', 1.0), (u'subdocument', SON([(u'b', 1.0), (u'a', 1.0)]))])
|
||||
SON([('_id', 1.0), ('subdocument', SON([('b', 1.0), ('a', 1.0)]))])
|
||||
|
||||
The subdocument's actual storage layout is now visible: "b" is before "a".
|
||||
|
||||
@ -287,7 +287,7 @@ There are two solutions. First, you can match the subdocument field-by-field:
|
||||
|
||||
>>> collection.find_one({'subdocument.a': 1.0,
|
||||
... 'subdocument.b': 1.0})
|
||||
{u'_id': 1.0, u'subdocument': {u'a': 1.0, u'b': 1.0}}
|
||||
{'_id': 1.0, 'subdocument': {'a': 1.0, 'b': 1.0}}
|
||||
|
||||
The query matches any subdocument with an "a" of 1.0 and a "b" of 1.0,
|
||||
regardless of the order you specify them in Python or the order they are stored
|
||||
@ -298,7 +298,7 @@ The second solution is to use a :class:`~bson.son.SON` to specify the key order:
|
||||
|
||||
>>> query = {'subdocument': SON([('b', 1.0), ('a', 1.0)])}
|
||||
>>> collection.find_one(query)
|
||||
{u'_id': 1.0, u'subdocument': {u'a': 1.0, u'b': 1.0}}
|
||||
{'_id': 1.0, 'subdocument': {'a': 1.0, 'b': 1.0}}
|
||||
|
||||
The key order you use when you create a :class:`~bson.son.SON` is preserved
|
||||
when it is serialized to BSON and used as a query. Thus you can create a
|
||||
|
||||
121
doc/tutorial.rst
121
doc/tutorial.rst
@ -143,7 +143,7 @@ of the collections in our database:
|
||||
.. doctest::
|
||||
|
||||
>>> db.list_collection_names()
|
||||
[u'posts']
|
||||
['posts']
|
||||
|
||||
Getting a Single Document With :meth:`~pymongo.collection.Collection.find_one`
|
||||
------------------------------------------------------------------------------
|
||||
@ -159,11 +159,11 @@ document from the posts collection:
|
||||
|
||||
>>> import pprint
|
||||
>>> pprint.pprint(posts.find_one())
|
||||
{u'_id': ObjectId('...'),
|
||||
u'author': u'Mike',
|
||||
u'date': datetime.datetime(...),
|
||||
u'tags': [u'mongodb', u'python', u'pymongo'],
|
||||
u'text': u'My first blog post!'}
|
||||
{'_id': ObjectId('...'),
|
||||
'author': 'Mike',
|
||||
'date': datetime.datetime(...),
|
||||
'tags': ['mongodb', 'python', 'pymongo'],
|
||||
'text': 'My first blog post!'}
|
||||
|
||||
The result is a dictionary matching the one that we inserted previously.
|
||||
|
||||
@ -177,11 +177,11 @@ our results to a document with author "Mike" we do:
|
||||
.. doctest::
|
||||
|
||||
>>> pprint.pprint(posts.find_one({"author": "Mike"}))
|
||||
{u'_id': ObjectId('...'),
|
||||
u'author': u'Mike',
|
||||
u'date': datetime.datetime(...),
|
||||
u'tags': [u'mongodb', u'python', u'pymongo'],
|
||||
u'text': u'My first blog post!'}
|
||||
{'_id': ObjectId('...'),
|
||||
'author': 'Mike',
|
||||
'date': datetime.datetime(...),
|
||||
'tags': ['mongodb', 'python', 'pymongo'],
|
||||
'text': 'My first blog post!'}
|
||||
|
||||
If we try with a different author, like "Eliot", we'll get no result:
|
||||
|
||||
@ -201,11 +201,11 @@ We can also find a post by its ``_id``, which in our example is an ObjectId:
|
||||
>>> post_id
|
||||
ObjectId(...)
|
||||
>>> pprint.pprint(posts.find_one({"_id": post_id}))
|
||||
{u'_id': ObjectId('...'),
|
||||
u'author': u'Mike',
|
||||
u'date': datetime.datetime(...),
|
||||
u'tags': [u'mongodb', u'python', u'pymongo'],
|
||||
u'text': u'My first blog post!'}
|
||||
{'_id': ObjectId('...'),
|
||||
'author': 'Mike',
|
||||
'date': datetime.datetime(...),
|
||||
'tags': ['mongodb', 'python', 'pymongo'],
|
||||
'text': 'My first blog post!'}
|
||||
|
||||
Note that an ObjectId is not the same as its string representation:
|
||||
|
||||
@ -229,23 +229,6 @@ case to **convert the ObjectId from a string** before passing it to
|
||||
|
||||
.. seealso:: :ref:`web-application-querying-by-objectid`
|
||||
|
||||
A Note On Unicode Strings
|
||||
-------------------------
|
||||
You probably noticed that the regular Python strings we stored earlier look
|
||||
different when retrieved from the server (e.g. u'Mike' instead of 'Mike').
|
||||
A short explanation is in order.
|
||||
|
||||
MongoDB stores data in `BSON format <http://bsonspec.org>`_. BSON strings are
|
||||
UTF-8 encoded so PyMongo must ensure that any strings it stores contain only
|
||||
valid UTF-8 data. Regular strings (<type 'str'>) are validated and stored
|
||||
unaltered. Unicode strings (<type 'unicode'>) are encoded UTF-8 first. The
|
||||
reason our example string is represented in the Python shell as u'Mike' instead
|
||||
of 'Mike' is that PyMongo decodes each BSON string to a Python unicode string,
|
||||
not a regular str.
|
||||
|
||||
`You can read more about Python unicode strings here
|
||||
<http://docs.python.org/howto/unicode.html>`_.
|
||||
|
||||
Bulk Inserts
|
||||
------------
|
||||
In order to make querying a little more interesting, let's insert a
|
||||
@ -293,21 +276,21 @@ document in the ``posts`` collection:
|
||||
>>> for post in posts.find():
|
||||
... pprint.pprint(post)
|
||||
...
|
||||
{u'_id': ObjectId('...'),
|
||||
u'author': u'Mike',
|
||||
u'date': datetime.datetime(...),
|
||||
u'tags': [u'mongodb', u'python', u'pymongo'],
|
||||
u'text': u'My first blog post!'}
|
||||
{u'_id': ObjectId('...'),
|
||||
u'author': u'Mike',
|
||||
u'date': datetime.datetime(...),
|
||||
u'tags': [u'bulk', u'insert'],
|
||||
u'text': u'Another post!'}
|
||||
{u'_id': ObjectId('...'),
|
||||
u'author': u'Eliot',
|
||||
u'date': datetime.datetime(...),
|
||||
u'text': u'and pretty easy too!',
|
||||
u'title': u'MongoDB is fun'}
|
||||
{'_id': ObjectId('...'),
|
||||
'author': 'Mike',
|
||||
'date': datetime.datetime(...),
|
||||
'tags': ['mongodb', 'python', 'pymongo'],
|
||||
'text': 'My first blog post!'}
|
||||
{'_id': ObjectId('...'),
|
||||
'author': 'Mike',
|
||||
'date': datetime.datetime(...),
|
||||
'tags': ['bulk', 'insert'],
|
||||
'text': 'Another post!'}
|
||||
{'_id': ObjectId('...'),
|
||||
'author': 'Eliot',
|
||||
'date': datetime.datetime(...),
|
||||
'text': 'and pretty easy too!',
|
||||
'title': 'MongoDB is fun'}
|
||||
|
||||
Just like we did with :meth:`~pymongo.collection.Collection.find_one`,
|
||||
we can pass a document to :meth:`~pymongo.collection.Collection.find`
|
||||
@ -319,16 +302,16 @@ author is "Mike":
|
||||
>>> for post in posts.find({"author": "Mike"}):
|
||||
... pprint.pprint(post)
|
||||
...
|
||||
{u'_id': ObjectId('...'),
|
||||
u'author': u'Mike',
|
||||
u'date': datetime.datetime(...),
|
||||
u'tags': [u'mongodb', u'python', u'pymongo'],
|
||||
u'text': u'My first blog post!'}
|
||||
{u'_id': ObjectId('...'),
|
||||
u'author': u'Mike',
|
||||
u'date': datetime.datetime(...),
|
||||
u'tags': [u'bulk', u'insert'],
|
||||
u'text': u'Another post!'}
|
||||
{'_id': ObjectId('...'),
|
||||
'author': 'Mike',
|
||||
'date': datetime.datetime(...),
|
||||
'tags': ['mongodb', 'python', 'pymongo'],
|
||||
'text': 'My first blog post!'}
|
||||
{'_id': ObjectId('...'),
|
||||
'author': 'Mike',
|
||||
'date': datetime.datetime(...),
|
||||
'tags': ['bulk', 'insert'],
|
||||
'text': 'Another post!'}
|
||||
|
||||
Counting
|
||||
--------
|
||||
@ -362,16 +345,16 @@ than a certain date, but also sort the results by author:
|
||||
>>> for post in posts.find({"date": {"$lt": d}}).sort("author"):
|
||||
... pprint.pprint(post)
|
||||
...
|
||||
{u'_id': ObjectId('...'),
|
||||
u'author': u'Eliot',
|
||||
u'date': datetime.datetime(...),
|
||||
u'text': u'and pretty easy too!',
|
||||
u'title': u'MongoDB is fun'}
|
||||
{u'_id': ObjectId('...'),
|
||||
u'author': u'Mike',
|
||||
u'date': datetime.datetime(...),
|
||||
u'tags': [u'bulk', u'insert'],
|
||||
u'text': u'Another post!'}
|
||||
{'_id': ObjectId('...'),
|
||||
'author': 'Eliot',
|
||||
'date': datetime.datetime(...),
|
||||
'text': 'and pretty easy too!',
|
||||
'title': 'MongoDB is fun'}
|
||||
{'_id': ObjectId('...'),
|
||||
'author': 'Mike',
|
||||
'date': datetime.datetime(...),
|
||||
'tags': ['bulk', 'insert'],
|
||||
'text': 'Another post!'}
|
||||
|
||||
Here we use the special ``"$lt"`` operator to do a range query, and
|
||||
also call :meth:`~pymongo.cursor.Cursor.sort` to sort the results
|
||||
@ -393,7 +376,7 @@ First, we'll need to create the index:
|
||||
>>> result = db.profiles.create_index([('user_id', pymongo.ASCENDING)],
|
||||
... unique=True)
|
||||
>>> sorted(list(db.profiles.index_information()))
|
||||
[u'_id_', u'user_id_1']
|
||||
['_id_', 'user_id_1']
|
||||
|
||||
Notice that we have two indexes now: one is the index on ``_id`` that MongoDB
|
||||
creates automatically, and the other is the index on ``user_id`` we just
|
||||
|
||||
@ -107,12 +107,11 @@ class GridFS(object):
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
`data` can be either an instance of :class:`str` (:class:`bytes`
|
||||
in python 3) or a file-like object providing a :meth:`read` method.
|
||||
If an `encoding` keyword argument is passed, `data` can also be a
|
||||
:class:`unicode` (:class:`str` in python 3) instance, which will
|
||||
be encoded as `encoding` before being written. Any keyword arguments
|
||||
will be passed through to the created file - see
|
||||
`data` can be either an instance of :class:`bytes` or a file-like
|
||||
object providing a :meth:`read` method. If an `encoding` keyword
|
||||
argument is passed, `data` can also be a :class:`str` instance, which
|
||||
will be encoded as `encoding` before being written. Any keyword
|
||||
arguments will be passed through to the created file - see
|
||||
:meth:`~gridfs.grid_file.GridIn` for possible arguments. Returns the
|
||||
``"_id"`` of the created file.
|
||||
|
||||
|
||||
@ -144,11 +144,8 @@ class GridIn(object):
|
||||
- ``"chunkSize"`` or ``"chunk_size"``: size of each of the
|
||||
chunks, in bytes (default: 255 kb)
|
||||
|
||||
- ``"encoding"``: encoding used for this file. In Python 2,
|
||||
any :class:`unicode` that is written to the file will be
|
||||
converted to a :class:`str`. In Python 3, any :class:`str`
|
||||
that is written to the file will be converted to
|
||||
:class:`bytes`.
|
||||
- ``"encoding"``: encoding used for this file. Any :class:`str`
|
||||
that is written to the file will be converted to :class:`bytes`.
|
||||
|
||||
:Parameters:
|
||||
- `root_collection`: root collection to write to
|
||||
@ -345,15 +342,14 @@ class GridIn(object):
|
||||
`data` can be either a string of bytes or a file-like object
|
||||
(implementing :meth:`read`). If the file has an
|
||||
:attr:`encoding` attribute, `data` can also be a
|
||||
:class:`unicode` (:class:`str` in python 3) instance, which
|
||||
will be encoded as :attr:`encoding` before being written.
|
||||
:class:`str` instance, which will be encoded as
|
||||
:attr:`encoding` before being written.
|
||||
|
||||
Due to buffering, the data may not actually be written to the
|
||||
database until the :meth:`close` method is called. Raises
|
||||
:class:`ValueError` if this file is already closed. Raises
|
||||
:class:`TypeError` if `data` is not an instance of
|
||||
:class:`str` (:class:`bytes` in python 3), a file-like object,
|
||||
or an instance of :class:`unicode` (:class:`str` in python 3).
|
||||
:class:`bytes`, a file-like object, or an instance of :class:`str`.
|
||||
Unicode data is only allowed if the file has an :attr:`encoding`
|
||||
attribute.
|
||||
|
||||
|
||||
@ -439,8 +439,8 @@ class Collection(common.BaseObject):
|
||||
>>> for doc in db.test.find({}):
|
||||
... print(doc)
|
||||
...
|
||||
{u'x': 1, u'_id': ObjectId('54f62e60fba5226811f634ef')}
|
||||
{u'x': 1, u'_id': ObjectId('54f62e60fba5226811f634f0')}
|
||||
{'x': 1, '_id': ObjectId('54f62e60fba5226811f634ef')}
|
||||
{'x': 1, '_id': ObjectId('54f62e60fba5226811f634f0')}
|
||||
>>> # DeleteMany, UpdateOne, and UpdateMany are also available.
|
||||
...
|
||||
>>> from pymongo import InsertOne, DeleteOne, ReplaceOne
|
||||
@ -458,9 +458,9 @@ class Collection(common.BaseObject):
|
||||
>>> for doc in db.test.find({}):
|
||||
... print(doc)
|
||||
...
|
||||
{u'x': 1, u'_id': ObjectId('54f62e60fba5226811f634f0')}
|
||||
{u'y': 1, u'_id': ObjectId('54f62ee2fba5226811f634f1')}
|
||||
{u'z': 1, u'_id': ObjectId('54f62ee28891e756a6e1abd5')}
|
||||
{'x': 1, '_id': ObjectId('54f62e60fba5226811f634f0')}
|
||||
{'y': 1, '_id': ObjectId('54f62ee2fba5226811f634f1')}
|
||||
{'z': 1, '_id': ObjectId('54f62ee28891e756a6e1abd5')}
|
||||
|
||||
:Parameters:
|
||||
- `requests`: A list of write operations (see examples above).
|
||||
@ -660,7 +660,7 @@ class Collection(common.BaseObject):
|
||||
>>> result.inserted_id
|
||||
ObjectId('54f112defba522406c9cc208')
|
||||
>>> db.test.find_one({'x': 1})
|
||||
{u'x': 1, u'_id': ObjectId('54f112defba522406c9cc208')}
|
||||
{'x': 1, '_id': ObjectId('54f112defba522406c9cc208')}
|
||||
|
||||
:Parameters:
|
||||
- `document`: The document to insert. Must be a mutable mapping
|
||||
@ -876,7 +876,7 @@ class Collection(common.BaseObject):
|
||||
>>> for doc in db.test.find({}):
|
||||
... print(doc)
|
||||
...
|
||||
{u'x': 1, u'_id': ObjectId('54f4c5befba5220aa4d6dee7')}
|
||||
{'x': 1, '_id': ObjectId('54f4c5befba5220aa4d6dee7')}
|
||||
>>> result = db.test.replace_one({'x': 1}, {'y': 1})
|
||||
>>> result.matched_count
|
||||
1
|
||||
@ -885,7 +885,7 @@ class Collection(common.BaseObject):
|
||||
>>> for doc in db.test.find({}):
|
||||
... print(doc)
|
||||
...
|
||||
{u'y': 1, u'_id': ObjectId('54f4c5befba5220aa4d6dee7')}
|
||||
{'y': 1, '_id': ObjectId('54f4c5befba5220aa4d6dee7')}
|
||||
|
||||
The *upsert* option can be used to insert a new document if a matching
|
||||
document does not exist.
|
||||
@ -898,7 +898,7 @@ class Collection(common.BaseObject):
|
||||
>>> result.upserted_id
|
||||
ObjectId('54f11e5c8891e756a6e1abd4')
|
||||
>>> db.test.find_one({'x': 1})
|
||||
{u'x': 1, u'_id': ObjectId('54f11e5c8891e756a6e1abd4')}
|
||||
{'x': 1, '_id': ObjectId('54f11e5c8891e756a6e1abd4')}
|
||||
|
||||
:Parameters:
|
||||
- `filter`: A query that matches the document to replace.
|
||||
@ -955,9 +955,9 @@ class Collection(common.BaseObject):
|
||||
>>> for doc in db.test.find():
|
||||
... print(doc)
|
||||
...
|
||||
{u'x': 1, u'_id': 0}
|
||||
{u'x': 1, u'_id': 1}
|
||||
{u'x': 1, u'_id': 2}
|
||||
{'x': 1, '_id': 0}
|
||||
{'x': 1, '_id': 1}
|
||||
{'x': 1, '_id': 2}
|
||||
>>> result = db.test.update_one({'x': 1}, {'$inc': {'x': 3}})
|
||||
>>> result.matched_count
|
||||
1
|
||||
@ -966,9 +966,9 @@ class Collection(common.BaseObject):
|
||||
>>> for doc in db.test.find():
|
||||
... print(doc)
|
||||
...
|
||||
{u'x': 4, u'_id': 0}
|
||||
{u'x': 1, u'_id': 1}
|
||||
{u'x': 1, u'_id': 2}
|
||||
{'x': 4, '_id': 0}
|
||||
{'x': 1, '_id': 1}
|
||||
{'x': 1, '_id': 2}
|
||||
|
||||
:Parameters:
|
||||
- `filter`: A query that matches the document to update.
|
||||
@ -1031,9 +1031,9 @@ class Collection(common.BaseObject):
|
||||
>>> for doc in db.test.find():
|
||||
... print(doc)
|
||||
...
|
||||
{u'x': 1, u'_id': 0}
|
||||
{u'x': 1, u'_id': 1}
|
||||
{u'x': 1, u'_id': 2}
|
||||
{'x': 1, '_id': 0}
|
||||
{'x': 1, '_id': 1}
|
||||
{'x': 1, '_id': 2}
|
||||
>>> result = db.test.update_many({'x': 1}, {'$inc': {'x': 3}})
|
||||
>>> result.matched_count
|
||||
3
|
||||
@ -1042,9 +1042,9 @@ class Collection(common.BaseObject):
|
||||
>>> for doc in db.test.find():
|
||||
... print(doc)
|
||||
...
|
||||
{u'x': 4, u'_id': 0}
|
||||
{u'x': 4, u'_id': 1}
|
||||
{u'x': 4, u'_id': 2}
|
||||
{'x': 4, '_id': 0}
|
||||
{'x': 4, '_id': 1}
|
||||
{'x': 4, '_id': 2}
|
||||
|
||||
:Parameters:
|
||||
- `filter`: A query that matches the documents to update.
|
||||
@ -2123,10 +2123,10 @@ class Collection(common.BaseObject):
|
||||
like this:
|
||||
|
||||
>>> db.test.create_index("x", unique=True)
|
||||
u'x_1'
|
||||
'x_1'
|
||||
>>> db.test.index_information()
|
||||
{u'_id_': {u'key': [(u'_id', 1)]},
|
||||
u'x_1': {u'unique': True, u'key': [(u'x', 1)]}}
|
||||
{'_id_': {'key': [('_id', 1)]},
|
||||
'x_1': {'unique': True, 'key': [('x', 1)]}}
|
||||
|
||||
:Parameters:
|
||||
- `session` (optional): a
|
||||
@ -2800,7 +2800,7 @@ class Collection(common.BaseObject):
|
||||
>>> db.test.count_documents({'x': 1})
|
||||
2
|
||||
>>> db.test.find_one_and_delete({'x': 1})
|
||||
{u'x': 1, u'_id': ObjectId('54f4e12bfba5220aa4d6dee8')}
|
||||
{'x': 1, '_id': ObjectId('54f4e12bfba5220aa4d6dee8')}
|
||||
>>> db.test.count_documents({'x': 1})
|
||||
1
|
||||
|
||||
@ -2809,17 +2809,17 @@ class Collection(common.BaseObject):
|
||||
>>> for doc in db.test.find({'x': 1}):
|
||||
... print(doc)
|
||||
...
|
||||
{u'x': 1, u'_id': 0}
|
||||
{u'x': 1, u'_id': 1}
|
||||
{u'x': 1, u'_id': 2}
|
||||
{'x': 1, '_id': 0}
|
||||
{'x': 1, '_id': 1}
|
||||
{'x': 1, '_id': 2}
|
||||
>>> db.test.find_one_and_delete(
|
||||
... {'x': 1}, sort=[('_id', pymongo.DESCENDING)])
|
||||
{u'x': 1, u'_id': 2}
|
||||
{'x': 1, '_id': 2}
|
||||
|
||||
The *projection* option can be used to limit the fields returned.
|
||||
|
||||
>>> db.test.find_one_and_delete({'x': 1}, projection={'_id': False})
|
||||
{u'x': 1}
|
||||
{'x': 1}
|
||||
|
||||
:Parameters:
|
||||
- `filter`: A query that matches the document to delete.
|
||||
@ -2877,17 +2877,17 @@ class Collection(common.BaseObject):
|
||||
>>> for doc in db.test.find({}):
|
||||
... print(doc)
|
||||
...
|
||||
{u'x': 1, u'_id': 0}
|
||||
{u'x': 1, u'_id': 1}
|
||||
{u'x': 1, u'_id': 2}
|
||||
{'x': 1, '_id': 0}
|
||||
{'x': 1, '_id': 1}
|
||||
{'x': 1, '_id': 2}
|
||||
>>> db.test.find_one_and_replace({'x': 1}, {'y': 1})
|
||||
{u'x': 1, u'_id': 0}
|
||||
{'x': 1, '_id': 0}
|
||||
>>> for doc in db.test.find({}):
|
||||
... print(doc)
|
||||
...
|
||||
{u'y': 1, u'_id': 0}
|
||||
{u'x': 1, u'_id': 1}
|
||||
{u'x': 1, u'_id': 2}
|
||||
{'y': 1, '_id': 0}
|
||||
{'x': 1, '_id': 1}
|
||||
{'x': 1, '_id': 2}
|
||||
|
||||
:Parameters:
|
||||
- `filter`: A query that matches the document to replace.
|
||||
@ -2953,7 +2953,7 @@ class Collection(common.BaseObject):
|
||||
|
||||
>>> db.test.find_one_and_update(
|
||||
... {'_id': 665}, {'$inc': {'count': 1}, '$set': {'done': True}})
|
||||
{u'_id': 665, u'done': False, u'count': 25}}
|
||||
{'_id': 665, 'done': False, 'count': 25}}
|
||||
|
||||
Returns ``None`` if no document matches the filter.
|
||||
|
||||
@ -2971,7 +2971,7 @@ class Collection(common.BaseObject):
|
||||
... {'_id': 'userid'},
|
||||
... {'$inc': {'seq': 1}},
|
||||
... return_document=ReturnDocument.AFTER)
|
||||
{u'_id': u'userid', u'seq': 1}
|
||||
{'_id': 'userid', 'seq': 1}
|
||||
|
||||
You can limit the fields returned with the *projection* option.
|
||||
|
||||
@ -2980,7 +2980,7 @@ class Collection(common.BaseObject):
|
||||
... {'$inc': {'seq': 1}},
|
||||
... projection={'seq': True, '_id': False},
|
||||
... return_document=ReturnDocument.AFTER)
|
||||
{u'seq': 2}
|
||||
{'seq': 2}
|
||||
|
||||
The *upsert* option can be used to create the document if it doesn't
|
||||
already exist.
|
||||
@ -2993,20 +2993,20 @@ class Collection(common.BaseObject):
|
||||
... projection={'seq': True, '_id': False},
|
||||
... upsert=True,
|
||||
... return_document=ReturnDocument.AFTER)
|
||||
{u'seq': 1}
|
||||
{'seq': 1}
|
||||
|
||||
If multiple documents match *filter*, a *sort* can be applied.
|
||||
|
||||
>>> for doc in db.test.find({'done': True}):
|
||||
... print(doc)
|
||||
...
|
||||
{u'_id': 665, u'done': True, u'result': {u'count': 26}}
|
||||
{u'_id': 701, u'done': True, u'result': {u'count': 17}}
|
||||
{'_id': 665, 'done': True, 'result': {'count': 26}}
|
||||
{'_id': 701, 'done': True, 'result': {'count': 17}}
|
||||
>>> db.test.find_one_and_update(
|
||||
... {'done': True},
|
||||
... {'$set': {'final': True}},
|
||||
... sort=[('_id', pymongo.DESCENDING)])
|
||||
{u'_id': 701, u'done': True, u'result': {u'count': 17}}
|
||||
{'_id': 701, 'done': True, 'result': {'count': 17}}
|
||||
|
||||
:Parameters:
|
||||
- `filter`: A query that matches the document to update.
|
||||
|
||||
@ -234,8 +234,7 @@ def validate_non_negative_integer_or_none(option, value):
|
||||
|
||||
|
||||
def validate_string(option, value):
|
||||
"""Validates that 'value' is an instance of `basestring` for Python 2
|
||||
or `str` for Python 3.
|
||||
"""Validates that 'value' is an instance of `str`.
|
||||
"""
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
|
||||
@ -142,10 +142,6 @@ def decompress(data, compressor_id):
|
||||
# https://github.com/andrix/python-snappy/issues/65
|
||||
# This only matters when data is a memoryview since
|
||||
# id(bytes(data)) == id(data) when data is a bytes.
|
||||
# NOTE: bytes(memoryview) returns the memoryview repr
|
||||
# in Python 2.7. The right thing to do in 2.7 is call
|
||||
# memoryview.tobytes(), but we currently only use
|
||||
# memoryview in Python 3.x.
|
||||
return snappy.uncompress(bytes(data))
|
||||
elif compressor_id == ZlibContext.compressor_id:
|
||||
return zlib.decompress(data)
|
||||
|
||||
@ -364,12 +364,12 @@ class ClientEncryption(object):
|
||||
These are the Azure Active Directory credentials used to
|
||||
generate Azure Key Vault messages.
|
||||
- `gcp`: Map with "email" as a string and "privateKey"
|
||||
as `bytes` or a base64 encoded string (unicode on Python 2).
|
||||
as `bytes` or a base64 encoded string.
|
||||
Additionally, "endpoint" may also be specified as a string
|
||||
(defaults to 'oauth2.googleapis.com'). These are the
|
||||
credentials used to generate Google Cloud KMS messages.
|
||||
- `local`: Map with "key" as `bytes` (96 bytes in length) or
|
||||
a base64 encoded string (unicode on Python 2) which decodes
|
||||
a base64 encoded string which decodes
|
||||
to 96 bytes. "key" is the master key used to encrypt/decrypt
|
||||
data keys. This key should be generated and stored as securely
|
||||
as possible.
|
||||
|
||||
@ -65,12 +65,12 @@ class AutoEncryptionOpts(object):
|
||||
These are the Azure Active Directory credentials used to
|
||||
generate Azure Key Vault messages.
|
||||
- `gcp`: Map with "email" as a string and "privateKey"
|
||||
as `bytes` or a base64 encoded string (unicode on Python 2).
|
||||
as `bytes` or a base64 encoded string.
|
||||
Additionally, "endpoint" may also be specified as a string
|
||||
(defaults to 'oauth2.googleapis.com'). These are the
|
||||
credentials used to generate Google Cloud KMS messages.
|
||||
- `local`: Map with "key" as `bytes` (96 bytes in length) or
|
||||
a base64 encoded string (unicode on Python 2) which decodes
|
||||
a base64 encoded string which decodes
|
||||
to 96 bytes. "key" is the master key used to encrypt/decrypt
|
||||
data keys. This key should be generated and stored as securely
|
||||
as possible.
|
||||
|
||||
@ -1574,8 +1574,6 @@ class _OpReply(object):
|
||||
# PYTHON-945: ignore starting_from field.
|
||||
flags, cursor_id, _, number_returned = cls.UNPACK_FROM(msg)
|
||||
|
||||
# Convert Python 3 memoryview to bytes. Note we should call
|
||||
# memoryview.tobytes() if we start using memoryview in Python 2.7.
|
||||
documents = bytes(msg[20:])
|
||||
return cls(flags, cursor_id, number_returned, documents)
|
||||
|
||||
@ -1649,8 +1647,6 @@ class _OpMsg(object):
|
||||
if len(msg) != first_payload_size + 5:
|
||||
raise ProtocolError("Unsupported OP_MSG reply: >1 section")
|
||||
|
||||
# Convert Python 3 memoryview to bytes. Note we should call
|
||||
# memoryview.tobytes() if we start using memoryview in Python 2.7.
|
||||
payload_document = bytes(msg[5:])
|
||||
return cls(flags, payload_document)
|
||||
|
||||
@ -1699,17 +1695,17 @@ def _first_batch(sock_info, db, coll, query, ntoreturn,
|
||||
# listIndexes
|
||||
if 'cursor' in cmd:
|
||||
result = {
|
||||
u'cursor': {
|
||||
u'firstBatch': docs,
|
||||
u'id': reply.cursor_id,
|
||||
u'ns': u'%s.%s' % (db, coll)
|
||||
'cursor': {
|
||||
'firstBatch': docs,
|
||||
'id': reply.cursor_id,
|
||||
'ns': '%s.%s' % (db, coll)
|
||||
},
|
||||
u'ok': 1.0
|
||||
'ok': 1.0
|
||||
}
|
||||
# fsyncUnlock, currentOp
|
||||
else:
|
||||
result = docs[0] if docs else {}
|
||||
result[u'ok'] = 1.0
|
||||
result['ok'] = 1.0
|
||||
if publish:
|
||||
duration = (datetime.datetime.now() - start) + encoding_duration
|
||||
listeners.publish_command_success(
|
||||
|
||||
@ -26,9 +26,9 @@ access:
|
||||
>>> from pymongo import MongoClient
|
||||
>>> c = MongoClient()
|
||||
>>> c.test_database
|
||||
Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), u'test_database')
|
||||
Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'test_database')
|
||||
>>> c['test-database']
|
||||
Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), u'test-database')
|
||||
Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'test-database')
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
@ -118,12 +118,7 @@ class MongoClient(common.BaseObject):
|
||||
passwords reserved characters like ':', '/', '+' and '@' must be
|
||||
percent encoded following RFC 2396::
|
||||
|
||||
try:
|
||||
# Python 3.x
|
||||
from urllib.parse import quote_plus
|
||||
except ImportError:
|
||||
# Python 2.x
|
||||
from urllib import quote_plus
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
uri = "mongodb://%s:%s@%s" % (
|
||||
quote_plus(user), quote_plus(password), host)
|
||||
|
||||
@ -225,7 +225,7 @@ else:
|
||||
# while the main thread holds the import lock, getaddrinfo deadlocks trying
|
||||
# to import the IDNA codec. Import it here, where presumably we're on the
|
||||
# main thread, to avoid the deadlock. See PYTHON-607.
|
||||
u'foo'.encode('idna')
|
||||
'foo'.encode('idna')
|
||||
|
||||
|
||||
def _raise_connection_failure(address, error, msg_prefix=None):
|
||||
|
||||
@ -49,8 +49,8 @@ else:
|
||||
|
||||
:Parameters:
|
||||
- `data`: The string to SASLprep. Unicode strings
|
||||
(python 2.x unicode, 3.x str) are supported. Byte strings
|
||||
(python 2.x str, 3.x bytes) are ignored.
|
||||
(:class:`str`) are supported. Byte strings
|
||||
(:class:`bytes`) are ignored.
|
||||
- `prohibit_unassigned_code_points`: True / False. RFC 3454
|
||||
and RFCs for various SASL mechanisms distinguish between
|
||||
`queries` (unassigned code points allowed) and
|
||||
|
||||
@ -72,8 +72,6 @@ class Selection(object):
|
||||
def __bool__(self):
|
||||
return bool(self.server_descriptions)
|
||||
|
||||
__nonzero__ = __bool__ # Python 2.
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.server_descriptions[item]
|
||||
|
||||
|
||||
@ -19,114 +19,26 @@ import sys as _sys
|
||||
|
||||
# PROTOCOL_TLS_CLIENT is Python 3.6+
|
||||
PROTOCOL_SSLv23 = getattr(_ssl, "PROTOCOL_TLS_CLIENT", _ssl.PROTOCOL_SSLv23)
|
||||
# Python 2.7.9+
|
||||
OP_NO_SSLv2 = getattr(_ssl, "OP_NO_SSLv2", 0)
|
||||
# Python 2.7.9+
|
||||
OP_NO_SSLv3 = getattr(_ssl, "OP_NO_SSLv3", 0)
|
||||
# Python 2.7.9+, OpenSSL 1.0.0+
|
||||
OP_NO_COMPRESSION = getattr(_ssl, "OP_NO_COMPRESSION", 0)
|
||||
# Python 3.7+, OpenSSL 1.1.0h+
|
||||
OP_NO_RENEGOTIATION = getattr(_ssl, "OP_NO_RENEGOTIATION", 0)
|
||||
|
||||
# Python 2.7.9+
|
||||
HAS_SNI = getattr(_ssl, "HAS_SNI", False)
|
||||
IS_PYOPENSSL = False
|
||||
|
||||
# Base Exception class
|
||||
SSLError = _ssl.SSLError
|
||||
|
||||
try:
|
||||
# CPython 2.7.9+
|
||||
from ssl import SSLContext
|
||||
if hasattr(_ssl, "VERIFY_CRL_CHECK_LEAF"):
|
||||
from ssl import VERIFY_CRL_CHECK_LEAF
|
||||
# Python 3.7 uses OpenSSL's hostname matching implementation
|
||||
# making it the obvious version to start using SSLConext.check_hostname.
|
||||
# Python 3.6 might have been a good version, but it suffers
|
||||
# from https://bugs.python.org/issue32185.
|
||||
# We'll use our bundled match_hostname for older Python
|
||||
# versions, which also supports IP address matching
|
||||
# with Python < 3.5.
|
||||
CHECK_HOSTNAME_SAFE = _sys.version_info[:2] >= (3, 7)
|
||||
except ImportError:
|
||||
from pymongo.errors import ConfigurationError
|
||||
|
||||
class SSLContext(object):
|
||||
"""A fake SSLContext.
|
||||
|
||||
This implements an API similar to ssl.SSLContext from python 3.2
|
||||
but does not implement methods or properties that would be
|
||||
incompatible with ssl.wrap_socket from python 2.7 < 2.7.9.
|
||||
|
||||
You must pass protocol which must be one of the PROTOCOL_* constants
|
||||
defined in the ssl module. ssl.PROTOCOL_SSLv23 is recommended for maximum
|
||||
interoperability.
|
||||
"""
|
||||
|
||||
__slots__ = ('_cafile', '_certfile',
|
||||
'_keyfile', '_protocol', '_verify_mode')
|
||||
|
||||
def __init__(self, protocol):
|
||||
self._cafile = None
|
||||
self._certfile = None
|
||||
self._keyfile = None
|
||||
self._protocol = protocol
|
||||
self._verify_mode = _ssl.CERT_NONE
|
||||
|
||||
@property
|
||||
def protocol(self):
|
||||
"""The protocol version chosen when constructing the context.
|
||||
This attribute is read-only.
|
||||
"""
|
||||
return self._protocol
|
||||
|
||||
def __get_verify_mode(self):
|
||||
"""Whether to try to verify other peers' certificates and how to
|
||||
behave if verification fails. This attribute must be one of
|
||||
ssl.CERT_NONE, ssl.CERT_OPTIONAL or ssl.CERT_REQUIRED.
|
||||
"""
|
||||
return self._verify_mode
|
||||
|
||||
def __set_verify_mode(self, value):
|
||||
"""Setter for verify_mode."""
|
||||
self._verify_mode = value
|
||||
|
||||
verify_mode = property(__get_verify_mode, __set_verify_mode)
|
||||
|
||||
def load_cert_chain(self, certfile, keyfile=None, password=None):
|
||||
"""Load a private key and the corresponding certificate. The certfile
|
||||
string must be the path to a single file in PEM format containing the
|
||||
certificate as well as any number of CA certificates needed to
|
||||
establish the certificate's authenticity. The keyfile string, if
|
||||
present, must point to a file containing the private key. Otherwise
|
||||
the private key will be taken from certfile as well.
|
||||
"""
|
||||
if password is not None:
|
||||
raise ConfigurationError(
|
||||
"Support for ssl_pem_passphrase requires "
|
||||
"python 2.7.9+ (pypy 2.5.1+), python 3 or "
|
||||
"PyOpenSSL")
|
||||
self._certfile = certfile
|
||||
self._keyfile = keyfile
|
||||
|
||||
def load_verify_locations(self, cafile=None, dummy=None):
|
||||
"""Load a set of "certification authority"(CA) certificates used to
|
||||
validate other peers' certificates when `~verify_mode` is other than
|
||||
ssl.CERT_NONE.
|
||||
"""
|
||||
self._cafile = cafile
|
||||
|
||||
def wrap_socket(self, sock, server_side=False,
|
||||
do_handshake_on_connect=True,
|
||||
suppress_ragged_eofs=True, dummy=None):
|
||||
"""Wrap an existing Python socket sock and return an ssl.SSLSocket
|
||||
object.
|
||||
"""
|
||||
return _ssl.wrap_socket(sock, keyfile=self._keyfile,
|
||||
certfile=self._certfile,
|
||||
server_side=server_side,
|
||||
cert_reqs=self._verify_mode,
|
||||
ssl_version=self._protocol,
|
||||
ca_certs=self._cafile,
|
||||
do_handshake_on_connect=do_handshake_on_connect,
|
||||
suppress_ragged_eofs=suppress_ragged_eofs)
|
||||
from ssl import SSLContext
|
||||
if hasattr(_ssl, "VERIFY_CRL_CHECK_LEAF"):
|
||||
from ssl import VERIFY_CRL_CHECK_LEAF
|
||||
# Python 3.7 uses OpenSSL's hostname matching implementation
|
||||
# making it the obvious version to start using SSLConext.check_hostname.
|
||||
# Python 3.6 might have been a good version, but it suffers
|
||||
# from https://bugs.python.org/issue32185.
|
||||
# We'll use our bundled match_hostname for older Python
|
||||
# versions, which also supports IP address matching
|
||||
# with Python < 3.5.
|
||||
CHECK_HOSTNAME_SAFE = _sys.version_info[:2] >= (3, 7)
|
||||
|
||||
40
setup.py
40
setup.py
@ -145,46 +145,6 @@ class doc(Command):
|
||||
raise RuntimeError(
|
||||
"You must install Sphinx to build or test the documentation.")
|
||||
|
||||
# TODO: Convert all the docs to Python 3 and delete all this.
|
||||
import doctest
|
||||
from doctest import OutputChecker as _OutputChecker
|
||||
|
||||
# Match u or U (possibly followed by r or R), removing it.
|
||||
# r/R can follow u/U but not precede it. Don't match the
|
||||
# single character string 'u' or 'U'.
|
||||
_u_literal_re = re.compile(
|
||||
r"(\W|^)(?<![\'\"])[uU]([rR]?[\'\"])", re.UNICODE)
|
||||
# Match b or B (possibly followed by r or R), removing.
|
||||
# r/R can follow b/B but not precede it. Don't match the
|
||||
# single character string 'b' or 'B'.
|
||||
_b_literal_re = re.compile(
|
||||
r"(\W|^)(?<![\'\"])[bB]([rR]?[\'\"])", re.UNICODE)
|
||||
|
||||
class _StringPrefixFixer(_OutputChecker):
|
||||
|
||||
def check_output(self, want, got, optionflags):
|
||||
# The docstrings are written with python 2.x in mind.
|
||||
# To make the doctests pass in python 3 we have to
|
||||
# strip the 'u' prefix from the expected results. The
|
||||
# actual results won't have that prefix.
|
||||
want = re.sub(_u_literal_re, r'\1\2', want)
|
||||
# We also have to strip the 'b' prefix from the actual
|
||||
# results since python 2.x expected results won't have
|
||||
# that prefix.
|
||||
got = re.sub(_b_literal_re, r'\1\2', got)
|
||||
return super(
|
||||
_StringPrefixFixer, self).check_output(
|
||||
want, got, optionflags)
|
||||
|
||||
def output_difference(self, example, got, optionflags):
|
||||
example.want = re.sub(_u_literal_re, r'\1\2', example.want)
|
||||
got = re.sub(_b_literal_re, r'\1\2', got)
|
||||
return super(
|
||||
_StringPrefixFixer, self).output_difference(
|
||||
example, got, optionflags)
|
||||
|
||||
doctest.OutputChecker = _StringPrefixFixer
|
||||
|
||||
if self.test:
|
||||
path = os.path.join(
|
||||
os.path.abspath('.'), "doc", "_build", "doctest")
|
||||
|
||||
@ -18,11 +18,7 @@ import os
|
||||
import sys
|
||||
import threading
|
||||
|
||||
try:
|
||||
from urllib.parse import quote_plus
|
||||
except ImportError:
|
||||
# Python 2
|
||||
from urllib import quote_plus
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
@ -516,63 +512,63 @@ class TestSCRAM(unittest.TestCase):
|
||||
|
||||
if HAVE_STRINGPREP:
|
||||
# Test the use of SASLprep on passwords. For example,
|
||||
# saslprep(u'\u2136') becomes u'IV' and saslprep(u'I\u00ADX')
|
||||
# becomes u'IX'. SASLprep is only supported when the standard
|
||||
# saslprep('\u2136') becomes 'IV' and saslprep('I\u00ADX')
|
||||
# becomes 'IX'. SASLprep is only supported when the standard
|
||||
# library provides stringprep.
|
||||
client_context.create_user(
|
||||
'testscram',
|
||||
u'\u2168',
|
||||
u'\u2163',
|
||||
'\u2168',
|
||||
'\u2163',
|
||||
roles=['dbOwner'],
|
||||
mechanisms=['SCRAM-SHA-256'])
|
||||
|
||||
client_context.create_user(
|
||||
'testscram',
|
||||
u'IX',
|
||||
u'IX',
|
||||
'IX',
|
||||
'IX',
|
||||
roles=['dbOwner'],
|
||||
mechanisms=['SCRAM-SHA-256'])
|
||||
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(u'\u2168', u'\u2163'))
|
||||
client.testscram.authenticate('\u2168', '\u2163'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(
|
||||
u'\u2168', u'\u2163', mechanism='SCRAM-SHA-256'))
|
||||
'\u2168', '\u2163', mechanism='SCRAM-SHA-256'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(u'\u2168', u'IV'))
|
||||
client.testscram.authenticate('\u2168', 'IV'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(u'IX', u'I\u00ADX'))
|
||||
client.testscram.authenticate('IX', 'I\u00ADX'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(
|
||||
u'IX', u'I\u00ADX', mechanism='SCRAM-SHA-256'))
|
||||
'IX', 'I\u00ADX', mechanism='SCRAM-SHA-256'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(u'IX', u'IX'))
|
||||
client.testscram.authenticate('IX', 'IX'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
u'mongodb://\u2168:\u2163@%s:%d/testscram' % (host, port))
|
||||
'mongodb://\u2168:\u2163@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
client = rs_or_single_client_noauth(
|
||||
u'mongodb://\u2168:IV@%s:%d/testscram' % (host, port))
|
||||
'mongodb://\u2168:IV@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
u'mongodb://IX:I\u00ADX@%s:%d/testscram' % (host, port))
|
||||
'mongodb://IX:I\u00ADX@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
client = rs_or_single_client_noauth(
|
||||
u'mongodb://IX:IX@%s:%d/testscram' % (host, port))
|
||||
'mongodb://IX:IX@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
self.listener.results.clear()
|
||||
|
||||
@ -97,10 +97,7 @@ class TestBinary(unittest.TestCase):
|
||||
self.assertRaises(ValueError, Binary, b"hello", 256)
|
||||
self.assertTrue(Binary(b"hello", 0))
|
||||
self.assertTrue(Binary(b"hello", 255))
|
||||
if platform.python_implementation() != "Jython":
|
||||
# Jython's memoryview accepts unicode strings...
|
||||
# https://bugs.jython.org/issue2784
|
||||
self.assertRaises(TypeError, Binary, "hello")
|
||||
self.assertRaises(TypeError, Binary, "hello")
|
||||
|
||||
def test_subtype(self):
|
||||
one = Binary(b"hello")
|
||||
|
||||
@ -147,8 +147,8 @@ class TestBSON(unittest.TestCase):
|
||||
helper({"a binary": Binary(b"test", 128)})
|
||||
helper({"a binary": Binary(b"test", 254)})
|
||||
helper({"another binary": Binary(b"test", 2)})
|
||||
helper(SON([(u'test dst', datetime.datetime(1993, 4, 4, 2))]))
|
||||
helper(SON([(u'test negative dst',
|
||||
helper(SON([('test dst', datetime.datetime(1993, 4, 4, 2))]))
|
||||
helper(SON([('test negative dst',
|
||||
datetime.datetime(1, 1, 1, 1, 1, 1))]))
|
||||
helper({"big float": float(10000000000)})
|
||||
helper({"ref": DBRef("coll", 5)})
|
||||
@ -653,7 +653,7 @@ class TestBSON(unittest.TestCase):
|
||||
# The C extension was segfaulting on unicode RegExs, so we have this test
|
||||
# that doesn't really test anything but the lack of a segfault.
|
||||
def test_unicode_regex(self):
|
||||
regex = re.compile(u'revisi\xf3n')
|
||||
regex = re.compile('revisi\xf3n')
|
||||
decode(encode({"regex": regex}))
|
||||
|
||||
def test_non_string_keys(self):
|
||||
@ -675,9 +675,6 @@ class TestBSON(unittest.TestCase):
|
||||
doc = {"a": "\x00"}
|
||||
self.assertEqual(doc, decode(encode(doc)))
|
||||
|
||||
# This test doesn't make much sense in Python2
|
||||
# since {'a': '\x00'} == {'a': u'\x00'}.
|
||||
# Decoding here actually returns {'a': '\x00'}
|
||||
doc = {"a": "\x00"}
|
||||
self.assertEqual(doc, decode(encode(doc)))
|
||||
|
||||
@ -770,8 +767,8 @@ class TestBSON(unittest.TestCase):
|
||||
self.assertEqual(doc1, decode(doc1_bson))
|
||||
|
||||
# Valid Python regex, with flags.
|
||||
re2 = re.compile(u'.*', re.I | re.M | re.S | re.U | re.X)
|
||||
bson_re2 = Regex(u'.*', re.I | re.M | re.S | re.U | re.X)
|
||||
re2 = re.compile('.*', re.I | re.M | re.S | re.U | re.X)
|
||||
bson_re2 = Regex('.*', re.I | re.M | re.S | re.U | re.X)
|
||||
|
||||
doc2_with_re = {'r': re2}
|
||||
doc2_with_bson_re = {'r': bson_re2}
|
||||
|
||||
@ -54,7 +54,7 @@ class TestCode(unittest.TestCase):
|
||||
self.assertEqual(str(with_scope), str(another_scope))
|
||||
self.assertEqual({'new_var': 42, 'my_var': 5}, another_scope.scope)
|
||||
# No error.
|
||||
Code(u'héllø world¡')
|
||||
Code('héllø world¡')
|
||||
|
||||
def test_repr(self):
|
||||
c = Code("hello world", {})
|
||||
|
||||
@ -1299,11 +1299,11 @@ class TestCollection(IntegrationTest):
|
||||
self.addCleanup(coll.drop)
|
||||
|
||||
coll.create_index('a', unique=True)
|
||||
coll.insert_one({'a': u'unicode \U0001f40d'})
|
||||
coll.insert_one({'a': 'unicode \U0001f40d'})
|
||||
with self.assertRaisesRegex(
|
||||
DuplicateKeyError,
|
||||
'E11000 duplicate key error') as ctx:
|
||||
coll.insert_one({'a': u'unicode \U0001f40d'})
|
||||
coll.insert_one({'a': 'unicode \U0001f40d'})
|
||||
|
||||
# Once more for good measure.
|
||||
self.assertIn('E11000 duplicate key error',
|
||||
|
||||
@ -857,7 +857,6 @@ class TestCursor(IntegrationTest):
|
||||
|
||||
self.assertEqual(3, db.test.find().where('this.x < 3').count())
|
||||
self.assertEqual(10, db.test.find().count())
|
||||
self.assertEqual(3, db.test.find().where(u'this.x < 3').count())
|
||||
self.assertEqual([0, 1, 2],
|
||||
[a["x"] for a in
|
||||
db.test.find().where('this.x < 3')])
|
||||
|
||||
@ -61,7 +61,7 @@ class TestDBRef(unittest.TestCase):
|
||||
self.assertEqual(repr(DBRef("coll",
|
||||
ObjectId("1234567890abcdef12345678"))),
|
||||
"DBRef(%s, ObjectId('1234567890abcdef12345678'))"
|
||||
% (repr(u'coll'),)
|
||||
% (repr('coll'),)
|
||||
)
|
||||
self.assertEqual(repr(DBRef("coll", 5, foo="bar")),
|
||||
"DBRef('coll', 5, foo='bar')")
|
||||
|
||||
@ -38,7 +38,6 @@ from pymongo.topology_description import TOPOLOGY_TYPE
|
||||
from pymongo.uri_parser import parse_uri
|
||||
from test import unittest, IntegrationTest
|
||||
from test.utils import (assertion_context,
|
||||
cdecimal_patched,
|
||||
CMAPListener,
|
||||
client_context,
|
||||
get_pool,
|
||||
@ -358,15 +357,6 @@ class TestIntegration(SpecRunner):
|
||||
event_type = getattr(monitoring, event)
|
||||
return self.pool_listener.event_count(event_type)
|
||||
|
||||
def maybe_skip_scenario(self, test):
|
||||
"""Override to skip threaded tests when cdecimal is installed on 2.7
|
||||
"""
|
||||
super(TestIntegration, self).maybe_skip_scenario(test)
|
||||
# PYTHON-2332
|
||||
ops = [op['name'] for op in test['operations']]
|
||||
if cdecimal_patched() and 'startThread' in ops:
|
||||
raise unittest.SkipTest('PYTHON-2332 test fails with cdecimal')
|
||||
|
||||
def assert_event_count(self, event, count):
|
||||
"""Run the assertEventCount test operation.
|
||||
|
||||
|
||||
@ -58,13 +58,13 @@ class TestErrors(PyMongoTestCase):
|
||||
self.assertIn("full error", traceback.format_exc())
|
||||
|
||||
def test_unicode_strs_operation_failure(self):
|
||||
exc = OperationFailure(u'unicode \U0001f40d', 10,
|
||||
{"errmsg": u'unicode \U0001f40d'})
|
||||
exc = OperationFailure('unicode \U0001f40d', 10,
|
||||
{"errmsg": 'unicode \U0001f40d'})
|
||||
self._test_unicode_strs(exc)
|
||||
|
||||
def test_unicode_strs_not_master_error(self):
|
||||
exc = NotMasterError(u'unicode \U0001f40d',
|
||||
{"errmsg": u'unicode \U0001f40d'})
|
||||
exc = NotMasterError('unicode \U0001f40d',
|
||||
{"errmsg": 'unicode \U0001f40d'})
|
||||
self._test_unicode_strs(exc)
|
||||
|
||||
def assertPyMongoErrorEqual(self, exc1, exc2):
|
||||
|
||||
@ -30,9 +30,9 @@ from test.test_client import IntegrationTest
|
||||
|
||||
class TestRawBSONDocument(IntegrationTest):
|
||||
|
||||
# {u'_id': ObjectId('556df68b6e32ab21a95e0785'),
|
||||
# u'name': u'Sherlock',
|
||||
# u'addresses': [{u'street': u'Baker Street'}]}
|
||||
# {'_id': ObjectId('556df68b6e32ab21a95e0785'),
|
||||
# 'name': 'Sherlock',
|
||||
# 'addresses': [{'street': 'Baker Street'}]}
|
||||
bson_string = (
|
||||
b'Z\x00\x00\x00\x07_id\x00Um\xf6\x8bn2\xab!\xa9^\x07\x85\x02name\x00\t'
|
||||
b'\x00\x00\x00Sherlock\x00\x04addresses\x00&\x00\x00\x00\x030\x00\x1e'
|
||||
@ -99,8 +99,8 @@ class TestRawBSONDocument(IntegrationTest):
|
||||
self.assertEqual(raw_coll.find_one(), raw)
|
||||
|
||||
def test_with_codec_options(self):
|
||||
# {u'date': datetime.datetime(2015, 6, 3, 18, 40, 50, 826000),
|
||||
# u'_id': UUID('026fab8f-975f-4965-9fbf-85ad874c60ff')}
|
||||
# {'date': datetime.datetime(2015, 6, 3, 18, 40, 50, 826000),
|
||||
# '_id': UUID('026fab8f-975f-4965-9fbf-85ad874c60ff')}
|
||||
# encoded with JAVA_LEGACY uuid representation.
|
||||
bson_string = (
|
||||
b'-\x00\x00\x00\x05_id\x00\x10\x00\x00\x00\x03eI_\x97\x8f\xabo\x02'
|
||||
@ -143,8 +143,8 @@ class TestRawBSONDocument(IntegrationTest):
|
||||
|
||||
# Make sure that CodecOptions are preserved.
|
||||
# {'embedded': [
|
||||
# {u'date': datetime.datetime(2015, 6, 3, 18, 40, 50, 826000),
|
||||
# u'_id': UUID('026fab8f-975f-4965-9fbf-85ad874c60ff')}
|
||||
# {'date': datetime.datetime(2015, 6, 3, 18, 40, 50, 826000),
|
||||
# '_id': UUID('026fab8f-975f-4965-9fbf-85ad874c60ff')}
|
||||
# ]}
|
||||
# encoded with JAVA_LEGACY uuid representation.
|
||||
bson_string = (
|
||||
|
||||
@ -20,11 +20,7 @@ import sys
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
try:
|
||||
from urllib.parse import quote_plus
|
||||
except ImportError:
|
||||
# Python 2
|
||||
from urllib import quote_plus
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from pymongo import MongoClient, ssl_support
|
||||
from pymongo.errors import (ConfigurationError,
|
||||
|
||||
@ -890,16 +890,6 @@ def is_greenthread_patched():
|
||||
return gevent_monkey_patched() or eventlet_monkey_patched()
|
||||
|
||||
|
||||
def cdecimal_patched():
|
||||
"""Check if Python 2.7 cdecimal patching is active."""
|
||||
try:
|
||||
import decimal
|
||||
import cdecimal
|
||||
return decimal is cdecimal
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
|
||||
def disable_replication(client):
|
||||
"""Disable replication on all secondaries, requires MongoDB 3.2."""
|
||||
for host, port in client.secondaries:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user