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:
Steven Silvester 2023-11-08 10:13:38 -06:00 committed by GitHub
parent 684279ed0a
commit 8f71800c4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 293 additions and 249 deletions

View File

@ -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"]

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -38,6 +38,7 @@ async def page_handler(request):
# -- handler-end --
# -- main-start --
async def init_connection():
db = await setup_db()

View File

@ -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)
...

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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::

View File

@ -47,7 +47,6 @@ def depart_mongoref_node(self, node):
class MongodocDirective(rst.Directive):
has_content = True
required_arguments = 0
optional_arguments = 0

View File

@ -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())

View File

@ -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)

View File

@ -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)

View File

@ -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}]

View File

@ -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.

View File

@ -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)

View File

@ -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: ...

View File

@ -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

View File

@ -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",

View File

@ -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):

View File

@ -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}")

View File

@ -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):

View File

@ -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)

View File

@ -116,7 +116,6 @@ class MotorReplicaSetTestBase(MotorTest):
class MotorMockServerTest(MotorTest):
executor = concurrent.futures.ThreadPoolExecutor(1)
def server(self, *args, **kwargs):