MOTOR-1210 Update pre-commit to match PyMongo Checks (#232)
* MOTOR-1210 Update pre-commit to match PyMongo Checks * update doctests
This commit is contained in:
parent
684279ed0a
commit
8f71800c4a
@ -1,12 +1,13 @@
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-case-conflict
|
||||
- id: check-toml
|
||||
- id: check-yaml
|
||||
exclude: template.yaml
|
||||
- id: debug-statements
|
||||
- id: end-of-file-fixer
|
||||
exclude: WHEEL
|
||||
@ -16,34 +17,74 @@ repos:
|
||||
exclude: .patch
|
||||
exclude_types: [json]
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
files: \.(py|pyi)$
|
||||
args: [--line-length=100]
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.1.1
|
||||
rev: v0.1.3
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: ["--fix", "--show-fixes"]
|
||||
- id: ruff-format
|
||||
|
||||
- repo: https://github.com/adamchainz/blacken-docs
|
||||
rev: "1.16.0"
|
||||
hooks:
|
||||
- id: blacken-docs
|
||||
additional_dependencies:
|
||||
- black==22.3.0
|
||||
|
||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||
rev: "v1.10.0"
|
||||
hooks:
|
||||
- id: rst-backticks
|
||||
- id: rst-directive-colons
|
||||
- id: rst-inline-touching-normal
|
||||
|
||||
- repo: https://github.com/rstcheck/rstcheck
|
||||
rev: v6.2.0
|
||||
hooks:
|
||||
- id: rstcheck
|
||||
additional_dependencies: [sphinx]
|
||||
args: ["--ignore-directives=doctest,testsetup,todo,automodule,mongodoc,autodoc,testcleanup,autoclass","--ignore-substitutions=release", "--report-level=error"]
|
||||
exclude: '^doc/migrate-to-motor-3.rst'
|
||||
|
||||
# We use the Python version instead of the original version which seems to require Docker
|
||||
# https://github.com/koalaman/shellcheck-precommit
|
||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||
rev: v0.8.0.4
|
||||
rev: v0.9.0.6
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
name: shellcheck
|
||||
args: ["--severity=warning"]
|
||||
stages: [manual]
|
||||
|
||||
- repo: https://github.com/PyCQA/doc8
|
||||
rev: v1.1.1
|
||||
hooks:
|
||||
- id: doc8
|
||||
args: ["--ignore=D001"] # ignore line length
|
||||
stages: [manual]
|
||||
|
||||
- repo: https://github.com/sirosen/check-jsonschema
|
||||
rev: 0.14.1
|
||||
rev: 0.27.0
|
||||
hooks:
|
||||
- id: check-jsonschema
|
||||
name: "Check GitHub Workflows"
|
||||
files: ^\.github/workflows/
|
||||
types: [yaml]
|
||||
args: ["--schemafile", "https://json.schemastore.org/github-workflow"]
|
||||
|
||||
- repo: https://github.com/ariebovenberg/slotscheck
|
||||
rev: v0.17.0
|
||||
hooks:
|
||||
- id: slotscheck
|
||||
files: \.py$
|
||||
exclude: "^(doc|test)/"
|
||||
stages: [manual]
|
||||
args: ["--no-strict-imports"]
|
||||
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: "v2.2.6"
|
||||
hooks:
|
||||
- id: codespell
|
||||
args: ["-L", "fle"]
|
||||
|
||||
@ -29,13 +29,13 @@ and a `source distribution <https://packaging.python.org/guides/distributing-pac
|
||||
|
||||
#. Check JIRA to ensure all the tickets in this version have been completed.
|
||||
|
||||
#. Add release notes to `doc/changelog.rst`. Generally just summarize/clarify
|
||||
#. Add release notes to ``doc/changelog.rst``. Generally just summarize/clarify
|
||||
the git log, but you might add some more long form notes for big changes.
|
||||
|
||||
#. Replace the `devN` version number w/ the new version number (see
|
||||
#. Replace the ``devN`` version number w/ the new version number (see
|
||||
note above in `Versioning`_). Make sure version number is updated in
|
||||
``motor/_version.py``. Commit the change and tag the release.
|
||||
Immediately bump the version number to `dev0` in a new commit::
|
||||
Immediately bump the version number to ``dev0`` in a new commit::
|
||||
|
||||
$ # Bump to release version number
|
||||
$ git commit -a -m "BUMP <release version number>"
|
||||
@ -45,7 +45,7 @@ and a `source distribution <https://packaging.python.org/guides/distributing-pac
|
||||
$ git push
|
||||
$ git push --tags
|
||||
|
||||
#. Build the release packages by running the `release.sh`
|
||||
#. Build the release packages by running the ``release.sh``
|
||||
script on macOS::
|
||||
|
||||
$ git clone git@github.com:mongodb/motor.git
|
||||
|
||||
@ -67,11 +67,11 @@ New features:
|
||||
|
||||
The new Queryable Encryption changes that are in beta are:
|
||||
|
||||
- The `encrypted_fields` argument to the
|
||||
- The ``encrypted_fields`` argument to the
|
||||
:class:`~motor.motor_tornado.MotorCollection` constructor, and the
|
||||
:meth:`~motor.motor_tornado.MotorDatabase.create_collection`
|
||||
and :meth:`~motor.motor_tornado.MotorDatabase.drop_collection` methods.
|
||||
- The `query_type` and `contention_factor` arguments to
|
||||
- The ``query_type`` and ``contention_factor`` arguments to
|
||||
:meth:`motor.motor_asyncio.AsyncIOMotorClientEncryption.encrypt` and
|
||||
:meth:`motor.motor_tornado.MotorClientEncryption.encrypt`.
|
||||
|
||||
@ -87,7 +87,7 @@ Motor 3.0
|
||||
---------
|
||||
|
||||
Motor 3.0 adds support for PyMongo 4.0+. It inherits a number
|
||||
of improvemnts and breaking API changes from PyMongo 4.0+.
|
||||
of improvements and breaking API changes from PyMongo 4.0+.
|
||||
See :doc:`migrate-to-motor-3` for more information.
|
||||
|
||||
Breaking Changes
|
||||
@ -167,7 +167,7 @@ Breaking Changes
|
||||
- Comparing two :class:`~motor.motor_tornado.MotorClient` instances now
|
||||
uses a set of immutable properties rather than
|
||||
:attr:`~motor.motor_tornado.MotorClient.address` which can change.
|
||||
- Removed the `disable_md5` parameter for :class:`~gridfs.GridFSBucket` and
|
||||
- Removed the ``disable_md5`` parameter for :class:`~gridfs.GridFSBucket` and
|
||||
:class:`~gridfs.GridFS`. See :ref:`removed-gridfs-checksum` for details.
|
||||
- PyMongoCrypt 1.2.0 or later is now required for client side field level
|
||||
encryption support.
|
||||
@ -180,10 +180,10 @@ Notable improvements
|
||||
- Added the ``maxConnecting`` URI and
|
||||
:class:`~motor.motor_tornado.MotorClient` keyword argument.
|
||||
- :class:`~motor.motor_tornado.MotorClient` now accepts a URI and keyword
|
||||
argument `srvMaxHosts` that limits the number of mongos-like hosts a client
|
||||
argument ``srvMaxHosts`` that limits the number of mongos-like hosts a client
|
||||
will connect to. More specifically, when a mongodb+srv:// connection string
|
||||
resolves to more than `srvMaxHosts` number of hosts, the client will randomly
|
||||
choose a `srvMaxHosts` sized subset of hosts.
|
||||
resolves to more than ``srvMaxHosts`` number of hosts, the client will randomly
|
||||
choose a ``srvMaxHosts`` sized subset of hosts.
|
||||
- Added :attr:`motor.motor_tornado.MotorClient.options` for read-only access
|
||||
to a client's configuration options.
|
||||
- Added support for the ``comment`` parameter to all helpers. For example see
|
||||
@ -666,7 +666,7 @@ Highlights include:
|
||||
verification.
|
||||
- TLS compression is now explicitly disabled when possible.
|
||||
- The Server Name Indication (SNI) TLS extension is used when possible.
|
||||
- PyMongo's `bson` module provides finer control over JSON encoding/decoding
|
||||
- PyMongo's ``bson`` module provides finer control over JSON encoding/decoding
|
||||
with :class:`~bson.json_util.JSONOptions`.
|
||||
- Allow :class:`~bson.code.Code` objects to have a scope of ``None``,
|
||||
signifying no scope. Also allow encoding Code objects with an empty scope
|
||||
@ -738,8 +738,8 @@ Unix domain socket paths must be quoted with :func:`urllib.parse.quote_plus` (or
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
path = '/tmp/mongodb-27017.sock'
|
||||
MotorClient('mongodb://%s' % urllib.parse.quote_plus(path))
|
||||
path = "/tmp/mongodb-27017.sock"
|
||||
MotorClient("mongodb://%s" % urllib.parse.quote_plus(path))
|
||||
|
||||
:class:`~motor.motor_tornado.MotorCollection` changes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -1015,20 +1015,20 @@ Motor 0.6
|
||||
This is a bugfix release. Fixing these bugs has introduced tiny API changes that
|
||||
may affect some programs.
|
||||
|
||||
`motor_asyncio` and `motor_tornado` submodules
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
``motor_asyncio`` and ``motor_tornado`` submodules
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These modules have been moved from:
|
||||
|
||||
- `motor_asyncio.py`
|
||||
- `motor_tornado.py`
|
||||
- ``motor_asyncio.py``
|
||||
- ``motor_tornado.py``
|
||||
|
||||
To:
|
||||
|
||||
- `motor_asyncio/__init__.py`
|
||||
- `motor_tornado/__init__.py`
|
||||
- ``motor_asyncio/__init__.py``
|
||||
- ``motor_tornado/__init__.py``
|
||||
|
||||
Motor had to make this change in order to omit the `motor_asyncio` submodule
|
||||
Motor had to make this change in order to omit the ``motor_asyncio`` submodule
|
||||
entirely and avoid a spurious :exc:`SyntaxError` being printed when installing in
|
||||
Python 2. The change should be invisible to application code.
|
||||
|
||||
@ -1082,18 +1082,18 @@ explanation.)
|
||||
|
||||
.. _commit message dc19418c: https://github.com/mongodb/motor/commit/dc19418c
|
||||
|
||||
`async` and `await`
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
``async`` and ``await``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Motor now supports Python 3.5 native coroutines, written with the `async` and
|
||||
`await` syntax::
|
||||
Motor now supports Python 3.5 native coroutines, written with the ``async`` and
|
||||
``await`` syntax::
|
||||
|
||||
async def f():
|
||||
await collection.insert({'_id': 1})
|
||||
|
||||
Cursors from :meth:`~MotorCollection.find`, :meth:`~MotorCollection.aggregate`, or
|
||||
:meth:`~MotorGridFS.find` can be iterated elegantly and very efficiently in native
|
||||
coroutines with `async for`::
|
||||
coroutines with ``async for``::
|
||||
|
||||
async def f():
|
||||
async for doc in collection.find():
|
||||
@ -1105,7 +1105,7 @@ coroutines with `async for`::
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:meth:`MotorCollection.aggregate` now returns a cursor by default, and the cursor
|
||||
is returned immediately without a `yield`. The old syntax is no longer
|
||||
is returned immediately without a ``yield``. The old syntax is no longer
|
||||
supported::
|
||||
|
||||
# Motor 0.4 and older, no longer supported.
|
||||
@ -1143,7 +1143,7 @@ Deprecations
|
||||
|
||||
Motor 0.5 deprecates a large number of APIs that will be removed in version 1.0:
|
||||
|
||||
`MotorClient`:
|
||||
``MotorClient``:
|
||||
- `~MotorClient.host`
|
||||
- `~MotorClient.port`
|
||||
- `~MotorClient.document_class`
|
||||
@ -1154,7 +1154,7 @@ Motor 0.5 deprecates a large number of APIs that will be removed in version 1.0:
|
||||
- `~MotorClient.disconnect`
|
||||
- `~MotorClient.alive`
|
||||
|
||||
`MotorReplicaSetClient`:
|
||||
``MotorReplicaSetClient``:
|
||||
- `~MotorReplicaSetClient.document_class`
|
||||
- `~MotorReplicaSetClient.tz_aware`
|
||||
- `~MotorReplicaSetClient.secondary_acceptable_latency_ms`
|
||||
@ -1162,12 +1162,12 @@ Motor 0.5 deprecates a large number of APIs that will be removed in version 1.0:
|
||||
- `~MotorReplicaSetClient.uuid_subtype`
|
||||
- `~MotorReplicaSetClient.alive`
|
||||
|
||||
`MotorDatabase`:
|
||||
``MotorDatabase``:
|
||||
- `~MotorDatabase.secondary_acceptable_latency_ms`
|
||||
- `~MotorDatabase.tag_sets`
|
||||
- `~MotorDatabase.uuid_subtype`
|
||||
|
||||
`MotorCollection`:
|
||||
``MotorCollection``:
|
||||
- `~MotorCollection.secondary_acceptable_latency_ms`
|
||||
- `~MotorCollection.tag_sets`
|
||||
- `~MotorCollection.uuid_subtype`
|
||||
@ -1197,7 +1197,7 @@ SSL hostname validation error
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When you use Motor with Tornado and SSL hostname validation fails, Motor used
|
||||
to raise a :exc:`~pymongo.errors.ConnectionFailure` with a useful messsage like "hostname 'X'
|
||||
to raise a :exc:`~pymongo.errors.ConnectionFailure` with a useful message like "hostname 'X'
|
||||
doesn't match 'Y'". The message is now empty and Tornado logs a warning
|
||||
instead.
|
||||
|
||||
|
||||
@ -90,9 +90,9 @@ system_js
|
||||
|
||||
PyMongo supports Javascript procedures stored in MongoDB with syntax like:
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> db.system_js.my_func = 'function(x) { return x * x; }'
|
||||
>>> db.system_js.my_func = "function(x) { return x * x; }"
|
||||
>>> db.system_js.my_func(2)
|
||||
4.0
|
||||
|
||||
@ -133,17 +133,10 @@ There are two ways to create a capped collection using PyMongo:
|
||||
.. code-block:: python
|
||||
|
||||
# Typical:
|
||||
db.create_collection(
|
||||
'collection1',
|
||||
capped=True,
|
||||
size=1000)
|
||||
db.create_collection("collection1", capped=True, size=1000)
|
||||
|
||||
# Unusual:
|
||||
collection = Collection(
|
||||
db,
|
||||
'collection2',
|
||||
capped=True,
|
||||
size=1000)
|
||||
collection = Collection(db, "collection2", capped=True, size=1000)
|
||||
|
||||
Motor can't do I/O in a constructor, so the unusual style is prohibited and
|
||||
only the typical style is allowed:
|
||||
@ -151,7 +144,4 @@ only the typical style is allowed:
|
||||
.. code-block:: python
|
||||
|
||||
async def f():
|
||||
await db.create_collection(
|
||||
'collection1',
|
||||
capped=True,
|
||||
size=1000)
|
||||
await db.create_collection("collection1", capped=True, size=1000)
|
||||
|
||||
@ -38,6 +38,7 @@ async def page_handler(request):
|
||||
|
||||
# -- handler-end --
|
||||
|
||||
|
||||
# -- main-start --
|
||||
async def init_connection():
|
||||
db = await setup_db()
|
||||
|
||||
@ -31,9 +31,10 @@ bulk insert operations.
|
||||
.. doctest::
|
||||
|
||||
>>> async def f():
|
||||
... await db.test.insert_many(({'i': i} for i in range(10000)))
|
||||
... await db.test.insert_many(({"i": i} for i in range(10000)))
|
||||
... count = await db.test.count_documents({})
|
||||
... print("Final count: %d" % count)
|
||||
...
|
||||
>>>
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
Final count: 10000
|
||||
@ -61,14 +62,17 @@ of operations performed.
|
||||
>>> from pprint import pprint
|
||||
>>> from pymongo import InsertOne, DeleteMany, ReplaceOne, UpdateOne
|
||||
>>> async def f():
|
||||
... result = await db.test.bulk_write([
|
||||
... DeleteMany({}), # Remove all documents from the previous example.
|
||||
... InsertOne({'_id': 1}),
|
||||
... InsertOne({'_id': 2}),
|
||||
... InsertOne({'_id': 3}),
|
||||
... UpdateOne({'_id': 1}, {'$set': {'foo': 'bar'}}),
|
||||
... UpdateOne({'_id': 4}, {'$inc': {'j': 1}}, upsert=True),
|
||||
... ReplaceOne({'j': 1}, {'j': 2})])
|
||||
... result = await db.test.bulk_write(
|
||||
... [
|
||||
... DeleteMany({}), # Remove all documents from the previous example.
|
||||
... InsertOne({"_id": 1}),
|
||||
... InsertOne({"_id": 2}),
|
||||
... InsertOne({"_id": 3}),
|
||||
... UpdateOne({"_id": 1}, {"$set": {"foo": "bar"}}),
|
||||
... UpdateOne({"_id": 4}, {"$inc": {"j": 1}}, upsert=True),
|
||||
... ReplaceOne({"j": 1}, {"j": 2}),
|
||||
... ]
|
||||
... )
|
||||
... pprint(result.bulk_api_result)
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
@ -95,9 +99,10 @@ the failure.
|
||||
>>> from pymongo.errors import BulkWriteError
|
||||
>>> async def f():
|
||||
... requests = [
|
||||
... ReplaceOne({'j': 2}, {'i': 5}),
|
||||
... InsertOne({'_id': 4}), # Violates the unique key constraint on _id.
|
||||
... DeleteOne({'i': 5})]
|
||||
... ReplaceOne({"j": 2}, {"i": 5}),
|
||||
... InsertOne({"_id": 4}), # Violates the unique key constraint on _id.
|
||||
... DeleteOne({"i": 5}),
|
||||
... ]
|
||||
... try:
|
||||
... await db.test.bulk_write(requests)
|
||||
... except BulkWriteError as bwe:
|
||||
@ -136,10 +141,11 @@ and fourth operations succeed.
|
||||
|
||||
>>> async def f():
|
||||
... requests = [
|
||||
... InsertOne({'_id': 1}),
|
||||
... DeleteOne({'_id': 2}),
|
||||
... InsertOne({'_id': 3}),
|
||||
... ReplaceOne({'_id': 4}, {'i': 1})]
|
||||
... InsertOne({"_id": 1}),
|
||||
... DeleteOne({"_id": 2}),
|
||||
... InsertOne({"_id": 3}),
|
||||
... ReplaceOne({"_id": 4}, {"i": 1}),
|
||||
... ]
|
||||
... try:
|
||||
... await db.test.bulk_write(requests, ordered=False)
|
||||
... except BulkWriteError as bwe:
|
||||
@ -181,10 +187,9 @@ after all operations are attempted, regardless of execution order.
|
||||
|
||||
>>> from pymongo import WriteConcern
|
||||
>>> async def f():
|
||||
... coll = db.get_collection(
|
||||
... 'test', write_concern=WriteConcern(w=4, wtimeout=1))
|
||||
... coll = db.get_collection("test", write_concern=WriteConcern(w=4, wtimeout=1))
|
||||
... try:
|
||||
... await coll.bulk_write([InsertOne({'a': i}) for i in range(4)])
|
||||
... await coll.bulk_write([InsertOne({"a": i}) for i in range(4)])
|
||||
... except BulkWriteError as bwe:
|
||||
... pprint(bwe.details)
|
||||
...
|
||||
|
||||
@ -16,11 +16,12 @@ of a replica set member:
|
||||
from asyncio import sleep
|
||||
from pymongo.cursor import CursorType
|
||||
|
||||
|
||||
async def tail_oplog_example():
|
||||
oplog = client.local.oplog.rs
|
||||
first = await oplog.find().sort('$natural', pymongo.ASCENDING).limit(-1).next()
|
||||
first = await oplog.find().sort("$natural", pymongo.ASCENDING).limit(-1).next()
|
||||
print(first)
|
||||
ts = first['ts']
|
||||
ts = first["ts"]
|
||||
|
||||
while True:
|
||||
# For a regular capped collection CursorType.TAILABLE_AWAIT is the
|
||||
@ -30,12 +31,14 @@ of a replica set member:
|
||||
# can only be used when querying the oplog. Starting in MongoDB 4.4
|
||||
# this option is ignored by the server as queries against the oplog
|
||||
# are optimized automatically by the MongoDB query engine.
|
||||
cursor = oplog.find({'ts': {'$gt': ts}},
|
||||
cursor_type=CursorType.TAILABLE_AWAIT,
|
||||
oplog_replay=True)
|
||||
cursor = oplog.find(
|
||||
{"ts": {"$gt": ts}},
|
||||
cursor_type=CursorType.TAILABLE_AWAIT,
|
||||
oplog_replay=True,
|
||||
)
|
||||
while cursor.alive:
|
||||
async for doc in cursor:
|
||||
ts = doc['ts']
|
||||
ts = doc["ts"]
|
||||
print(doc)
|
||||
# We end up here if the find() returned no documents or if the
|
||||
# tailable cursor timed out (no new documents were added to the
|
||||
|
||||
@ -18,7 +18,7 @@ http://localhost:8888
|
||||
Open a ``mongo`` shell in the terminal and perform some operations on the
|
||||
"test" collection in the "test" database:
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: text
|
||||
|
||||
> use test
|
||||
switched to db test
|
||||
@ -29,7 +29,7 @@ Open a ``mongo`` shell in the terminal and perform some operations on the
|
||||
The application receives each change notification and displays it as JSON on
|
||||
the web page:
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: text
|
||||
|
||||
Changes
|
||||
|
||||
|
||||
@ -20,8 +20,8 @@ Featureful
|
||||
Motor wraps almost all of PyMongo's API and makes it non-blocking. For the few
|
||||
PyMongo features not implemented in Motor, see :doc:`differences`.
|
||||
|
||||
Convenient With `tornado.gen`
|
||||
=============================
|
||||
Convenient With ``tornado.gen``
|
||||
===============================
|
||||
The :mod:`tornado.gen` module lets you use coroutines to simplify asynchronous
|
||||
code. Motor methods return Futures that are convenient to use with coroutines.
|
||||
|
||||
|
||||
@ -118,6 +118,7 @@ used callbacks:
|
||||
else:
|
||||
print(result)
|
||||
|
||||
|
||||
collection.find_one({}, callback=callback)
|
||||
|
||||
Callbacks have been largely superseded by a Futures API intended for use with
|
||||
@ -134,6 +135,7 @@ a parameter:
|
||||
except Exception as exc:
|
||||
print(exc)
|
||||
|
||||
|
||||
future = collection.find_one({})
|
||||
future.add_done_callback(callback)
|
||||
|
||||
@ -181,7 +183,7 @@ Or:
|
||||
.. code-block:: python3
|
||||
|
||||
with client.start_session() as session:
|
||||
doc = client.db.collection.find_one({}, session=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
|
||||
@ -203,4 +205,4 @@ Or:
|
||||
.. code-block:: python3
|
||||
|
||||
async with client.start_session() as session:
|
||||
doc = await client.db.collection.find_one({}, session=session)
|
||||
doc = await client.db.collection.find_one({}, session=session)
|
||||
|
||||
@ -422,7 +422,7 @@ GridFS changes
|
||||
disable_md5 parameter is removed
|
||||
................................
|
||||
|
||||
Removed the `disable_md5` option for :class:`~motor.motor_tornado.gridfs.MotorGridFSBucket` and
|
||||
Removed the ``disable_md5`` option for :class:`~motor.motor_tornado.gridfs.MotorGridFSBucket` and
|
||||
:class:`~motor.motor_tornado.gridfs.MotorGridFS`. GridFS no longer generates checksums.
|
||||
Applications that desire a file digest should implement it outside GridFS
|
||||
and store it with other file metadata. For example::
|
||||
|
||||
@ -47,7 +47,6 @@ def depart_mongoref_node(self, node):
|
||||
|
||||
|
||||
class MongodocDirective(rst.Directive):
|
||||
|
||||
has_content = True
|
||||
required_arguments = 0
|
||||
optional_arguments = 0
|
||||
|
||||
@ -12,6 +12,7 @@ Tutorial: Using Motor With :mod:`asyncio`
|
||||
import pymongo
|
||||
import motor.motor_asyncio
|
||||
import asyncio
|
||||
|
||||
client = motor.motor_asyncio.AsyncIOMotorClient()
|
||||
db = client.test_database
|
||||
|
||||
@ -20,14 +21,17 @@ Tutorial: Using Motor With :mod:`asyncio`
|
||||
import pymongo
|
||||
import motor.motor_asyncio
|
||||
import asyncio
|
||||
|
||||
client = motor.motor_asyncio.AsyncIOMotorClient()
|
||||
db = client.test_database
|
||||
pymongo.MongoClient().test_database.test_collection.insert_many(
|
||||
[{'i': i} for i in range(2000)])
|
||||
[{"i": i} for i in range(2000)]
|
||||
)
|
||||
|
||||
.. testcleanup:: *
|
||||
|
||||
import pymongo
|
||||
|
||||
pymongo.MongoClient().test_database.test_collection.delete_many({})
|
||||
|
||||
A guide to using MongoDB and asyncio with Motor.
|
||||
@ -87,13 +91,13 @@ specify the host and port like:
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> client = motor.motor_asyncio.AsyncIOMotorClient('localhost', 27017)
|
||||
>>> client = motor.motor_asyncio.AsyncIOMotorClient("localhost", 27017)
|
||||
|
||||
Motor also supports `connection URIs`_:
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> client = motor.motor_asyncio.AsyncIOMotorClient('mongodb://localhost:27017')
|
||||
>>> client = motor.motor_asyncio.AsyncIOMotorClient("mongodb://localhost:27017")
|
||||
|
||||
Connect to a replica set like:
|
||||
|
||||
@ -111,7 +115,7 @@ dot-notation or bracket-notation:
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> db = client.test_database
|
||||
>>> db = client['test_database']
|
||||
>>> db = client["test_database"]
|
||||
|
||||
Creating a reference to a database does no I/O and does not require an
|
||||
``await`` expression.
|
||||
@ -126,7 +130,7 @@ collection in Motor works the same as getting a database:
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> collection = db.test_collection
|
||||
>>> collection = db['test_collection']
|
||||
>>> collection = db["test_collection"]
|
||||
|
||||
Just like getting a reference to a database, getting a reference to a
|
||||
collection does no I/O and doesn't require an ``await`` expression.
|
||||
@ -140,9 +144,9 @@ store a document in MongoDB, call :meth:`~AsyncIOMotorCollection.insert_one` in
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> async def do_insert():
|
||||
... document = {'key': 'value'}
|
||||
... document = {"key": "value"}
|
||||
... result = await db.test_collection.insert_one(document)
|
||||
... print('result %s' % repr(result.inserted_id))
|
||||
... print("result %s" % repr(result.inserted_id))
|
||||
...
|
||||
>>>
|
||||
>>> import asyncio
|
||||
@ -157,23 +161,22 @@ store a document in MongoDB, call :meth:`~AsyncIOMotorCollection.insert_one` in
|
||||
|
||||
>>> # Clean up from previous insert
|
||||
>>> pymongo.MongoClient().test_database.test_collection.delete_many({})
|
||||
<pymongo.results.DeleteResult ...>
|
||||
DeleteResult({'n': 1, 'ok': 1.0}, acknowledged=True)
|
||||
|
||||
Insert documents in large batches with :meth:`~AsyncIOMotorCollection.insert_many`:
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> async def do_insert():
|
||||
... result = await db.test_collection.insert_many(
|
||||
... [{'i': i} for i in range(2000)])
|
||||
... print('inserted %d docs' % (len(result.inserted_ids),))
|
||||
... result = await db.test_collection.insert_many([{"i": i} for i in range(2000)])
|
||||
... print("inserted %d docs" % (len(result.inserted_ids),))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_insert())
|
||||
inserted 2000 docs
|
||||
|
||||
Getting a Single Document With `find_one`
|
||||
-----------------------------------------
|
||||
Getting a Single Document With ``find_one``
|
||||
-------------------------------------------
|
||||
|
||||
Use :meth:`~motor.motor_asyncio.AsyncIOMotorCollection.find_one` to get the first document that
|
||||
matches a query. For example, to get a document where the value for key "i" is
|
||||
@ -182,7 +185,7 @@ less than 1:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find_one():
|
||||
... document = await db.test_collection.find_one({'i': {'$lt': 1}})
|
||||
... document = await db.test_collection.find_one({"i": {"$lt": 1}})
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
@ -209,7 +212,7 @@ To find all documents with "i" less than 5:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find():
|
||||
... cursor = db.test_collection.find({'i': {'$lt': 5}}).sort('i')
|
||||
... cursor = db.test_collection.find({"i": {"$lt": 5}}).sort("i")
|
||||
... for document in await cursor.to_list(length=100):
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
@ -233,7 +236,7 @@ You can handle one document at a time in an ``async for`` loop:
|
||||
|
||||
>>> async def do_find():
|
||||
... c = db.test_collection
|
||||
... async for document in c.find({'i': {'$lt': 2}}):
|
||||
... async for document in c.find({"i": {"$lt": 2}}):
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
@ -246,9 +249,9 @@ You can apply a sort, limit, or skip to a query before you begin iterating:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find():
|
||||
... cursor = db.test_collection.find({'i': {'$lt': 4}})
|
||||
... cursor = db.test_collection.find({"i": {"$lt": 4}})
|
||||
... # Modify the query before iterating
|
||||
... cursor.sort('i', -1).skip(1).limit(2)
|
||||
... cursor.sort("i", -1).skip(1).limit(2)
|
||||
... async for document in cursor:
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
@ -272,9 +275,9 @@ that match a query:
|
||||
|
||||
>>> async def do_count():
|
||||
... n = await db.test_collection.count_documents({})
|
||||
... print('%s documents in collection' % n)
|
||||
... n = await db.test_collection.count_documents({'i': {'$gt': 1000}})
|
||||
... print('%s documents where i > 1000' % n)
|
||||
... print("%s documents in collection" % n)
|
||||
... n = await db.test_collection.count_documents({"i": {"$gt": 1000}})
|
||||
... print("%s documents where i > 1000" % n)
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_count())
|
||||
@ -293,13 +296,13 @@ replacement document. The query follows the same syntax as for :meth:`find` or
|
||||
|
||||
>>> async def do_replace():
|
||||
... coll = db.test_collection
|
||||
... old_document = await coll.find_one({'i': 50})
|
||||
... print('found document: %s' % pprint.pformat(old_document))
|
||||
... _id = old_document['_id']
|
||||
... result = await coll.replace_one({'_id': _id}, {'key': 'value'})
|
||||
... print('replaced %s document' % result.modified_count)
|
||||
... new_document = await coll.find_one({'_id': _id})
|
||||
... print('document is now %s' % pprint.pformat(new_document))
|
||||
... old_document = await coll.find_one({"i": 50})
|
||||
... print("found document: %s" % pprint.pformat(old_document))
|
||||
... _id = old_document["_id"]
|
||||
... result = await coll.replace_one({"_id": _id}, {"key": "value"})
|
||||
... print("replaced %s document" % result.modified_count)
|
||||
... new_document = await coll.find_one({"_id": _id})
|
||||
... print("document is now %s" % pprint.pformat(new_document))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_replace())
|
||||
@ -319,10 +322,10 @@ operator to set "key" to "value":
|
||||
|
||||
>>> async def do_update():
|
||||
... coll = db.test_collection
|
||||
... result = await coll.update_one({'i': 51}, {'$set': {'key': 'value'}})
|
||||
... print('updated %s document' % result.modified_count)
|
||||
... new_document = await coll.find_one({'i': 51})
|
||||
... print('document is now %s' % pprint.pformat(new_document))
|
||||
... result = await coll.update_one({"i": 51}, {"$set": {"key": "value"}})
|
||||
... print("updated %s document" % result.modified_count)
|
||||
... new_document = await coll.find_one({"i": 51})
|
||||
... print("document is now %s" % pprint.pformat(new_document))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_update())
|
||||
@ -351,9 +354,9 @@ Deleting Documents
|
||||
>>> async def do_delete_one():
|
||||
... coll = db.test_collection
|
||||
... n = await coll.count_documents({})
|
||||
... print('%s documents before calling delete_one()' % n)
|
||||
... result = await db.test_collection.delete_one({'i': {'$gte': 1000}})
|
||||
... print('%s documents after' % (await coll.count_documents({})))
|
||||
... print("%s documents before calling delete_one()" % n)
|
||||
... result = await db.test_collection.delete_one({"i": {"$gte": 1000}})
|
||||
... print("%s documents after" % (await coll.count_documents({})))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_delete_one())
|
||||
@ -369,9 +372,9 @@ Deleting Documents
|
||||
>>> async def do_delete_many():
|
||||
... coll = db.test_collection
|
||||
... n = await coll.count_documents({})
|
||||
... print('%s documents before calling delete_many()' % n)
|
||||
... result = await db.test_collection.delete_many({'i': {'$gte': 1000}})
|
||||
... print('%s documents after' % (await coll.count_documents({})))
|
||||
... print("%s documents before calling delete_many()" % n)
|
||||
... result = await db.test_collection.delete_many({"i": {"$gte": 1000}})
|
||||
... print("%s documents after" % (await coll.count_documents({})))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_delete_many())
|
||||
@ -390,8 +393,7 @@ the :meth:`~motor.motor_asyncio.AsyncIOMotorDatabase.command` method on
|
||||
|
||||
>>> from bson import SON
|
||||
>>> async def use_distinct_command():
|
||||
... response = await db.command(SON([("distinct", "test_collection"),
|
||||
... ("key", "i")]))
|
||||
... response = await db.command(SON([("distinct", "test_collection"), ("key", "i")]))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(use_distinct_command())
|
||||
|
||||
@ -14,6 +14,7 @@ Tutorial: Using Motor With Tornado
|
||||
import tornado.web
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado import gen
|
||||
|
||||
db = motor.motor_tornado.MotorClient().test_database
|
||||
|
||||
.. testsetup:: after-inserting-2000-docs
|
||||
@ -23,15 +24,16 @@ Tutorial: Using Motor With Tornado
|
||||
import tornado.web
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado import gen
|
||||
|
||||
db = motor.motor_tornado.MotorClient().test_database
|
||||
sync_db = pymongo.MongoClient().test_database
|
||||
sync_db.test_collection.drop()
|
||||
sync_db.test_collection.insert_many(
|
||||
[{'i': i} for i in range(2000)])
|
||||
sync_db.test_collection.insert_many([{"i": i} for i in range(2000)])
|
||||
|
||||
.. testcleanup:: *
|
||||
|
||||
import pymongo
|
||||
|
||||
pymongo.MongoClient().test_database.test_collection.delete_many({})
|
||||
|
||||
A guide to using MongoDB and Tornado with Motor.
|
||||
@ -96,13 +98,13 @@ specify the host and port like:
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> client = motor.motor_tornado.MotorClient('localhost', 27017)
|
||||
>>> client = motor.motor_tornado.MotorClient("localhost", 27017)
|
||||
|
||||
Motor also supports `connection URIs`_:
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> client = motor.motor_tornado.MotorClient('mongodb://localhost:27017')
|
||||
>>> client = motor.motor_tornado.MotorClient("mongodb://localhost:27017")
|
||||
|
||||
Connect to a replica set like:
|
||||
|
||||
@ -120,7 +122,7 @@ dot-notation or bracket-notation:
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> db = client.test_database
|
||||
>>> db = client['test_database']
|
||||
>>> db = client["test_database"]
|
||||
|
||||
Creating a reference to a database does no I/O and does not require an
|
||||
``await`` expression.
|
||||
@ -187,7 +189,7 @@ collection in Motor works the same as getting a database:
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> collection = db.test_collection
|
||||
>>> collection = db['test_collection']
|
||||
>>> collection = db["test_collection"]
|
||||
|
||||
Just like getting a reference to a database, getting a reference to a
|
||||
collection does no I/O and doesn't require an ``await`` expression.
|
||||
@ -201,9 +203,9 @@ store a document in MongoDB, call :meth:`~MotorCollection.insert_one` in an
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> async def do_insert():
|
||||
... document = {'key': 'value'}
|
||||
... document = {"key": "value"}
|
||||
... result = await db.test_collection.insert_one(document)
|
||||
... print('result %s' % repr(result.inserted_id))
|
||||
... print("result %s" % repr(result.inserted_id))
|
||||
...
|
||||
>>>
|
||||
>>> IOLoop.current().run_sync(do_insert)
|
||||
@ -216,7 +218,7 @@ store a document in MongoDB, call :meth:`~MotorCollection.insert_one` in an
|
||||
|
||||
>>> # Clean up from previous insert
|
||||
>>> pymongo.MongoClient().test_database.test_collection.delete_many({})
|
||||
<pymongo.results.DeleteResult ...>
|
||||
DeleteResult({'n': 1, 'ok': 1.0}, acknowledged=True)
|
||||
|
||||
A typical beginner's mistake with Motor is to insert documents in a loop,
|
||||
not waiting for each insert to complete before beginning the next::
|
||||
@ -236,7 +238,7 @@ sequence, use ``await``:
|
||||
|
||||
>>> async def do_insert():
|
||||
... for i in range(2000):
|
||||
... await db.test_collection.insert_one({'i': i})
|
||||
... await db.test_collection.insert_one({"i": i})
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_insert)
|
||||
|
||||
@ -249,7 +251,7 @@ sequence, use ``await``:
|
||||
|
||||
>>> # Clean up from previous insert
|
||||
>>> pymongo.MongoClient().test_database.test_collection.delete_many({})
|
||||
<pymongo.results.DeleteResult ...>
|
||||
DeleteResult({'n': 2000, 'ok': 1.0}, acknowledged=True)
|
||||
|
||||
For better performance, insert documents in large batches with
|
||||
:meth:`~MotorCollection.insert_many`:
|
||||
@ -257,9 +259,8 @@ For better performance, insert documents in large batches with
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> async def do_insert():
|
||||
... result = await db.test_collection.insert_many(
|
||||
... [{'i': i} for i in range(2000)])
|
||||
... print('inserted %d docs' % (len(result.inserted_ids),))
|
||||
... result = await db.test_collection.insert_many([{"i": i} for i in range(2000)])
|
||||
... print("inserted %d docs" % (len(result.inserted_ids),))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_insert)
|
||||
inserted 2000 docs
|
||||
@ -273,7 +274,7 @@ less than 1:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find_one():
|
||||
... document = await db.test_collection.find_one({'i': {'$lt': 1}})
|
||||
... document = await db.test_collection.find_one({"i": {"$lt": 1}})
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_find_one)
|
||||
@ -302,7 +303,7 @@ To find all documents with "i" less than 5:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find():
|
||||
... cursor = db.test_collection.find({'i': {'$lt': 5}}).sort('i')
|
||||
... cursor = db.test_collection.find({"i": {"$lt": 5}}).sort("i")
|
||||
... for document in await cursor.to_list(length=100):
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
@ -325,7 +326,7 @@ You can handle one document at a time in an ``async for`` loop:
|
||||
|
||||
>>> async def do_find():
|
||||
... c = db.test_collection
|
||||
... async for document in c.find({'i': {'$lt': 2}}):
|
||||
... async for document in c.find({"i": {"$lt": 2}}):
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_find)
|
||||
@ -337,9 +338,9 @@ You can apply a sort, limit, or skip to a query before you begin iterating:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find():
|
||||
... cursor = db.test_collection.find({'i': {'$lt': 4}})
|
||||
... cursor = db.test_collection.find({"i": {"$lt": 4}})
|
||||
... # Modify the query before iterating
|
||||
... cursor.sort('i', -1).skip(1).limit(2)
|
||||
... cursor.sort("i", -1).skip(1).limit(2)
|
||||
... async for document in cursor:
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
@ -361,9 +362,9 @@ documents in a collection, or the number of documents that match a query:
|
||||
|
||||
>>> async def do_count():
|
||||
... n = await db.test_collection.count_documents({})
|
||||
... print('%s documents in collection' % n)
|
||||
... n = await db.test_collection.count_documents({'i': {'$gt': 1000}})
|
||||
... print('%s documents where i > 1000' % n)
|
||||
... print("%s documents in collection" % n)
|
||||
... n = await db.test_collection.count_documents({"i": {"$gt": 1000}})
|
||||
... print("%s documents where i > 1000" % n)
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_count)
|
||||
2000 documents in collection
|
||||
@ -381,13 +382,13 @@ replacement document. The query follows the same syntax as for :meth:`find` or
|
||||
|
||||
>>> async def do_replace():
|
||||
... coll = db.test_collection
|
||||
... old_document = await coll.find_one({'i': 50})
|
||||
... print('found document: %s' % pprint.pformat(old_document))
|
||||
... _id = old_document['_id']
|
||||
... result = await coll.replace_one({'_id': _id}, {'key': 'value'})
|
||||
... print('replaced %s document' % result.modified_count)
|
||||
... new_document = await coll.find_one({'_id': _id})
|
||||
... print('document is now %s' % pprint.pformat(new_document))
|
||||
... old_document = await coll.find_one({"i": 50})
|
||||
... print("found document: %s" % pprint.pformat(old_document))
|
||||
... _id = old_document["_id"]
|
||||
... result = await coll.replace_one({"_id": _id}, {"key": "value"})
|
||||
... print("replaced %s document" % result.modified_count)
|
||||
... new_document = await coll.find_one({"_id": _id})
|
||||
... print("document is now %s" % pprint.pformat(new_document))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_replace)
|
||||
found document: {'_id': ObjectId('...'), 'i': 50}
|
||||
@ -406,10 +407,10 @@ operator to set "key" to "value":
|
||||
|
||||
>>> async def do_update():
|
||||
... coll = db.test_collection
|
||||
... result = await coll.update_one({'i': 51}, {'$set': {'key': 'value'}})
|
||||
... print('updated %s document' % result.modified_count)
|
||||
... new_document = await coll.find_one({'i': 51})
|
||||
... print('document is now %s' % pprint.pformat(new_document))
|
||||
... result = await coll.update_one({"i": 51}, {"$set": {"key": "value"}})
|
||||
... print("updated %s document" % result.modified_count)
|
||||
... new_document = await coll.find_one({"i": 51})
|
||||
... print("document is now %s" % pprint.pformat(new_document))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_update)
|
||||
updated 1 document
|
||||
@ -437,9 +438,9 @@ Removing Documents
|
||||
>>> async def do_delete_one():
|
||||
... coll = db.test_collection
|
||||
... n = await coll.count_documents({})
|
||||
... print('%s documents before calling delete_one()' % n)
|
||||
... result = await db.test_collection.delete_one({'i': {'$gte': 1000}})
|
||||
... print('%s documents after' % (await coll.count_documents({})))
|
||||
... print("%s documents before calling delete_one()" % n)
|
||||
... result = await db.test_collection.delete_one({"i": {"$gte": 1000}})
|
||||
... print("%s documents after" % (await coll.count_documents({})))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_delete_one)
|
||||
2000 documents before calling delete_one()
|
||||
@ -454,9 +455,9 @@ Removing Documents
|
||||
>>> async def do_delete_many():
|
||||
... coll = db.test_collection
|
||||
... n = await coll.count_documents({})
|
||||
... print('%s documents before calling delete_many()' % n)
|
||||
... result = await db.test_collection.delete_many({'i': {'$gte': 1000}})
|
||||
... print('%s documents after' % (await coll.count_documents({})))
|
||||
... print("%s documents before calling delete_many()" % n)
|
||||
... result = await db.test_collection.delete_many({"i": {"$gte": 1000}})
|
||||
... print("%s documents after" % (await coll.count_documents({})))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_delete_many)
|
||||
1999 documents before calling delete_many()
|
||||
@ -474,8 +475,7 @@ the :meth:`~motor.motor_tornado.MotorDatabase.command` method on
|
||||
|
||||
>>> from bson import SON
|
||||
>>> async def use_distinct_command():
|
||||
... response = await db.command(SON([("distinct", "test_collection"),
|
||||
... ("key", "i")]))
|
||||
... response = await db.command(SON([("distinct", "test_collection"), ("key", "i")]))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(use_distinct_command)
|
||||
|
||||
|
||||
@ -133,9 +133,9 @@ class AIOHTTPGridFS:
|
||||
app = aiohttp.web.Application()
|
||||
|
||||
# The GridFS URL pattern must have a "{filename}" variable.
|
||||
resource = app.router.add_resource('/fs/{filename}')
|
||||
resource.add_route('GET', gridfs_handler)
|
||||
resource.add_route('HEAD', gridfs_handler)
|
||||
resource = app.router.add_resource("/fs/{filename}")
|
||||
resource.add_route("GET", gridfs_handler)
|
||||
resource.add_route("HEAD", gridfs_handler)
|
||||
|
||||
app_handler = app.make_handler()
|
||||
server = loop.create_server(app_handler, port=80)
|
||||
|
||||
@ -321,8 +321,8 @@ class AgnosticClientSession(AgnosticBase):
|
||||
|
||||
async with await client.start_session() as s:
|
||||
async with s.start_transaction():
|
||||
await collection.delete_one({'x': 1}, session=s)
|
||||
await collection.insert_one({'x': 2}, session=s)
|
||||
await collection.delete_one({"x": 1}, session=s)
|
||||
await collection.insert_one({"x": 2}, session=s)
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
@ -388,7 +388,7 @@ class AgnosticClientSession(AgnosticBase):
|
||||
In the event of an exception, ``with_transaction`` may retry the commit
|
||||
or the entire transaction, therefore ``coro`` may be awaited
|
||||
multiple times by a single call to ``with_transaction``. Developers
|
||||
should be mindful of this possiblity when writing a ``coro`` that
|
||||
should be mindful of this possibility when writing a ``coro`` that
|
||||
modifies application state or has any other side-effects.
|
||||
Note that even when the ``coro`` is invoked multiple times,
|
||||
``with_transaction`` ensures that the transaction will be committed
|
||||
@ -500,8 +500,8 @@ class AgnosticClientSession(AgnosticBase):
|
||||
# Use "await" for start_session, but not for start_transaction.
|
||||
async with await client.start_session() as s:
|
||||
async with s.start_transaction():
|
||||
await collection.delete_one({'x': 1}, session=s)
|
||||
await collection.insert_one({'x': 2}, session=s)
|
||||
await collection.delete_one({"x": 1}, session=s)
|
||||
await collection.insert_one({"x": 2}, session=s)
|
||||
|
||||
"""
|
||||
self.delegate.start_transaction(
|
||||
@ -1171,6 +1171,7 @@ class AgnosticCollection(AgnosticBaseProperties):
|
||||
|
||||
change_stream = None
|
||||
|
||||
|
||||
async def watch_collection():
|
||||
global change_stream
|
||||
|
||||
@ -1181,17 +1182,20 @@ class AgnosticCollection(AgnosticBaseProperties):
|
||||
async for change in change_stream:
|
||||
print(change)
|
||||
|
||||
|
||||
# Tornado
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
|
||||
def main():
|
||||
loop = IOLoop.current()
|
||||
# Start watching collection for changes.
|
||||
try:
|
||||
loop.run_sync(watch_collection)
|
||||
except KeyboardInterrupt:
|
||||
if change_stream:
|
||||
loop.run_sync(change_stream.close)
|
||||
try:
|
||||
loop.run_sync(watch_collection)
|
||||
except KeyboardInterrupt:
|
||||
if change_stream:
|
||||
loop.run_sync(change_stream.close)
|
||||
|
||||
|
||||
# asyncio
|
||||
try:
|
||||
@ -1210,14 +1214,14 @@ class AgnosticCollection(AgnosticBaseProperties):
|
||||
.. code-block:: python3
|
||||
|
||||
try:
|
||||
pipeline = [{'$match': {'operationType': 'insert'}}]
|
||||
pipeline = [{"$match": {"operationType": "insert"}}]
|
||||
async with db.collection.watch(pipeline) as stream:
|
||||
async for change in stream:
|
||||
print(change)
|
||||
except pymongo.errors.PyMongoError:
|
||||
# The ChangeStream encountered an unrecoverable error or the
|
||||
# resume attempt failed to recreate the cursor.
|
||||
logging.error('...')
|
||||
logging.error("...")
|
||||
|
||||
For a precise description of the resume process see the
|
||||
`change streams specification`_.
|
||||
@ -1435,10 +1439,10 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
|
||||
>>> async def f():
|
||||
... await collection.drop()
|
||||
... await collection.insert_many([{'_id': i} for i in range(5)])
|
||||
... await collection.insert_many([{"_id": i} for i in range(5)])
|
||||
... async for doc in collection.find():
|
||||
... sys.stdout.write(str(doc['_id']) + ', ')
|
||||
... print('done')
|
||||
... sys.stdout.write(str(doc["_id"]) + ", ")
|
||||
... print("done")
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
0, 1, 2, 3, 4, done
|
||||
@ -1451,12 +1455,12 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
|
||||
>>> async def f():
|
||||
... await collection.drop()
|
||||
... await collection.insert_many([{'_id': i} for i in range(5)])
|
||||
... cursor = collection.find().sort([('_id', 1)])
|
||||
... while (await cursor.fetch_next):
|
||||
... await collection.insert_many([{"_id": i} for i in range(5)])
|
||||
... cursor = collection.find().sort([("_id", 1)])
|
||||
... while await cursor.fetch_next:
|
||||
... doc = cursor.next_object()
|
||||
... sys.stdout.write(str(doc['_id']) + ', ')
|
||||
... print('done')
|
||||
... sys.stdout.write(str(doc["_id"]) + ", ")
|
||||
... print("done")
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
0, 1, 2, 3, 4, done
|
||||
@ -1524,9 +1528,9 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
.. testsetup:: each
|
||||
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
MongoClient().test.test_collection.delete_many({})
|
||||
MongoClient().test.test_collection.insert_many(
|
||||
[{'_id': i} for i in range(5)])
|
||||
MongoClient().test.test_collection.insert_many([{"_id": i} for i in range(5)])
|
||||
|
||||
collection = MotorClient().test.test_collection
|
||||
|
||||
@ -1536,13 +1540,13 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
... if error:
|
||||
... raise error
|
||||
... elif result:
|
||||
... sys.stdout.write(str(result['_id']) + ', ')
|
||||
... sys.stdout.write(str(result["_id"]) + ", ")
|
||||
... else:
|
||||
... # Iteration complete
|
||||
... IOLoop.current().stop()
|
||||
... print('done')
|
||||
... print("done")
|
||||
...
|
||||
>>> cursor = collection.find().sort([('_id', 1)])
|
||||
>>> cursor = collection.find().sort([("_id", 1)])
|
||||
>>> cursor.each(callback=each)
|
||||
>>> IOLoop.current().start()
|
||||
0, 1, 2, 3, 4, done
|
||||
@ -1594,7 +1598,7 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
.. testsetup:: to_list
|
||||
|
||||
MongoClient().test.test_collection.delete_many({})
|
||||
MongoClient().test.test_collection.insert_many([{'_id': i} for i in range(4)])
|
||||
MongoClient().test.test_collection.insert_many([{"_id": i} for i in range(4)])
|
||||
|
||||
from tornado import ioloop
|
||||
|
||||
@ -1604,13 +1608,12 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
>>> collection = MotorClient().test.test_collection
|
||||
>>>
|
||||
>>> async def f():
|
||||
... cursor = collection.find().sort([('_id', 1)])
|
||||
... cursor = collection.find().sort([("_id", 1)])
|
||||
... docs = await cursor.to_list(length=2)
|
||||
... while docs:
|
||||
... print(docs)
|
||||
... docs = await cursor.to_list(length=2)
|
||||
...
|
||||
... print('done')
|
||||
... print("done")
|
||||
...
|
||||
>>> ioloop.IOLoop.current().run_sync(f)
|
||||
[{'_id': 0}, {'_id': 1}]
|
||||
|
||||
@ -111,7 +111,7 @@ For example, to list all non-system collections::
|
||||
- `comment` (optional): A user-provided comment to attach to this command.
|
||||
- `**kwargs` (optional): Optional parameters of the
|
||||
`listCollections
|
||||
<https://www.mongodb.com/docs/manual/reference/command/listCollections/>`_ comand.
|
||||
<https://www.mongodb.com/docs/manual/reference/command/listCollections/>`_ command.
|
||||
can be passed as keyword arguments to this method. The supported
|
||||
options differ by server version.
|
||||
|
||||
@ -1140,17 +1140,17 @@ Pass a field name and a direction, either
|
||||
.. testsetup:: sort
|
||||
|
||||
MongoClient().test.test_collection.drop()
|
||||
MongoClient().test.test_collection.insert_many([
|
||||
{'_id': i, 'field1': i % 2, 'field2': i}
|
||||
for i in range(5)])
|
||||
MongoClient().test.test_collection.insert_many(
|
||||
[{"_id": i, "field1": i % 2, "field2": i} for i in range(5)]
|
||||
)
|
||||
collection = MotorClient().test.test_collection
|
||||
|
||||
.. doctest:: sort
|
||||
|
||||
>>> async def f():
|
||||
... cursor = collection.find().sort('_id', pymongo.DESCENDING)
|
||||
... cursor = collection.find().sort("_id", pymongo.DESCENDING)
|
||||
... docs = await cursor.to_list(None)
|
||||
... print([d['_id'] for d in docs])
|
||||
... print([d["_id"] for d in docs])
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
[4, 3, 2, 1, 0]
|
||||
@ -1160,12 +1160,11 @@ To sort by multiple fields, pass a list of (key, direction) pairs:
|
||||
.. doctest:: sort
|
||||
|
||||
>>> async def f():
|
||||
... cursor = collection.find().sort([
|
||||
... ('field1', pymongo.ASCENDING),
|
||||
... ('field2', pymongo.DESCENDING)])
|
||||
...
|
||||
... cursor = collection.find().sort(
|
||||
... [("field1", pymongo.ASCENDING), ("field2", pymongo.DESCENDING)]
|
||||
... )
|
||||
... docs = await cursor.to_list(None)
|
||||
... print([(d['field1'], d['field2']) for d in docs])
|
||||
... print([(d["field1"], d["field2"]) for d in docs])
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
[(0, 4), (0, 2), (0, 0), (1, 3), (1, 1)]
|
||||
@ -1175,24 +1174,23 @@ Text search results can be sorted by relevance:
|
||||
.. testsetup:: sort_text
|
||||
|
||||
MongoClient().test.test_collection.drop()
|
||||
MongoClient().test.test_collection.insert_many([
|
||||
{'field': 'words'},
|
||||
{'field': 'words about some words'}])
|
||||
MongoClient().test.test_collection.insert_many(
|
||||
[{"field": "words"}, {"field": "words about some words"}]
|
||||
)
|
||||
|
||||
MongoClient().test.test_collection.create_index([('field', 'text')])
|
||||
MongoClient().test.test_collection.create_index([("field", "text")])
|
||||
collection = MotorClient().test.test_collection
|
||||
|
||||
.. doctest:: sort_text
|
||||
|
||||
>>> async def f():
|
||||
... cursor = collection.find({
|
||||
... '$text': {'$search': 'some words'}},
|
||||
... {'score': {'$meta': 'textScore'}})
|
||||
...
|
||||
... cursor = collection.find(
|
||||
... {"$text": {"$search": "some words"}}, {"score": {"$meta": "textScore"}}
|
||||
... )
|
||||
... # Sort by 'score' field.
|
||||
... cursor.sort([('score', {'$meta': 'textScore'})])
|
||||
... cursor.sort([("score", {"$meta": "textScore"})])
|
||||
... async for doc in cursor:
|
||||
... print('%.1f %s' % (doc['score'], doc['field']))
|
||||
... print("%.1f %s" % (doc["score"], doc["field"]))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
1.5 words about some words
|
||||
@ -1230,11 +1228,10 @@ to initialize it, or an ``async with`` statement.
|
||||
# Or, use an "async with" statement to end the session
|
||||
# automatically.
|
||||
async with await client.start_session() as s:
|
||||
doc = {'_id': ObjectId(), 'x': 1}
|
||||
doc = {"_id": ObjectId(), "x": 1}
|
||||
await collection.insert_one(doc, session=s)
|
||||
|
||||
secondary = collection.with_options(
|
||||
read_preference=ReadPreference.SECONDARY)
|
||||
secondary = collection.with_options(read_preference=ReadPreference.SECONDARY)
|
||||
|
||||
# Sessions are causally consistent by default, so we can read
|
||||
# the doc we just inserted, even reading from a secondary.
|
||||
@ -1245,8 +1242,8 @@ to initialize it, or an ``async with`` statement.
|
||||
async with await client.start_session() as s:
|
||||
# Note, start_transaction doesn't require "await".
|
||||
async with s.start_transaction():
|
||||
await collection.delete_one({'x': 1}, session=s)
|
||||
await collection.insert_one({'x': 2}, session=s)
|
||||
await collection.delete_one({"x": 1}, session=s)
|
||||
await collection.insert_one({"x": 2}, session=s)
|
||||
|
||||
# Exiting the "with s.start_transaction()" block while throwing an
|
||||
# exception automatically aborts the transaction, exiting the block
|
||||
@ -1255,10 +1252,10 @@ to initialize it, or an ``async with`` statement.
|
||||
# You can run additional transactions in the same session, so long as
|
||||
# you run them one at a time.
|
||||
async with s.start_transaction():
|
||||
await collection.insert_one({'x': 3}, session=s)
|
||||
await collection.insert_many({'x': {'$gte': 2}},
|
||||
{'$inc': {'x': 1}},
|
||||
session=s)
|
||||
await collection.insert_one({"x": 3}, session=s)
|
||||
await collection.insert_many(
|
||||
{"x": {"$gte": 2}}, {"$inc": {"x": 1}}, session=s
|
||||
)
|
||||
|
||||
|
||||
Requires MongoDB 3.6.
|
||||
|
||||
@ -179,7 +179,7 @@ class AgnosticGridOut:
|
||||
@tornado.web.asynchronous
|
||||
@gen.coroutine
|
||||
def get(self, filename):
|
||||
db = self.settings['db']
|
||||
db = self.settings["db"]
|
||||
fs = await motor.MotorGridFSBucket(db())
|
||||
try:
|
||||
gridout = await fs.open_download_stream_by_name(filename)
|
||||
|
||||
@ -105,7 +105,7 @@ class AgnosticGridIn:
|
||||
root_collection: AgnosticCollection,
|
||||
delegate: Any = None,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
**kwargs: Any
|
||||
**kwargs: Any,
|
||||
) -> None: ...
|
||||
async def __aenter__(self) -> AgnosticGridIn: ...
|
||||
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: ...
|
||||
|
||||
@ -40,9 +40,11 @@ class GridFSHandler(tornado.web.RequestHandler):
|
||||
.. code-block:: python
|
||||
|
||||
db = motor.MotorClient().my_database
|
||||
application = web.Application([
|
||||
(r"/static/(.*)", web.GridFSHandler, {"database": db}),
|
||||
])
|
||||
application = web.Application(
|
||||
[
|
||||
(r"/static/(.*)", web.GridFSHandler, {"database": db}),
|
||||
]
|
||||
)
|
||||
|
||||
By default, requests' If-Modified-Since headers are honored, but no
|
||||
specific cache-control timeout is sent to clients. Thus each request for
|
||||
|
||||
@ -85,6 +85,8 @@ include = ["motor"]
|
||||
[tool.ruff]
|
||||
target-version = "py37"
|
||||
line-length = 100
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"E", "F", "W", # flake8
|
||||
"B", # flake8-bugbear
|
||||
@ -93,7 +95,6 @@ select = [
|
||||
"C4", # flake8-comprehensions
|
||||
"EM", # flake8-errmsg
|
||||
"ICN", # flake8-import-conventions
|
||||
"ISC", # flake8-implicit-str-concat
|
||||
"G", # flake8-logging-format
|
||||
"PGH", # pygrep-hooks
|
||||
"PIE", # flake8-pie
|
||||
@ -108,7 +109,7 @@ select = [
|
||||
"YTT", # flake8-2020
|
||||
"EXE", # flake8-executable
|
||||
]
|
||||
extend-ignore = [
|
||||
ignore = [
|
||||
"ARG001", # Unused function argument
|
||||
"UP007", # Use `X | Y` for type annotation
|
||||
"EM101", # Exception must not use a string literal, assign to variable first
|
||||
@ -131,7 +132,7 @@ exclude = []
|
||||
flake8-unused-arguments.ignore-variadic-names = true
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?)|dummy.*)$"
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"test/*.py" = ["PT009", "ARG", "E402", "PT027", "UP031",
|
||||
"B904", "C405", "SIM", "PLR", "PTH", "B018", "C4",
|
||||
"S", "E501", "T201", "E731", "F841", "F811",
|
||||
|
||||
@ -460,9 +460,9 @@ class Database(Synchro):
|
||||
if self.delegate is None:
|
||||
self.delegate = motor.MotorDatabase(client.delegate, name, **kwargs)
|
||||
|
||||
assert isinstance(
|
||||
self.delegate, motor.MotorDatabase
|
||||
), "synchro.Database delegate must be MotorDatabase, not %s" % repr(self.delegate)
|
||||
assert isinstance(self.delegate, motor.MotorDatabase), (
|
||||
"synchro.Database delegate must be MotorDatabase, not %s" % repr(self.delegate)
|
||||
)
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
|
||||
@ -198,7 +198,7 @@ excluded_tests = [
|
||||
excluded_modules_matched = set()
|
||||
excluded_tests_matched = set()
|
||||
|
||||
# Valide the exclude lists.
|
||||
# Validate the exclude lists.
|
||||
for item in excluded_modules:
|
||||
if not re.match(r"^test\.[a-zA-Z_]+$", item):
|
||||
raise ValueError(f"Improper excluded module {item}")
|
||||
|
||||
@ -112,7 +112,7 @@ class AsyncIOTestCase(AssertLogsMixin, unittest.TestCase):
|
||||
return motor_asyncio.AsyncIOMotorClient(
|
||||
kwargs.pop("host", None) or uri or env.uri,
|
||||
*args,
|
||||
**self.get_client_kwargs(**kwargs, set_loop=set_loop)
|
||||
**self.get_client_kwargs(**kwargs, set_loop=set_loop),
|
||||
)
|
||||
|
||||
def asyncio_rsc(self, uri=None, *args, **kwargs):
|
||||
|
||||
@ -1414,7 +1414,6 @@ LOCAL_MASTER_KEY = base64.b64decode(
|
||||
|
||||
|
||||
class TestQueryableEncryptionDocsExample(AsyncIOTestCase):
|
||||
|
||||
# Queryable Encryption is not supported on Standalone topology.
|
||||
|
||||
@env.require_version_min(7, 0, -1)
|
||||
|
||||
@ -116,7 +116,6 @@ class MotorReplicaSetTestBase(MotorTest):
|
||||
|
||||
|
||||
class MotorMockServerTest(MotorTest):
|
||||
|
||||
executor = concurrent.futures.ThreadPoolExecutor(1)
|
||||
|
||||
def server(self, *args, **kwargs):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user