MOTOR-1473 Add support for Python 3.14 and drop 3.9 support (#344)

This commit is contained in:
Steven Silvester 2025-07-30 14:55:53 -05:00 committed by GitHub
parent bbada7dace
commit 5fc7c41fb5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 136 additions and 117 deletions

View File

@ -609,14 +609,10 @@ axes:
- id: tox-env - id: tox-env
display_name: "Tox Env RHEL8" display_name: "Tox Env RHEL8"
values: values:
- id: "test-pypy39" - id: "test-pypy310"
variables: variables:
TOX_ENV: "test" TOX_ENV: "test"
PYTHON_BINARY: "/opt/python/pypy3.9/bin/python3" PYTHON_BINARY: "/opt/python/pypy3.10/bin/python3"
- id: "test-py39"
variables:
TOX_ENV: "test"
PYTHON_BINARY: "/opt/python/3.9/bin/python3"
- id: "test-py310" - id: "test-py310"
variables: variables:
TOX_ENV: "test" TOX_ENV: "test"
@ -633,30 +629,34 @@ axes:
variables: variables:
TOX_ENV: "test" TOX_ENV: "test"
PYTHON_BINARY: "/opt/python/3.13/bin/python3" PYTHON_BINARY: "/opt/python/3.13/bin/python3"
- id: "test-py314"
variables:
TOX_ENV: "test"
PYTHON_BINARY: "/opt/python/3.14/bin/python3"
- id: "test-pymongo-4.9" - id: "test-pymongo-4.9"
variables: variables:
TOX_ENV: "test-pymongo-4.9" TOX_ENV: "test-pymongo-4.9"
PYTHON_BINARY: "/opt/python/3.9/bin/python3" PYTHON_BINARY: "/opt/python/3.10/bin/python3"
- id: "test-pymongo-4.10" - id: "test-pymongo-4.10"
variables: variables:
TOX_ENV: "test-pymongo-4.10" TOX_ENV: "test-pymongo-4.10"
PYTHON_BINARY: "/opt/python/3.9/bin/python3" PYTHON_BINARY: "/opt/python/3.10/bin/python3"
- id: "test-pymongo-4.11" - id: "test-pymongo-4.11"
variables: variables:
TOX_ENV: "test-pymongo-4.11" TOX_ENV: "test-pymongo-4.11"
PYTHON_BINARY: "/opt/python/3.9/bin/python3" PYTHON_BINARY: "/opt/python/3.10/bin/python3"
- id: "test-pymongo-latest" - id: "test-pymongo-latest"
variables: variables:
TOX_ENV: "test-pymongo-latest" TOX_ENV: "test-pymongo-latest"
PYTHON_BINARY: "/opt/python/3.9/bin/python3" PYTHON_BINARY: "/opt/python/3.10/bin/python3"
- id: "synchro-py39" - id: "synchro-py310"
variables: variables:
TOX_ENV: "synchro" TOX_ENV: "synchro"
PYTHON_BINARY: "/opt/python/3.9/bin/python3" PYTHON_BINARY: "/opt/python/3.10/bin/python3"
- id: "synchro-py312" - id: "synchro-py313"
variables: variables:
TOX_ENV: "synchro" TOX_ENV: "synchro"
PYTHON_BINARY: "/opt/python/3.12/bin/python3" PYTHON_BINARY: "/opt/python/3.13/bin/python3"
- id: tox-env-rhel7 - id: tox-env-rhel7
display_name: "Tox Env RHEL7" display_name: "Tox Env RHEL7"
@ -664,24 +664,20 @@ axes:
- id: "test" - id: "test"
variables: variables:
TOX_ENV: "test" TOX_ENV: "test"
PYTHON_BINARY: "/opt/python/3.9/bin/python3" PYTHON_BINARY: "/opt/python/3.10/bin/python3"
# Test Python 3.9 only on Mac. # Test Python 3.10 only on Mac.
- id: tox-env-osx - id: tox-env-osx
display_name: "Tox Env OSX" display_name: "Tox Env OSX"
values: values:
- id: "test" - id: "test"
variables: variables:
TOX_ENV: "test" TOX_ENV: "test"
PYTHON_BINARY: "/Library/Frameworks/Python.framework/Versions/3.9/bin/python3" PYTHON_BINARY: "/Library/Frameworks/Python.framework/Versions/3.10/bin/python3"
- id: tox-env-win - id: tox-env-win
display_name: "Tox Env Windows" display_name: "Tox Env Windows"
values: values:
- id: "test-py39"
variables:
TOX_ENV: "test"
PYTHON_BINARY: "c:/python/Python39/python.exe"
- id: "test-py310" - id: "test-py310"
variables: variables:
TOX_ENV: "test" TOX_ENV: "test"
@ -694,6 +690,14 @@ axes:
variables: variables:
TOX_ENV: "test" TOX_ENV: "test"
PYTHON_BINARY: "c:/python/Python312/python.exe" PYTHON_BINARY: "c:/python/Python312/python.exe"
- id: "test-py313"
variables:
TOX_ENV: "test"
PYTHON_BINARY: "c:/python/Python313/python.exe"
- id: "test-py314"
variables:
TOX_ENV: "test"
PYTHON_BINARY: "c:/python/Python314/python.exe"
- id: os - id: os
display_name: "Operating System" display_name: "Operating System"
@ -724,7 +728,7 @@ buildvariants:
# TODO: synchro needs PyMongo's updated SSL test certs, # TODO: synchro needs PyMongo's updated SSL test certs,
# which may require Motor test suite changes. # which may require Motor test suite changes.
- os: "*" - os: "*"
tox-env: ["synchro-py39", "synchro-py312"] tox-env: ["synchro-py310", "synchro-py313"]
ssl: "ssl" ssl: "ssl"
tasks: tasks:
- ".rapid" - ".rapid"
@ -784,7 +788,7 @@ buildvariants:
- matrix_name: "enterprise-auth" - matrix_name: "enterprise-auth"
display_name: "Enterprise Auth-${tox-env}" display_name: "Enterprise Auth-${tox-env}"
matrix_spec: {"tox-env": ["synchro-py39", "synchro-py312"], ssl: "ssl"} matrix_spec: {"tox-env": ["synchro-py310", "synchro-py313"], ssl: "ssl"}
run_on: run_on:
- "rhel84-small" - "rhel84-small"
tasks: tasks:
@ -796,7 +800,7 @@ buildvariants:
- "rhel84-small" - "rhel84-small"
expansions: expansions:
TOX_ENV: "docs" TOX_ENV: "docs"
PYTHON_BINARY: "/opt/python/3.9/bin/python3" PYTHON_BINARY: "/opt/python/3.10/bin/python3"
tasks: tasks:
- name: "docs" - name: "docs"
@ -806,6 +810,6 @@ buildvariants:
- "rhel84-small" - "rhel84-small"
expansions: expansions:
TOX_ENV: "doctest" TOX_ENV: "doctest"
PYTHON_BINARY: "/opt/python/3.9/bin/python3" PYTHON_BINARY: "/opt/python/3.10/bin/python3"
tasks: tasks:
- name: "doctest" - name: "doctest"

View File

@ -20,7 +20,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
python-version: ["3.9", "3.12", "3.13"] python-version: ["3.10", "3.12", "3.14"]
fail-fast: false fail-fast: false
name: CPython ${{ matrix.python-version }}-${{ matrix.os }} name: CPython ${{ matrix.python-version }}-${{ matrix.os }}
steps: steps:
@ -54,7 +54,7 @@ jobs:
persist-credentials: false persist-credentials: false
- uses: actions/setup-python@v5 - uses: actions/setup-python@v5
with: with:
python-version: 3.9 python-version: '3.10'
cache: 'pip' cache: 'pip'
cache-dependency-path: 'pyproject.toml' cache-dependency-path: 'pyproject.toml'
- name: Install Python dependencies - name: Install Python dependencies
@ -72,7 +72,7 @@ jobs:
persist-credentials: false persist-credentials: false
- uses: actions/setup-python@v5 - uses: actions/setup-python@v5
with: with:
python-version: 3.9 python-version: '3.10'
cache: 'pip' cache: 'pip'
cache-dependency-path: 'pyproject.toml' cache-dependency-path: 'pyproject.toml'
- name: Install Python dependencies - name: Install Python dependencies
@ -97,7 +97,7 @@ jobs:
persist-credentials: false persist-credentials: false
- uses: actions/setup-python@v5 - uses: actions/setup-python@v5
with: with:
python-version: 3.9 python-version: '3.10'
cache: 'pip' cache: 'pip'
cache-dependency-path: 'pyproject.toml' cache-dependency-path: 'pyproject.toml'
- name: Install Python dependencies - name: Install Python dependencies

View File

@ -35,7 +35,7 @@ Python version on your path, and run:
tox -m test tox -m test
``` ```
The doctests pass with Python 3.9+ and a MongoDB 5.0 instance running on The doctests pass with Python 3.10+ and a MongoDB 5.0 instance running on
port 27017: port 27017:
```bash ```bash

View File

@ -116,7 +116,7 @@ by asyncio. It requires:
- Unix (including macOS) or Windows. - Unix (including macOS) or Windows.
- [PyMongo](http://pypi.python.org/pypi/pymongo/) >=4.9,<5 - [PyMongo](http://pypi.python.org/pypi/pymongo/) >=4.9,<5
- Python 3.9+ - Python 3.10+
Optional dependencies: Optional dependencies:
@ -192,7 +192,7 @@ ReadTheDocs](https://motor.readthedocs.io/en/stable/examples/index.html).
Motor's documentation is on Motor's documentation is on
[ReadTheDocs](https://motor.readthedocs.io/en/stable/). [ReadTheDocs](https://motor.readthedocs.io/en/stable/).
Build the documentation with Python 3.9+. Install Build the documentation with Python 3.10+. Install
[sphinx](http://sphinx.pocoo.org/), [Tornado](http://tornadoweb.org/), [sphinx](http://sphinx.pocoo.org/), [Tornado](http://tornadoweb.org/),
and [aiohttp](https://github.com/aio-libs/aiohttp), and do and [aiohttp](https://github.com/aio-libs/aiohttp), and do
`cd doc; make html`. `cd doc; make html`.

View File

@ -3,6 +3,18 @@ Changelog
.. currentmodule:: motor.motor_tornado .. currentmodule:: motor.motor_tornado
Motor 3.8.0
-----------
.. warning:: As of May 14th, 2025, Motor is deprecated in favor of the GA release of the PyMongo Async API.
No new features will be added to Motor, and only bug fixes will be provided until it reaches end of life on May 14th, 2026.
After that, only critical bug fixes will be made until final support ends on May 14th, 2027.
We strongly recommend migrating to the PyMongo Async API while Motor is still supported.
For help transitioning, see the `Migrate to PyMongo Async guide <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/reference/migration/>`_.
- Add support for Python 3.14.
- Drop support for Python 3.9.
Motor 3.7.1 Motor 3.7.1
----------- -----------

View File

@ -27,7 +27,7 @@ asyncio. It requires:
* Unix (including macOS) or Windows. * Unix (including macOS) or Windows.
* PyMongo_ >=4.9,<5 * PyMongo_ >=4.9,<5
* Python 3.9+ * Python 3.10+
Optional dependencies: Optional dependencies:

View File

@ -10,7 +10,7 @@ Requirements
The current version of Motor requires: The current version of Motor requires:
* CPython 3.9 and later. * CPython 3.10 and later.
* PyMongo_ 4.9 and later. * PyMongo_ 4.9 and later.
Motor can integrate with either Tornado or asyncio. Motor can integrate with either Tornado or asyncio.
@ -125,47 +125,51 @@ Motor 3.5 dropped support for Python 3.7 and added support for Python 3.13.
Motor 3.7 dropped support for Python 3.8. Motor 3.7 dropped support for Python 3.8.
+---------------------------------------------------------------------+ Motor 3.8 dropped support for Python 3.9 and added support for Python 3.14.
| Python Version |
+=====================+=====+=====+=====+=====+=====+=====+=====+=====+ +---------------------------------------------------------------------------+
| | 3.6 | 3.7 | 3.8 | 3.9 | 3.10| 3.11| 3.12| 3.13| | Python Version |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +=====================+=====+=====+=====+=====+=====+=====+=====+=====+=====+
| Motor Version | 1.0 | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**| | | 3.6 | 3.7 | 3.8 | 3.9 | 3.10| 3.11| 3.12| 3.13| 3.14|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 1.1 | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**| | Motor Version | 1.0 | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 1.2 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**| | | 1.1 | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 1.3 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**| | | 1.2 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.0 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**| | | 1.3 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.1 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**| | | 2.0 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.2 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**| | | 2.1 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.3 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**| | | 2.2 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.4 | Y | Y | Y | Y |**N**|**N**|**N**|**N**| | | 2.3 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.5 | Y | Y | Y | Y | Y |**N**|**N**|**N**| | | 2.4 | Y | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.0 |**N**| Y | Y | Y | Y |**N**|**N**|**N**| | | 2.5 | Y | Y | Y | Y | Y |**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.1 |**N**| Y | Y | Y | Y | Y |**N**|**N**| | | 3.0 |**N**| Y | Y | Y | Y |**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.2 |**N**| Y | Y | Y | Y | Y |**N**|**N**| | | 3.1 |**N**| Y | Y | Y | Y | Y |**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.3 |**N**| Y | Y | Y | Y | Y | Y |**N**| | | 3.2 |**N**| Y | Y | Y | Y | Y |**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.4 |**N**| Y | Y | Y | Y | Y | Y |**N**| | | 3.3 |**N**| Y | Y | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.5 |**N**|**N**| Y | Y | Y | Y | Y |**N**| | | 3.4 |**N**| Y | Y | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.6 |**N**|**N**| Y | Y | Y | Y | Y | Y | | | 3.5 |**N**|**N**| Y | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.7 |**N**|**N**|**N**| Y | Y | Y | Y | Y | | | 3.6 |**N**|**N**| Y | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.7 |**N**|**N**|**N**| Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.8 |**N**|**N**|**N**|**N**| Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
Not Supported Not Supported
------------- -------------

View File

@ -14,16 +14,16 @@
"""Version-related data for motor.""" """Version-related data for motor."""
import re import re
from typing import List, Tuple, Union from typing import Union
__version__ = "3.7.2.dev0" __version__ = "3.7.2.dev0"
def get_version_tuple(version: str) -> Tuple[Union[int, str], ...]: def get_version_tuple(version: str) -> tuple[Union[int, str], ...]:
pattern = r"(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)" pattern = r"(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)"
match = re.match(pattern, version) match = re.match(pattern, version)
if match: if match:
parts: List[Union[int, str]] = [int(match[part]) for part in ["major", "minor", "patch"]] parts: list[Union[int, str]] = [int(match[part]) for part in ["major", "minor", "patch"]]
if match["rest"]: if match["rest"]:
parts.append(match["rest"]) parts.append(match["rest"])
elif re.match(r"\d+.\d+", version): elif re.match(r"\d+.\d+", version):

View File

@ -13,17 +13,12 @@
# limitations under the License. # limitations under the License.
from asyncio import Future from asyncio import Future
from collections.abc import Callable, Coroutine, Iterable, Mapping, MutableMapping, Sequence
from typing import ( from typing import (
Any, Any,
Callable,
Coroutine,
Generic, Generic,
Iterable,
Mapping,
MutableMapping,
NoReturn, NoReturn,
Optional, Optional,
Sequence,
TypeVar, TypeVar,
Union, Union,
overload, overload,

View File

@ -15,9 +15,10 @@
"""Dynamic class-creation for Motor.""" """Dynamic class-creation for Motor."""
import functools import functools
import inspect import inspect
from typing import Any, Callable, Dict, TypeVar from collections.abc import Callable
from typing import Any, TypeVar
_class_cache: Dict[Any, Any] = {} _class_cache: dict[Any, Any] = {}
# mypy: ignore-errors # mypy: ignore-errors

View File

@ -1,4 +1,5 @@
from typing import Any, Mapping, MutableMapping, Optional, Union from collections.abc import Mapping, MutableMapping
from typing import Any, Optional, Union
from bson import Code, CodecOptions, Timestamp from bson import Code, CodecOptions, Timestamp
from bson.raw_bson import RawBSONDocument from bson.raw_bson import RawBSONDocument

View File

@ -14,7 +14,8 @@
import datetime import datetime
import os import os
from typing import Any, Iterable, Mapping, NoReturn, Optional from collections.abc import Iterable, Mapping
from typing import Any, NoReturn, Optional
from bson import ObjectId from bson import ObjectId
from gridfs import DEFAULT_CHUNK_SIZE, GridFSBucket, GridIn, GridOut, GridOutCursor # noqa: F401 from gridfs import DEFAULT_CHUNK_SIZE, GridFSBucket, GridIn, GridOut, GridOutCursor # noqa: F401

View File

@ -1,4 +1,5 @@
from typing import Any, Mapping, MutableMapping, Optional, Union from collections.abc import Mapping, MutableMapping
from typing import Any, Optional, Union
from bson import Code, CodecOptions, Timestamp from bson import Code, CodecOptions, Timestamp
from bson.raw_bson import RawBSONDocument from bson.raw_bson import RawBSONDocument

View File

@ -8,7 +8,7 @@ dynamic = ["version", "dependencies", "optional-dependencies"]
description = "Non-blocking MongoDB driver for Tornado or asyncio" description = "Non-blocking MongoDB driver for Tornado or asyncio"
readme = "README.md" readme = "README.md"
license = { file = "LICENSE" } license = { file = "LICENSE" }
requires-python = ">=3.9" requires-python = ">=3.10"
authors = [ authors = [
{ name = "A. Jesse Jiryu Davis", email = "jesse@mongodb.com" }, { name = "A. Jesse Jiryu Davis", email = "jesse@mongodb.com" },
] ]
@ -33,13 +33,13 @@ classifiers = [
"Typing :: Typed", "Typing :: Typed",
"Programming Language :: Python", "Programming Language :: Python",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: PyPy",
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
] ]
[project.urls] [project.urls]
@ -66,7 +66,7 @@ test = ["requirements/test.txt"]
zstd = ["requirements/zstd.txt"] zstd = ["requirements/zstd.txt"]
[tool.mypy] [tool.mypy]
python_version = "3.9" python_version = "3.10"
strict = true strict = true
pretty = true pretty = true
show_error_context = true show_error_context = true
@ -106,7 +106,6 @@ filterwarnings = [
] ]
[tool.ruff] [tool.ruff]
target-version = "py38"
line-length = 100 line-length = 100
[tool.ruff.lint] [tool.ruff.lint]

View File

@ -3,5 +3,5 @@ mockupdb
tornado>=5 tornado>=5
aiohttp>=3.8.7 aiohttp>=3.8.7
motor[encryption] motor[encryption]
cffi>=1.17.0rc1;python_version=="3.13" cffi>=1.17.0rc1;python_version>="3.13"
pytest_asyncio pytest_asyncio

View File

@ -299,7 +299,7 @@ class SynchroMeta(type):
# Set DelegateProperties' and SynchroProperties' names. # Set DelegateProperties' and SynchroProperties' names.
for name, attr in attrs.items(): for name, attr in attrs.items():
if isinstance(attr, (MotorAttributeFactory, SynchroAttr)): # noqa: SIM102 if isinstance(attr, (MotorAttributeFactory, SynchroAttr)): # noqa: SIM102,UP038
if attr.name is None: if attr.name is None:
attr.name = name attr.name = name

View File

@ -334,7 +334,7 @@ class TestAsyncIOCollection(AsyncIOTestCase):
await self.collection.explicit_encryption.find(find_payload).to_list(3), await self.collection.explicit_encryption.find(find_payload).to_list(3),
key=lambda x: x["_id"], key=lambda x: x["_id"],
) )
for elem, expected in zip(sorted_find, [6.0, 30.0, 200.0]): for elem, expected in zip(sorted_find, [6.0, 30.0, 200.0], strict=False):
self.assertEqual(elem[f"encrypted{name}"], expected) self.assertEqual(elem[f"encrypted{name}"], expected)

View File

@ -1,4 +1,4 @@
from typing import Any, Dict from typing import Any
from motor.motor_asyncio import ( from motor.motor_asyncio import (
AsyncIOMotorChangeStream, AsyncIOMotorChangeStream,
@ -9,9 +9,9 @@ from motor.motor_asyncio import (
AsyncIOMotorDatabase, AsyncIOMotorDatabase,
) )
client: AsyncIOMotorClient[Dict[str, Any]] client: AsyncIOMotorClient[dict[str, Any]]
db: AsyncIOMotorDatabase[Dict[str, Any]] db: AsyncIOMotorDatabase[dict[str, Any]]
cur: AsyncIOMotorCursor[Dict[str, Any]] cur: AsyncIOMotorCursor[dict[str, Any]]
coll: AsyncIOMotorCollection[Dict[str, Any]] coll: AsyncIOMotorCollection[dict[str, Any]]
cs: AsyncIOMotorChangeStream[Dict[str, Any]] cs: AsyncIOMotorChangeStream[dict[str, Any]]
enc: AsyncIOMotorClientEncryption[Dict[str, Any]] enc: AsyncIOMotorClientEncryption[dict[str, Any]]

View File

@ -1,7 +1,7 @@
import os import os
import sys import sys
import unittest import unittest
from typing import Iterable from collections.abc import Iterable
try: try:
from mypy import api from mypy import api

View File

@ -17,8 +17,9 @@ sample client code that uses Motor typings.
""" """
import unittest import unittest
from collections.abc import Callable, Mapping
from test.asyncio_tests import AsyncIOTestCase, asyncio_test from test.asyncio_tests import AsyncIOTestCase, asyncio_test
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, TypeVar, Union, cast from typing import TYPE_CHECKING, Any, TypeVar, Union, cast
from bson import CodecOptions from bson import CodecOptions
from bson.raw_bson import RawBSONDocument from bson.raw_bson import RawBSONDocument
@ -116,11 +117,11 @@ class TestMotor(AsyncIOTestCase):
async def test_bulk_write(self) -> None: async def test_bulk_write(self) -> None:
await self.collection.insert_one({}) await self.collection.insert_one({})
coll = self.collection coll = self.collection
requests: List[InsertOne[Movie]] = [InsertOne(Movie(name="American Graffiti", year=1973))] requests: list[InsertOne[Movie]] = [InsertOne(Movie(name="American Graffiti", year=1973))]
result_one = await coll.bulk_write(requests) result_one = await coll.bulk_write(requests)
self.assertTrue(result_one.acknowledged) self.assertTrue(result_one.acknowledged)
new_requests: List[Union[InsertOne[Movie], ReplaceOne[Movie]]] = [] new_requests: list[Union[InsertOne[Movie], ReplaceOne[Movie]]] = []
input_list: List[Union[InsertOne[Movie], ReplaceOne[Movie]]] = [ input_list: list[Union[InsertOne[Movie], ReplaceOne[Movie]]] = [
InsertOne(Movie(name="American Graffiti", year=1973)), InsertOne(Movie(name="American Graffiti", year=1973)),
ReplaceOne({}, Movie(name="American Graffiti", year=1973)), ReplaceOne({}, Movie(name="American Graffiti", year=1973)),
] ]
@ -134,14 +135,14 @@ class TestMotor(AsyncIOTestCase):
@asyncio_test # type:ignore[misc] @asyncio_test # type:ignore[misc]
async def test_bulk_write_heterogeneous(self) -> None: async def test_bulk_write_heterogeneous(self) -> None:
coll = self.collection coll = self.collection
requests: List[Union[InsertOne[Movie], ReplaceOne, DeleteOne]] = [ requests: list[Union[InsertOne[Movie], ReplaceOne, DeleteOne]] = [
InsertOne(Movie(name="American Graffiti", year=1973)), InsertOne(Movie(name="American Graffiti", year=1973)),
ReplaceOne({}, {"name": "American Graffiti", "year": "WRONG_TYPE"}), ReplaceOne({}, {"name": "American Graffiti", "year": "WRONG_TYPE"}),
DeleteOne({}), DeleteOne({}),
] ]
result_one = await coll.bulk_write(requests) result_one = await coll.bulk_write(requests)
self.assertTrue(result_one.acknowledged) self.assertTrue(result_one.acknowledged)
requests_two: List[Union[InsertOne[Movie], ReplaceOne[Movie], DeleteOne]] = [ requests_two: list[Union[InsertOne[Movie], ReplaceOne[Movie], DeleteOne]] = [
InsertOne(Movie(name="American Graffiti", year=1973)), InsertOne(Movie(name="American Graffiti", year=1973)),
ReplaceOne( ReplaceOne(
{}, {},
@ -154,7 +155,7 @@ class TestMotor(AsyncIOTestCase):
@asyncio_test # type:ignore[misc] @asyncio_test # type:ignore[misc]
async def test_command(self) -> None: async def test_command(self) -> None:
result: Dict = await self.cx.admin.command("ping") result: dict = await self.cx.admin.command("ping")
result.items() result.items()
@asyncio_test # type:ignore[misc] @asyncio_test # type:ignore[misc]
@ -192,7 +193,7 @@ class TestMotor(AsyncIOTestCase):
] ]
) )
class mydict(Dict[str, Any]): class mydict(dict[str, Any]):
pass pass
result = coll3.aggregate( result = coll3.aggregate(
@ -226,7 +227,7 @@ class TestDocumentType(AsyncIOTestCase):
@only_type_check @only_type_check
async def test_explicit_document_type(self) -> None: async def test_explicit_document_type(self) -> None:
client: AsyncIOMotorClient[Dict[str, Any]] = AsyncIOMotorClient() client: AsyncIOMotorClient[dict[str, Any]] = AsyncIOMotorClient()
coll = client.test.test coll = client.test.test
retrieved = await coll.find_one({"_id": "foo"}) retrieved = await coll.find_one({"_id": "foo"})
assert retrieved is not None assert retrieved is not None
@ -320,7 +321,7 @@ class TestDocumentType(AsyncIOTestCase):
@only_type_check @only_type_check
async def test_create_index(self) -> None: async def test_create_index(self) -> None:
client: AsyncIOMotorClient[Dict[str, str]] = AsyncIOMotorClient("test") client: AsyncIOMotorClient[dict[str, str]] = AsyncIOMotorClient("test")
db = client.test db = client.test
async with await client.start_session() as session: async with await client.start_session() as session:
index = await db.test.create_index( index = await db.test.create_index(
@ -361,13 +362,13 @@ class TestCommandDocumentType(AsyncIOTestCase):
@only_type_check @only_type_check
async def test_default(self) -> None: async def test_default(self) -> None:
client: AsyncIOMotorClient = AsyncIOMotorClient() client: AsyncIOMotorClient = AsyncIOMotorClient()
result: Dict = await client.admin.command("ping") result: dict = await client.admin.command("ping")
result["a"] = 1 result["a"] = 1
@only_type_check @only_type_check
async def test_explicit_document_type(self) -> None: async def test_explicit_document_type(self) -> None:
client: AsyncIOMotorClient = AsyncIOMotorClient() client: AsyncIOMotorClient = AsyncIOMotorClient()
codec_options: CodecOptions[Dict[str, Any]] = CodecOptions() codec_options: CodecOptions[dict[str, Any]] = CodecOptions()
result = await client.admin.command("ping", codec_options=codec_options) result = await client.admin.command("ping", codec_options=codec_options)
result["a"] = 1 result["a"] = 1

View File

@ -336,7 +336,7 @@ class MotorCollectionTest(MotorTest):
await self.collection.explicit_encryption.find(find_payload).to_list(3), await self.collection.explicit_encryption.find(find_payload).to_list(3),
key=lambda x: x["_id"], key=lambda x: x["_id"],
) )
for elem, expected in zip(sorted_find, [6.0, 30.0, 200.0]): for elem, expected in zip(sorted_find, [6.0, 30.0, 200.0], strict=False):
self.assertEqual(elem[f"encrypted{name}"], expected) self.assertEqual(elem[f"encrypted{name}"], expected)