Compare commits

...

11 Commits
master ... r1.3

Author SHA1 Message Date
A. Jesse Jiryu Davis
26047379cf Version -> dev0 2018-07-11 14:21:40 -04:00
A. Jesse Jiryu Davis
24ec78a32b
BUMP 1.3.1
Signed-off-by: A. Jesse Jiryu Davis <jesse@mongodb.com>
2018-07-11 14:20:05 -04:00
A. Jesse Jiryu Davis
19e0ce7cbb MOTOR-248 Don't use "async" as variable name
In Python 3.7, "async" becomes a full-fledged keyword.
2018-07-11 14:10:57 -04:00
A. Jesse Jiryu Davis
da422872fd Version -> dev0 2018-07-11 03:26:18 -04:00
A. Jesse Jiryu Davis
dfe61f1aa0
BUMP 1.3.0
Signed-off-by: A. Jesse Jiryu Davis <jesse@mongodb.com>
2018-07-11 03:21:28 -04:00
A. Jesse Jiryu Davis
6c71039837 MOTOR-255 Change streams use new async iter style 2018-07-08 12:04:17 -07:00
A. Jesse Jiryu Davis
50006e30ba Requirements for Motor 1.3 and 2.0 2018-07-03 18:37:48 -07:00
A. Jesse Jiryu Davis
8bfa1ff03b Update extension for latest Sphinx 2018-07-02 14:39:17 -04:00
A. Jesse Jiryu Davis
a5a00a6357 Motor 2 migration guide 2018-07-02 14:38:49 -04:00
A. Jesse Jiryu Davis
226c98df6a Deprecate the old callback-based asynchronous API 2018-07-02 14:35:59 -04:00
A. Jesse Jiryu Davis
453d206382 Changelog entries for 1.2.x 2018-07-02 13:54:13 -04:00
17 changed files with 352 additions and 543 deletions

View File

@ -3,6 +3,40 @@ Changelog
.. currentmodule:: motor.motor_tornado .. currentmodule:: motor.motor_tornado
Motor 1.3.1
-----------
Fix a Python 3.7 compatibility bug caused by importing "async", which is a
keyword in Python 3.7. Drop support for Python 3.4.3 and older.
Motor 1.3.0
-----------
Deprecate Motor's old callback-based async API in preparation for removing it in
Motor 2.0. Raise ``DeprecationWarning`` whenever a callback is passed.
See the :doc:`migrate-to-motor-2`.
Motor 1.2.4
-----------
Fix a Python 3.7 compatibility bug in the :class:`MotorChangeStream` class
returned by :meth:`MotorCollection.watch`. It is now possible to use change
streams in ``async for`` loops in Python 3.7.
Motor 1.2.3
-----------
Compatibility with latest Sphinx and document how to use the latest TLS
protocols.
Motor 1.2.2
-----------
Motor 1.2.x requires PyMongo 3.6 or later. The dependency was properly
documented, but not enforced in ``setup.py``. PyMongo 3.6 is now an install-time
requirement; thanks to Shane Harvey for the fix.
Motor 1.2.1 Motor 1.2.1
----------- -----------
@ -124,7 +158,7 @@ Motor 1.0
--------- ---------
Motor now depends on PyMongo 3.3 and later. The move from PyMongo 2 to 3 brings Motor now depends on PyMongo 3.3 and later. The move from PyMongo 2 to 3 brings
a large number of API changes, read :doc:`migrate-to-motor-1` and a large number of API changes, read
`the PyMongo 3 changelog`_ carefully. `the PyMongo 3 changelog`_ carefully.
.. _the PyMongo 3 changelog: http://api.mongodb.com/python/current/changelog.html#changes-in-version-3-0 .. _the PyMongo 3 changelog: http://api.mongodb.com/python/current/changelog.html#changes-in-version-3-0
@ -335,8 +369,7 @@ This version updates the PyMongo dependency from 2.8.0 to 2.9.x, and wraps
PyMongo 2.9's new APIs. PyMongo 2.9's new APIs.
Most of Motor 1.0's API is now implemented, and APIs that will be removed in Most of Motor 1.0's API is now implemented, and APIs that will be removed in
Motor 1.0 are now deprecated and raise warnings. See the Motor 1.0 are now deprecated and raise warnings.
:doc:`/migrate-to-motor-1` to prepare your code for Motor 1.0.
:class:`MotorClient` changes :class:`MotorClient` changes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -62,7 +62,7 @@ Contents
tutorial-asyncio tutorial-asyncio
examples/index examples/index
changelog changelog
migrate-to-motor-1 migrate-to-motor-2
developer-guide developer-guide
contributors contributors

View File

