File: developer-guide.rst

package info (click to toggle)
python-motor 3.7.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,572 kB
  • sloc: python: 12,252; javascript: 137; makefile: 74; sh: 8
file content (114 lines) | stat: -rw-r--r-- 4,955 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
===============
Developer Guide
===============

.. warning:: Motor will be deprecated on May 14th, 2026, one year after the production release of the PyMongo Async driver. Critical bug fixes will be made until May 14th, 2027.
  We strongly recommend that Motor users migrate to the PyMongo Async driver while Motor is still supported.
  To learn more, see `the migration guide <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/reference/migration/>`_.


Some explanations for those who would like to contribute to Motor development.

Compatibility
-------------

Motor supports the asyncio module in the standard library of Python 3.5.3 and
later.
Motor also works with Tornado 5.0 and later along with all the Python versions
it supports.

Each new Motor feature release depends on the latest PyMongo minor version release
or newer, up to the next PyMongo major version release. For example, if 3.10
is the latest available PyMongo version when Motor 2.1 is being released, Motor 2.1
will require 3.10<=PyMongo<4.

Frameworks
----------

Motor abstracts the differences between Tornado and asyncio by wrapping each in a "framework" interface.
A Motor framework is a module implementing these properties and functions:

- ``CLASS_PREFIX``
- ``add_future``
- ``call_soon``
- ``chain_future``
- ``chain_return_value``
- ``check_event_loop``
- ``coroutine`` (**DEPRECATED**)
- ``get_event_loop``
- ``get_future``
- ``is_event_loop``
- ``is_future``
- ``platform_info``
- ``pymongo_class_wrapper``
- ``run_on_executor``
- ``yieldable`` (**DEPRECATED**)

See the ``frameworks/tornado`` and ``frameworks/asyncio`` modules.

.. note:: Starting in Motor 2.2, the functions marked **DEPRECATED** in the
   list above are not used internally in Motor. Instead of being removed
   from the codebase, they have been left in a deprecated state to avoid
   breaking any libraries built on top of Motor. These deprecated functions
   may be removed in a future major release.

A framework-specific class, like ``MotorClient`` for Tornado or
``AsyncIOMotorClient`` for asyncio, is created by the
``create_class_with_framework`` function, which combines a framework with a
framework-agnostic class, in this case ``AgnosticClient``.

Wrapping PyMongo
----------------

For each PyMongo class, Motor declares an equivalent framework-agnostic class.
For example, the ``AgnosticClient`` class is a framework-agnostic equivalent to
PyMongo's ``MongoClient``. This agnostic class declares each method and property
of the PyMongo class that it intends to wrap. These methods and properties
begin life as type ``MotorAttributeFactory``.

When ``create_class_with_framework`` creates a framework-specific class from an
agnostic class, it creates methods and properties for that class which wrap the
equivalent PyMongo methods and properties.

For example, the ``AgnosticClient`` class declares that ``drop_database`` is an
``AsyncCommand``, which is a subclass of
``MotorAttributeFactory``. At import time, ``create_class_with_framework`` calls
the ``create_attribute`` method of each ``MotorAttributeFactory`` on the
``AgnosticClient``, which results in framework-specific implementations of each
method and property. So at import time, ``create_class_with_framework`` generates
framework-specific wrappers of ``drop_database`` for ``MotorClient`` and
``AsyncIOMotorClient``. These wrappers use framework-specific features to run the
``drop_database`` method asynchronously.

Asynchronization
----------------

This is the heart of Motor's implementation. The ``create_attribute`` method for
asynchronous methods like ``drop_database`` wraps the equivalent PyMongo method
in a Motor method. This wrapper method uses either the Tornado or asyncio
framework to:

- get a reference to the framework's event loop
- start the PyMongo method on a thread in the global ``ThreadPoolExecutor``
- create a ``Future`` that will be resolved by the event loop when the thread finishes
- returns the ``Future`` to the caller

This is what allows Tornado or asyncio awaitables to call Motor methods with
``await`` to await I/O without blocking the event loop.

Synchro
-------

A common kind of bug in Motor arises when PyMongo adds a feature, like a new
method or new optional behavior, which we forget to wrap with Motor.

Since PyMongo adds a test to its suite for each new feature, we could catch
these omissions by applying PyMongo's latest tests to Motor. Then a missing
method or feature would cause an obvious test failure. But PyMongo is
synchronous and Motor is async; how can Motor pass PyMongo's tests?

Synchro is a hacky little module that re-synchronizes all Motor methods using
the Tornado IOLoop's ``run_sync`` method. ``synchrotest.py`` overrides the Python
interpreter's import machinery to allow Synchro to masquerade as PyMongo, and
runs PyMongo's test suite against it. Use ``tox -e synchro`` to check out
PyMongo's test suite and run it with Synchro.