@ -1,400 +0,0 @@
Motor 1.0 Migration Guide
=========================
.. currentmodule:: motor.motor_tornado
Motor 1.0 brings a number of backward breaking changes to the pre-1.0 API.
Follow this guide to migrate an existing application that had used an older
version of Motor.
Removed features with no migration path
---------------------------------------
:class:`MotorReplicaSetClient` is removed
..........................................
In Motor 1.0, :class:`MotorClient` is the only class. Connect to a replica set with
a "replicaSet" URI option or parameter::
MotorClient("mongodb://localhost/?replicaSet=my-rs")
MotorClient(host, port, replicaSet="my-rs")
The "compile_re" option is removed
..................................
In Motor 1.0 regular expressions are never compiled to Python `re.match`
objects.
Motor 0.7
---------
The first step in migrating to Motor 1.0 is to upgrade to at least Motor 0.7.
If your project has a
requirements.txt file, add the line::
motor >= 0.7, < 1.0
Most of the key new
methods and options from Motor 1.0 are backported in Motor 0.7 making
migration much easier.
Enable Deprecation Warnings
---------------------------
Starting with Motor 0.7, :exc:`DeprecationWarning` is raised by most methods
removed in Motor 1.0. Make sure you enable runtime warnings to see
where deprecated functions and methods are being used in your application::
python -Wd <your application>
Warnings can also be changed to errors::
python -Wd -Werror <your application>
Not all deprecated features raise `DeprecationWarning` when
used. For example, `~motor.motor_tornado.MotorReplicaSetClient` will be
removed in Motor 1.0 but it does not raise `DeprecationWarning`
in Motor 0.7. See also `Removed features with no migration path`_.
CRUD API
--------
Changes to find() and find_one()
................................
"spec" renamed "filter"
~~~~~~~~~~~~~~~~~~~~~~~
The ``spec`` option has been renamed to ``filter``. Code like this::
cursor = collection.find(spec={"a": 1})
can be changed to this with Motor 0.7 or later::
cursor = collection.find(filter={"a": 1})
or this with any version of Motor::
cursor = collection.find({"a": 1})
"fields" renamed "projection"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``fields`` option has been renamed to ``projection``. Code like this::
cursor = collection.find({"a": 1}, fields={"_id": False})
can be changed to this with Motor 0.7 or later::
cursor = collection.find({"a": 1}, projection={"_id": False})
or this with any version of Motor::
cursor = collection.find({"a": 1}, {"_id": False})
"partial" renamed "allow_partial_results"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``partial`` option has been renamed to ``allow_partial_results``. Code like
this::
cursor = collection.find({"a": 1}, partial=True)
can be changed to this with Motor 0.7 or later::
cursor = collection.find({"a": 1}, allow_partial_results=True)
"timeout" replaced by "no_cursor_timeout"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``timeout`` option has been replaced by ``no_cursor_timeout``. Code like this::
cursor = collection.find({"a": 1}, timeout=False)
can be changed to this with Motor 0.7 or later::
cursor = collection.find({"a": 1}, no_cursor_timeout=True)
"snapshot" and "max_scan" replaced by "modifiers"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``snapshot`` and ``max_scan`` options have been removed. They can now be set,
along with other $ query modifiers, through the ``modifiers`` option. Code like
this::
cursor = collection.find({"a": 1}, snapshot=True)
can be changed to this with Motor 0.7 or later::
cursor = collection.find({"a": 1}, modifiers={"$snapshot": True})
or with any version of Motor::
cursor = collection.find({"$query": {"a": 1}, "$snapshot": True})
"network_timeout" is removed
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``network_timeout`` option has been removed. This option was always the
wrong solution for timing out long running queries and should never be used
in production. Starting with **MongoDB 2.6** you can use the $maxTimeMS query
modifier. Code like this::
# Set a 5 second select() timeout.
cursor = collection.find({"a": 1}, network_timeout=5)
can be changed to this with Motor 0.7 or later::
# Set a 5 second (5000 millisecond) server side query timeout.
cursor = collection.find({"a": 1}, modifiers={"$maxTimeMS": 5000})
or with any version of Motor::
cursor = collection.find({"$query": {"a": 1}, "$maxTimeMS": 5000})
.. seealso:: `$maxTimeMS
<http://docs.mongodb.org/manual/reference/operator/meta/maxTimeMS/>`_
Tailable cursors
~~~~~~~~~~~~~~~~
The ``tailable`` and ``await_data`` options have been replaced by ``cursor_type``.
Code like this::
cursor = collection.find({"a": 1}, tailable=True)
cursor = collection.find({"a": 1}, tailable=True, await_data=True)
can be changed to this with Motor 0.7 or later::
from pymongo import CursorType
cursor = collection.find({"a": 1}, cursor_type=CursorType.TAILABLE)
cursor = collection.find({"a": 1}, cursor_type=CursorType.TAILABLE_AWAIT)
Other removed options
~~~~~~~~~~~~~~~~~~~~~
The ``read_preference``, ``tag_sets``,
and ``secondary_acceptable_latency_ms`` options have been removed. See the `Read
Preferences`_ section for solutions.
Read Preferences
----------------
The "read_preference" attribute is immutable
............................................
Code like this::
from pymongo import ReadPreference
db = client.my_database
db.read_preference = ReadPreference.SECONDARY
can be changed to this with Motor 0.7 or later::
db = client.get_database("my_database",
read_preference=ReadPreference.SECONDARY)
Code like this::
cursor = collection.find({"a": 1},
read_preference=ReadPreference.SECONDARY)
can be changed to this with Motor 0.7 or later::
coll2 = collection.with_options(read_preference=ReadPreference.SECONDARY)
cursor = coll2.find({"a": 1})
.. seealso:: :meth:`~MotorDatabase.get_collection`
The "tag_sets" option and attribute are removed
...............................................
The ``tag_sets`` MotorClient option is removed. The ``read_preference``
option can be used instead. Code like this::
client = MotorClient(
read_preference=ReadPreference.SECONDARY,
tag_sets=[{"dc": "ny"}, {"dc": "sf"}])
can be changed to this with Motor 0.7 or later::
from pymongo.read_preferences import Secondary
client = MotorClient(read_preference=Secondary([{"dc": "ny"}]))
To change the tags sets for a MotorDatabase or MotorCollection, code like this::
db = client.my_database
db.read_preference = ReadPreference.SECONDARY
db.tag_sets = [{"dc": "ny"}]
can be changed to this with Motor 0.7 or later::
db = client.get_database("my_database",
read_preference=Secondary([{"dc": "ny"}]))
Code like this::
cursor = collection.find(
{"a": 1},
read_preference=ReadPreference.SECONDARY,
tag_sets=[{"dc": "ny"}])
can be changed to this with Motor 0.7 or later::
from pymongo.read_preferences import Secondary
coll2 = collection.with_options(
read_preference=Secondary([{"dc": "ny"}]))
cursor = coll2.find({"a": 1})
.. seealso:: :meth:`~MotorDatabase.get_collection`
The "secondary_acceptable_latency_ms" option and attribute are removed
......................................................................
Motor 0.x supports ``secondary_acceptable_latency_ms`` as an option to methods
throughout the driver, but mongos only supports a global latency option.
Motor 1.0 has changed to match the behavior of mongos, allowing migration
from a single server, to a replica set, to a sharded cluster without a
surprising change in server selection behavior. A new option,
``localThresholdMS``, is available through MotorClient and should be used in
place of ``secondaryAcceptableLatencyMS``. Code like this::
client = MotorClient(readPreference="nearest",
secondaryAcceptableLatencyMS=100)
can be changed to this with Motor 0.7 or later::
client = MotorClient(readPreference="nearest",
localThresholdMS=100)
Write Concern
-------------
The "write_concern" attribute is immutable
..........................................
The ``write_concern`` attribute is immutable in Motor 1.0. Code like this::
client = MotorClient()
client.write_concern = {"w": "majority"}
can be changed to this with any version of Motor::
client = MotorClient(w="majority")
Code like this::
db = client.my_database
db.write_concern = {"w": "majority"}
can be changed to this with Motor 0.7 or later::
from pymongo import WriteConcern
db = client.get_database("my_database",
write_concern=WriteConcern(w="majority"))
The new CRUD API write methods do not accept write concern options. Code like
this::
oid = collection.insert({"a": 2}, w="majority")
can be changed to this with Motor 0.7 or later::
from pymongo import WriteConcern
coll2 = collection.with_options(
write_concern=WriteConcern(w="majority"))
oid = coll2.insert({"a": 2})
.. seealso:: :meth:`~MotorDatabase.get_collection`
Codec Options
-------------
The "document_class" attribute is removed
.........................................
Code like this::
from bson.son import SON
client = MotorClient()
client.document_class = SON
can be replaced by this in any version of Motor::
from bson.son import SON
client = MotorClient(document_class=SON)
or to change the ``document_class`` for a :class:`MotorDatabase`
with Motor 0.7 or later::
from bson.codec_options import CodecOptions
from bson.son import SON
db = client.get_database("my_database", CodecOptions(SON))
.. seealso:: :meth:`~MotorDatabase.get_collection` and
:meth:`~MotorCollection.with_options`
The "uuid_subtype" option and attribute are removed
...................................................
Code like this::
from bson.binary import JAVA_LEGACY
db = client.my_database
db.uuid_subtype = JAVA_LEGACY
can be replaced by this with Motor 0.7 or later::
from bson.binary import JAVA_LEGACY
from bson.codec_options import CodecOptions
db = client.get_database("my_database",
CodecOptions(uuid_representation=JAVA_LEGACY))
.. seealso:: :meth:`~MotorDatabase.get_collection` and
:meth:`~MotorCollection.with_options`
MotorClient
-----------
The ``open`` method
...................
The :meth:`~MotorClient.open` method is removed in Motor 1.0.
Motor clients have opened themselves on demand since Motor 0.2.
The max_pool_size parameter is removed
......................................
Motor 1.0 replaced the max_pool_size parameter with support for the MongoDB URI
``maxPoolSize`` option. Code like this::
client = MotorClient(max_pool_size=10)
can be replaced by this with Motor 0.7 or later::
client = MotorClient(maxPoolSize=10)
client = MotorClient("mongodb://localhost:27017/?maxPoolSize=10")
The "disconnect" method is removed
..................................
Code like this::
client.disconnect()
can be replaced by this::
client.close()
The host and port attributes are removed
........................................
Code like this::
host = client.host
port = client.port
can be replaced by this with Motor 0.7 or later::
address = client.address
host, port = address or (None, None)

206
doc/migrate-to-motor-2.rst Normal file
View File

@ -0,0 +1,206 @@
Motor 2.0 Migration Guide
=========================
.. currentmodule:: motor.motor_tornado
Motor 2.0 brings a number of changes to Motor 1.0's API. The major version is
required in order to update the session API to support multi-document
transactions, introduced in MongoDB 4.0; this feature is so valuable that it
motivated me to make the breaking change and bump the version number to 2.0.
Since this is the first major version number in almost two years, it removes a
large number of APIs that have been deprecated in the time since Motor 1.0.
Follow this guide to migrate an existing application that had used Motor 1.x.
Check compatibility
-------------------
Read the :doc:`requirements` page and ensure your MongoDB server and Python
interpreter are compatible, and your Tornado version if you are using Tornado.
If you use aiohttp, upgrade to at least 3.0.
Upgrade to Motor 1.3
--------------------
The first step in migrating to Motor 2.0 is to upgrade to at least Motor 1.3.
If your project has a
requirements.txt file, add the line::
motor >= 1.3, < 2.0
Enable Deprecation Warnings
---------------------------
Starting with Motor 1.3, :exc:`DeprecationWarning` is raised by most methods
removed in Motor 2.0. Make sure you enable runtime warnings to see where
deprecated functions and methods are being used in your application::
python -Wd <your application>
Warnings can also be changed to errors::
python -Wd -Werror <your application>
Migrate from deprecated APIs
----------------------------
The following features are deprecated by PyMongo and scheduled for removal;
they are now deleted from Motor:
- ``MotorClient.kill_cursors`` and ``close_cursor``.
Allow :class:`MotorCursor` to handle its own cleanup.
- ``MotorClient.get_default_database``. Call :meth:`MotorClient.get_database`
with a database name of ``None`` for the same effect.
- ``MotorDatabase.add_son_manipulator``. Transform documents to and from
their MongoDB representations in your application code instead.
- The server command ``getLastError`` and related commands are deprecated,
their helper functions are deleted from Motor:
- ``MotorDatabase.last_status``
- ``MotorDatabase.error``
- ``MotorDatabase.previous_error``
- ``MotorDatabase.reset_error_history``
Use acknowledged writes and rely on Motor to raise exceptions.
- The server command ``parallelCollectionScan`` is deprecated and
``MotorCollection.parallel_scan`` is removed. Use a regular
:meth:`MotorCollection.find` cursor.
- ``MotorClient.database_names``. Use
:meth:`~MotorClient.list_database_names`.
- ``MotorDatabase.eval``. The server command is deprecated but
still available with ``MotorDatabase.command("eval", ...)``.
- ``MotorDatabase.group``. The server command is deprecated but
still available with ``MotorDatabase.command("group", ...)``.
- ``MotorDatabase.authenticate`` and ``MotorDatabase.logout``. Add credentials
to the URI or ``MotorClient`` options instead of calling ``authenticate``.
To authenticate as multiple users on the same database, instead of using
``authenticate`` and ``logout`` use a separate client for each user.
- ``MotorCollection.initialize_unordered_bulk_op``,
``initialize_unordered_bulk_op``, and ``MotorBulkOperationBuilder``. Use
:meth:`MotorCollection.bulk_write``, see :ref:`Bulk Writes Tutorial
<bulk-write-tutorial>`.
- ``MotorCollection.count``. Use :meth:`~MotorCollection.count_documents` or
:meth:`~MotorCollection.estimated_document_count`.
- ``MotorCollection.ensure_index``. Use
:meth:`MotorCollection.create_indexes`.
- Deprecated write methods have been deleted from :class:`MotorCollection`.
- ``save`` and ``insert``: Use :meth:`~MotorCollection.insert_one` or
:meth:`~MotorCollection.insert_many`.
- ``update``: Use :meth:`~MotorCollection.update_one`,
:meth:`~MotorCollection.update_many`, or
:meth:`~MotorCollection.replace_one`.
- ``remove``: Use :meth:`~MotorCollection.delete_one` or
:meth:`~MotorCollection.delete_many`.
- ``find_and_modify``: Use :meth:`~MotorCollection.find_one_and_update`,
:meth:`~MotorCollection.find_one_and_replace`, or
:meth:`~MotorCollection.find_one_and_delete`.
- ``MotorCursor.count`` and ``MotorGridOutCursor.count``. Use
:meth:`MotorCollection.count_documents` or
:meth:`MotorCollection.estimated_document_count`.
Migrate from the original callback API
--------------------------------------
Motor was first released before Tornado had introduced Futures, generator-based
coroutines, and the ``yield`` syntax, and long before the async features
developed during Python 3's career. Therefore Motor's original asynchronous API
used callbacks:
.. code-block:: python3
def callback(result, error):
if error:
print(error)
else:
print(result)
collection.find_one({}, callback=callback)
Callbacks have been largely superseded by a Futures API intended for use with
coroutines, see :doc:`tutorial-tornado`. You can still use callbacks with Motor when
appropriate but you must add the callback to a Future instead of passing it as
a parameter:
.. code-block:: python3
def callback(future):
try:
result = future.result()
print(result)
except Exception as exc:
print(exc)
future = collection.find_one({})
future.add_done_callback(callback)
The :meth:`~asyncio.Future.add_done_callback` call can be placed on the same
line:
.. code-block:: python3
collection.find_one({}).add_done_callback(callback)
In almost all cases the modern coroutine API is more readable and provides
better exception handling:
.. code-block:: python3
async def do_find():
try:
result = await collection.find_one({})
print(result)
except Exception as exc:
print(exc)
Upgrade to Motor 2.0
--------------------
Once your application runs without deprecation warnings with Motor 1.3, upgrade
to Motor 2.0. Update any calls in your code to
:meth:`MotorClient.start_session` or
:meth:`~pymongo.client_session.ClientSession.end_session` to handle the
following change.
:meth:`MotorClient.start_session` is a coroutine
------------------------------------------------
In the past, you could use a client session like:
.. code-block:: python3
session = client.start_session()
doc = await client.db.collection.find_one({}, session=session)
session.end_session()
Or:
.. code-block:: python3
with client.start_session() as session:
doc = client.db.collection.find_one({}, session=session)
To support multi-document transactions, in Motor 2.0
:meth:`MotorClient.start_session` is a coroutine, not a regular method. It must
be used like ``await client.start_session()`` or ``async with await
client.start_session()``. The coroutine now returns a new class
:class:`~motor.motor_tornado.MotorClientSession`, not PyMongo's
:class:`~pymongo.client_session.ClientSession`. The ``end_session`` method on
the returned :class:`~motor.motor_tornado.MotorClientSession` is also now a
coroutine instead of a regular method. Use it like:
.. code-block:: python3
session = await client.start_session()
doc = await client.db.collection.find_one({}, session=session)
await session.end_session()
Or:
.. code-block:: python3
async with client.start_session() as session:
doc = await client.db.collection.find_one({}, session=session)

View File

@ -1,4 +1,4 @@
# Copyright 2009-2015 MongoDB, Inc. # Copyright 2009-present MongoDB, Inc.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -15,8 +15,8 @@
"""MongoDB specific extensions to Sphinx.""" """MongoDB specific extensions to Sphinx."""
from docutils import nodes from docutils import nodes
from docutils.parsers import rst
from sphinx import addnodes from sphinx import addnodes
from sphinx.util.compat import Directive
class mongodoc(nodes.Admonition, nodes.Element): class mongodoc(nodes.Admonition, nodes.Element):
@ -28,7 +28,7 @@ class mongoref(nodes.reference):
def visit_mongodoc_node(self, node): def visit_mongodoc_node(self, node):
self.visit_admonition(node) self.visit_admonition(node, "seealso")
def depart_mongodoc_node(self, node): def depart_mongodoc_node(self, node):
@ -48,7 +48,7 @@ def depart_mongoref_node(self, node):
self.body.append('\n') self.body.append('\n')
class MongodocDirective(Directive): class MongodocDirective(rst.Directive):
has_content = True has_content = True
required_arguments = 0 required_arguments = 0
@ -58,7 +58,7 @@ class MongodocDirective(Directive):
def run(self): def run(self):
node = mongodoc() node = mongodoc()
title = 'See general MongoDB documentation' title = 'The MongoDB documentation on'
node += nodes.title(title, title) node += nodes.title(title, title)
self.state.nested_parse(self.content, self.content_offset, node) self.state.nested_parse(self.content, self.content_offset, node)
return [node] return [node]
@ -94,6 +94,4 @@ def setup(app):
html=(visit_mongoref_node, depart_mongoref_node)) html=(visit_mongoref_node, depart_mongoref_node))
app.add_directive("mongodoc", MongodocDirective) app.add_directive("mongodoc", MongodocDirective)
app.connect("doctree-resolved", process_mongodoc_nodes) app.connect("doctree-resolved", process_mongodoc_nodes)
return {'parallel_write_safe': True, 'parallel_read_safe': True}

View File

@ -36,67 +36,38 @@ Compatibility Matrix
Motor and PyMongo Motor and PyMongo
````````````````` `````````````````
Older versions of Motor depended on exact PyMongo versions. Version 0.7 requires
the latest PyMongo 2.9.x release beginning with 2.9.4, Version 1.0 works
with any PyMongo version beginning with 3.3.0, and Version 1.1 works with any
PyMongo version beginning with 3.4.0.
+-------------------+-----------------+ +-------------------+-----------------+
| Motor Version | PyMongo Version | | Motor Version | PyMongo Version |
+===================+=================+ +===================+=================+
| 0.1 | 2.5.0 |
+-------------------+-----------------+
| 0.2 | 2.7.0 |
+-------------------+-----------------+
| 0.3 | 2.7.1 |
+-------------------+-----------------+
| 0.4 | 2.8.0 |
+-------------------+-----------------+
| 0.5 | 2.8.0 |
+-------------------+-----------------+
| 0.6 | 2.8.0 |
+-------------------+-----------------+
| 0.7 | 2.9.4+ |
+-------------------+-----------------+
| 1.0 | 3.3+ | | 1.0 | 3.3+ |
+-------------------+-----------------+ +-------------------+-----------------+
| 1.1 | 3.4+ | | 1.1 | 3.4+ |
+-------------------+-----------------+ +-------------------+-----------------+
| 1.2 | 3.6+ | | 1.2 | 3.6+ |
+-------------------+-----------------+ +-------------------+-----------------+
| 1.3 | 3.6+ |
+-------------------+-----------------+
| 2.0 | 3.7+ |
+-------------------+-----------------+
Motor and MongoDB Motor and MongoDB
````````````````` `````````````````
All Motor versions are usable with all MongoDB versions as old as 2.2. +---------------------------------------------------------+-----+-----+
Where "N" appears there are some incompatibilities and | MongoDB Version | | |
unsupported server features. +=====================+=====+=====+=====+=====+=====+=====+=====+=====+
| | 2.2 | 2.4 | 2.6 | 3.0 | 3.2 | 3.4 | 3.6 | 4.0 |
+---------------------------------------------------------+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| MongoDB Version | | Motor Version | 1.0 | Y | Y | Y | Y | Y |**N**|**N**|**N**|
+=====================+=====+=====+=====+=====+=====+=====+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.2 | 2.4 | 2.6 | 3.0 | 3.2 | 3.4 | | | 1.1 | Y | Y | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| Motor Version | 0.1 | Y | Y |**N**|**N**|**N**|**N**| | | 1.2 |**N**|**N**| Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 0.2 | Y | Y | Y |**N**|**N**|**N**| | | 1.3 |**N**|**N**| Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 0.3 | Y | Y | Y |**N**|**N**|**N**| | | 2.0 |**N**|**N**|**N**| Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 0.4 | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+
| | 0.5 | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+
| | 0.6 | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+
| | 0.7 | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+
| | 1.0 | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+
| | 1.1 | Y | Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+
| | 1.2 |**N**|**N**| Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+
There is no relationship between PyMongo and MongoDB version numbers, although There is no relationship between PyMongo and MongoDB version numbers, although
the numbers happen to be close or equal in recent releases of PyMongo and MongoDB. the numbers happen to be close or equal in recent releases of PyMongo and MongoDB.
@ -112,70 +83,44 @@ Motor and Tornado
Where "N" appears in this matrix, the versions of Motor and Tornado are Where "N" appears in this matrix, the versions of Motor and Tornado are
known to be incompatible, or have not been tested together. known to be incompatible, or have not been tested together.
+---------------------------------+ +---------------------------------------+
| Tornado Version | | Tornado Version |
+=====================+=====+=====+ +=====================+=====+=====+=====+
| | 3.x | 4.x | | | 3.x | 4.x | 5.x |
+---------------+-----+-----+-----+ +---------------+-----+-----+-----+-----+
| Motor Version | 0.1 | Y |**N**| | Motor Version | 1.0 | Y | Y |**N**|
+---------------+-----+-----+-----+ +---------------+-----+-----+-----+-----+
| | 0.2 | Y | Y | | | 1.1 | Y | Y |**N**|
+---------------+-----+-----+-----+ +---------------+-----+-----+-----+-----+
| | 0.3 | Y | Y | | | 1.2 |**N**| Y |**N**|
+---------------+-----+-----+-----+ +---------------+-----+-----+-----+-----+
| | 0.4 | Y | Y | | | 1.3 |**N**| Y |**N**|
+---------------+-----+-----+-----+ +---------------+-----+-----+-----+-----+
| | 0.5 | Y | Y | | | 2.0 |**N**| Y | Y |
+---------------+-----+-----+-----+ +---------------+-----+-----+-----+-----+
| | 0.6 | Y | Y |
+---------------+-----+-----+-----+
| | 0.7 | Y | Y |
+---------------+-----+-----+-----+
| | 1.0 | Y | Y |
+---------------+-----+-----+-----+
| | 1.1 | Y | Y |
+---------------+-----+-----+-----+
| | 1.2 |**N**| Y |
+---------------+-----+-----+-----+
Motor and Python Motor and Python
```````````````` ````````````````
Until version 0.5, Motor required Tornado, and it supported the same version of Motor 1.2 dropped support for the short-lived version of
Python as its supported Tornado versions did.
Beginning in version 0.5, Motor integrates with asyncio or Tornado.
Beginning in version 0.5, supports the "async for" syntax with cursors in
Python 3.5 and later. Motor 1.2 dropped support for the short-lived version of
the "async for" protocol implemented in Python 3.5.0 and 3.5.1. Motor continues the "async for" protocol implemented in Python 3.5.0 and 3.5.1. Motor continues
to work with "async for" loops in Python 3.5.2 and later. to work with "async for" loops in Python 3.5.2 and later.
+-------------------------------------------------------------------------+ +-------------------------------------------------------------------------------+
| Python Version | | Python Version |
+=====================+=====+=====+=====+=====+=====+=======+=======+=====+ +=====================+=====+=====+=====+=====+=====+=======+=======+=====+=====+
| | 2.5 | 2.6 | 2.7 | 3.3 | 3.4 | 3.5.0 | 3.5.2 | 3.6 | | | 2.5 | 2.6 | 2.7 | 3.3 | 3.4 | 3.5.0 | 3.5.2 | 3.6 | 3.7 |
+---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+ +---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+-----+
| Motor Version | 0.1 | Y | Y | Y | Y |**N**|**N** |**N** |**N**| | Motor Version | 1.0 |**N**| Y | Y | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+ +---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+-----+
| | 0.2 |**N**| Y | Y | Y |**N**|**N** |**N** |**N**| | | 1.1 |**N**| Y | Y | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+ +---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+-----+
| | 0.3 |**N**| Y | Y | Y | Y |**N** |**N** |**N**| | | 1.2 |**N**|**N**| Y |**N**| Y |**N** | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+ +---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+-----+
| | 0.4 |**N**| Y | Y | Y | Y |**N** |**N** |**N**| | | 1.3 |**N**|**N**| Y |**N**| Y |**N** | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+ +---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+-----+
| | 0.5 |**N**| Y | Y | Y | Y | Y | Y |**N**| | | 2.0 |**N**|**N**| Y |**N**| Y |**N** | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+ +---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+-----+
| | 0.6 |**N**| Y | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+
| | 0.7 |**N**| Y | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+
| | 1.0 |**N**| Y | Y | Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+
| | 1.1 |**N**| Y | Y | Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+
| | 1.2 |**N**|**N**| Y |**N**| Y |**N** | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-------+-------+-----+
.. _asyncio package from PyPI: https://pypi.python.org/pypi/asyncio .. _asyncio package from PyPI: https://pypi.python.org/pypi/asyncio

View File

@ -20,7 +20,7 @@ import pymongo
from motor.motor_py3_compat import text_type from motor.motor_py3_compat import text_type
version_tuple = (1, 3, 'dev0') version_tuple = (1, 3, 2, 'dev0')
def get_version_string(): def get_version_string():

View File

@ -1336,7 +1336,7 @@ class AgnosticChangeStream(AgnosticBase):
if PY35: if PY35:
exec(textwrap.dedent(""" exec(textwrap.dedent("""
async def __aiter__(self): def __aiter__(self):
return self return self
__anext__ = next __anext__ = next

View File

@ -26,11 +26,6 @@ import functools
import multiprocessing import multiprocessing
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
try:
from asyncio import ensure_future
except ImportError:
from asyncio import async as ensure_future
CLASS_PREFIX = 'AsyncIO' CLASS_PREFIX = 'AsyncIO'

View File

@ -21,6 +21,7 @@ See "Frameworks" in the Developer Guide.
import functools import functools
import os import os
import warnings
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
import tornado.process import tornado.process
@ -89,6 +90,10 @@ def future_or_callback(future, callback, io_loop, return_value=_DEFAULT):
internally. internally.
""" """
if callback: if callback:
warnings.warn(
'Motor\'s callback interface is deprecated, see the "Migrating'
' to Motor 2.0" guide', DeprecationWarning, stacklevel=2)
if not callable(callback): if not callable(callback):
raise callback_type_error raise callback_type_error

View File

@ -152,7 +152,7 @@ if sys.version_info[0] >= 3:
packages.append('motor.aiohttp') packages.append('motor.aiohttp')
setup(name='motor', setup(name='motor',
version='1.3.dev0', version='1.3.2.dev0',
packages=packages, packages=packages,
description=description, description=description,
long_description=long_description, long_description=long_description,

View File

@ -16,8 +16,10 @@ from __future__ import unicode_literals
"""Test Motor, an asynchronous driver for MongoDB and Tornado.""" """Test Motor, an asynchronous driver for MongoDB and Tornado."""
import contextlib
import logging import logging
import unittest import unittest
import warnings
from unittest import SkipTest from unittest import SkipTest
from test.test_environment import env, db_user, CLIENT_PEM from test.test_environment import env, db_user, CLIENT_PEM
@ -77,3 +79,13 @@ class MockRequestHandler(object):
def flush(self): def flush(self):
pass pass
@contextlib.contextmanager
def assert_deprecation_warnings(n=1):
with warnings.catch_warnings(record=True) as records:
warnings.filterwarnings('always', category=DeprecationWarning)
yield
assert len(records) == n, \
"Expected %d DeprecationWarning, not %r" % (n, records,)

View File

@ -20,13 +20,9 @@ import gc
import inspect import inspect
import os import os
import unittest import unittest
from asyncio import ensure_future
from unittest import SkipTest from unittest import SkipTest
try:
from asyncio import ensure_future
except ImportError:
from asyncio import async as ensure_future
from mockupdb import MockupDB from mockupdb import MockupDB
from motor import motor_asyncio from motor import motor_asyncio

View File

@ -153,7 +153,11 @@ class TestAsyncIOCursor(AsyncIOMockServerTestCase):
# Done iterating. # Done iterating.
future.set_result(True) future.set_result(True)
cursor.each(callback) with warnings.catch_warnings():
# Should not raise, and not deprecated.
warnings.filterwarnings('error')
cursor.each(callback)
yield from future yield from future
expected = [{'_id': i} for i in range(200)] expected = [{'_id': i} for i in range(200)]
self.assertEqual(expected, results) self.assertEqual(expected, results)

View File

@ -142,8 +142,10 @@ class MotorCollectionTest(MotorTest):
def test_save_callback(self): def test_save_callback(self):
yield self.collection.save({}, callback=None) yield self.collection.save({}, callback=None)
# Should not raise # PyMongo deprecated "save", Motor deprecated the callback API used by
(result, error), _ = yield gen.Task(self.collection.save, {}) # gen.Task.
with test.assert_deprecation_warnings(2):
(result, error), _ = yield gen.Task(self.collection.save, {})
if error: if error:
raise error raise error
@ -399,7 +401,9 @@ class MotorCollectionTest(MotorTest):
else: else:
future.set_result(result) future.set_result(result)
cursor.to_list(collection_size, callback=cb) with test.assert_deprecation_warnings():
cursor.to_list(collection_size, callback=cb)
docs = yield future docs = yield future
self.assertAllDocs(expected_sum, docs) self.assertAllDocs(expected_sum, docs)

View File

@ -32,7 +32,7 @@ from pymongo.errors import OperationFailure
import motor import motor
import motor.motor_tornado import motor.motor_tornado
from test import SkipTest, env from test import SkipTest, env, assert_deprecation_warnings
from test.tornado_tests import (get_command_line, from test.tornado_tests import (get_command_line,
MotorTest, MotorTest,
MotorMockServerTest, MotorMockServerTest,
@ -182,7 +182,11 @@ class MotorCursorTest(MotorMockServerTest):
# Done iterating. # Done iterating.
future.set_result(True) future.set_result(True)
cursor.each(callback) with warnings.catch_warnings():
# Should not raise, and not deprecated.
warnings.filterwarnings('error')
cursor.each(callback)
yield future yield future
expected = [{'_id': i} for i in range(200)] expected = [{'_id': i} for i in range(200)]
self.assertEqual(expected, results) self.assertEqual(expected, results)
@ -205,11 +209,15 @@ class MotorCursorTest(MotorMockServerTest):
cursor = self.collection.find({}, {'_id': 1}) cursor = self.collection.find({}, {'_id': 1})
cursor.sort([('_id', pymongo.ASCENDING)]) cursor.sort([('_id', pymongo.ASCENDING)])
expected = [{'_id': i} for i in range(200)] expected = [{'_id': i} for i in range(200)]
(result, error), _ = yield gen.Task(cursor.to_list, length=1000) with assert_deprecation_warnings():
(result, error), _ = yield gen.Task(cursor.to_list, length=1000)
self.assertEqual(expected, result) self.assertEqual(expected, result)
cursor = self.collection.find().where('return foo') cursor = self.collection.find().where('return foo')
(result, error), _ = yield gen.Task(cursor.to_list, length=1000) with assert_deprecation_warnings():
(result, error), _ = yield gen.Task(cursor.to_list, length=1000)
self.assertEqual(None, result) self.assertEqual(None, result)
self.assertTrue(isinstance(error, OperationFailure)) self.assertTrue(isinstance(error, OperationFailure))

View File

@ -28,6 +28,7 @@ from tornado.testing import gen_test
import motor import motor
from motor.motor_py3_compat import StringIO from motor.motor_py3_compat import StringIO
from test import assert_deprecation_warnings
from test.tornado_tests import MotorTest from test.tornado_tests import MotorTest
@ -147,13 +148,15 @@ class MotorGridfsTest(MotorTest):
@gen_test @gen_test
def test_put_callback(self): def test_put_callback(self):
(oid, error), _ = yield gen.Task(self.fs.put, b"hello") with assert_deprecation_warnings():
self.assertTrue(isinstance(oid, ObjectId)) (oid, error), _ = yield gen.Task(self.fs.put, b"hello")
self.assertEqual(None, error) self.assertTrue(isinstance(oid, ObjectId))
self.assertEqual(None, error)
(result, error), _ = yield gen.Task(self.fs.put, b"hello", _id=oid) with assert_deprecation_warnings():
self.assertEqual(None, result) (result, error), _ = yield gen.Task(self.fs.put, b"hello", _id=oid)
self.assertTrue(isinstance(error, FileExists)) self.assertEqual(None, result)
self.assertTrue(isinstance(error, FileExists))
@gen_test @gen_test
def test_put_duplicate(self): def test_put_duplicate(self):