From 5fc7c41fb51e0a6ce2c8038808132725ee0da188 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 30 Jul 2025 14:55:53 -0500 Subject: [PATCH] MOTOR-1473 Add support for Python 3.14 and drop 3.9 support (#344) --- .evergreen/config.yml | 54 ++++++------ .github/workflows/test-python.yml | 8 +- CONTRIBUTING.md | 2 +- README.md | 4 +- doc/changelog.rst | 12 +++ doc/installation.rst | 2 +- doc/requirements.rst | 88 ++++++++++--------- motor/_version.py | 6 +- motor/core.pyi | 7 +- motor/metaprogramming.py | 5 +- motor/motor_asyncio.pyi | 3 +- motor/motor_gridfs.pyi | 3 +- motor/motor_tornado.pyi | 3 +- pyproject.toml | 7 +- requirements/test.txt | 2 +- synchro/__init__.py | 2 +- test/asyncio_tests/test_asyncio_collection.py | 2 +- test/check_runtime_types.py | 14 +-- test/test_mypy_fails.py | 2 +- test/test_typing.py | 25 +++--- test/tornado_tests/test_motor_collection.py | 2 +- 21 files changed, 136 insertions(+), 117 deletions(-) diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 30a9c374..fabdd4ad 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -609,14 +609,10 @@ axes: - id: tox-env display_name: "Tox Env RHEL8" values: - - id: "test-pypy39" + - id: "test-pypy310" variables: TOX_ENV: "test" - PYTHON_BINARY: "/opt/python/pypy3.9/bin/python3" - - id: "test-py39" - variables: - TOX_ENV: "test" - PYTHON_BINARY: "/opt/python/3.9/bin/python3" + PYTHON_BINARY: "/opt/python/pypy3.10/bin/python3" - id: "test-py310" variables: TOX_ENV: "test" @@ -633,30 +629,34 @@ axes: variables: TOX_ENV: "test" 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" variables: 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" variables: 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" variables: 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" variables: TOX_ENV: "test-pymongo-latest" - PYTHON_BINARY: "/opt/python/3.9/bin/python3" - - id: "synchro-py39" + PYTHON_BINARY: "/opt/python/3.10/bin/python3" + - id: "synchro-py310" variables: TOX_ENV: "synchro" - PYTHON_BINARY: "/opt/python/3.9/bin/python3" - - id: "synchro-py312" + PYTHON_BINARY: "/opt/python/3.10/bin/python3" + - id: "synchro-py313" variables: TOX_ENV: "synchro" - PYTHON_BINARY: "/opt/python/3.12/bin/python3" + PYTHON_BINARY: "/opt/python/3.13/bin/python3" - id: tox-env-rhel7 display_name: "Tox Env RHEL7" @@ -664,24 +664,20 @@ axes: - id: "test" variables: 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 display_name: "Tox Env OSX" values: - id: "test" variables: 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 display_name: "Tox Env Windows" values: - - id: "test-py39" - variables: - TOX_ENV: "test" - PYTHON_BINARY: "c:/python/Python39/python.exe" - id: "test-py310" variables: TOX_ENV: "test" @@ -694,6 +690,14 @@ axes: variables: TOX_ENV: "test" 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 display_name: "Operating System" @@ -724,7 +728,7 @@ buildvariants: # TODO: synchro needs PyMongo's updated SSL test certs, # which may require Motor test suite changes. - os: "*" - tox-env: ["synchro-py39", "synchro-py312"] + tox-env: ["synchro-py310", "synchro-py313"] ssl: "ssl" tasks: - ".rapid" @@ -784,7 +788,7 @@ buildvariants: - matrix_name: "enterprise-auth" 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: - "rhel84-small" tasks: @@ -796,7 +800,7 @@ buildvariants: - "rhel84-small" expansions: TOX_ENV: "docs" - PYTHON_BINARY: "/opt/python/3.9/bin/python3" + PYTHON_BINARY: "/opt/python/3.10/bin/python3" tasks: - name: "docs" @@ -806,6 +810,6 @@ buildvariants: - "rhel84-small" expansions: TOX_ENV: "doctest" - PYTHON_BINARY: "/opt/python/3.9/bin/python3" + PYTHON_BINARY: "/opt/python/3.10/bin/python3" tasks: - name: "doctest" diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index 39cb424d..50ec3af5 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - python-version: ["3.9", "3.12", "3.13"] + python-version: ["3.10", "3.12", "3.14"] fail-fast: false name: CPython ${{ matrix.python-version }}-${{ matrix.os }} steps: @@ -54,7 +54,7 @@ jobs: persist-credentials: false - uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' cache: 'pip' cache-dependency-path: 'pyproject.toml' - name: Install Python dependencies @@ -72,7 +72,7 @@ jobs: persist-credentials: false - uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' cache: 'pip' cache-dependency-path: 'pyproject.toml' - name: Install Python dependencies @@ -97,7 +97,7 @@ jobs: persist-credentials: false - uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' cache: 'pip' cache-dependency-path: 'pyproject.toml' - name: Install Python dependencies diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 535457b2..6066574b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,7 +35,7 @@ Python version on your path, and run: 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: ```bash diff --git a/README.md b/README.md index eb67f8d5..4ad174f5 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ by asyncio. It requires: - Unix (including macOS) or Windows. - [PyMongo](http://pypi.python.org/pypi/pymongo/) >=4.9,<5 -- Python 3.9+ +- Python 3.10+ Optional dependencies: @@ -192,7 +192,7 @@ ReadTheDocs](https://motor.readthedocs.io/en/stable/examples/index.html). Motor's documentation is on [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/), and [aiohttp](https://github.com/aio-libs/aiohttp), and do `cd doc; make html`. diff --git a/doc/changelog.rst b/doc/changelog.rst index af11cc9d..2f0f214e 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -3,6 +3,18 @@ Changelog .. 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 `_. + +- Add support for Python 3.14. +- Drop support for Python 3.9. + Motor 3.7.1 ----------- diff --git a/doc/installation.rst b/doc/installation.rst index 1193cac3..539c694d 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -27,7 +27,7 @@ asyncio. It requires: * Unix (including macOS) or Windows. * PyMongo_ >=4.9,<5 -* Python 3.9+ +* Python 3.10+ Optional dependencies: diff --git a/doc/requirements.rst b/doc/requirements.rst index 7d592c2b..5f1dc26d 100644 --- a/doc/requirements.rst +++ b/doc/requirements.rst @@ -10,7 +10,7 @@ Requirements The current version of Motor requires: -* CPython 3.9 and later. +* CPython 3.10 and later. * PyMongo_ 4.9 and later. 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. -+---------------------------------------------------------------------+ -| Python Version | -+=====================+=====+=====+=====+=====+=====+=====+=====+=====+ -| | 3.6 | 3.7 | 3.8 | 3.9 | 3.10| 3.11| 3.12| 3.13| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| Motor Version | 1.0 | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 1.1 | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 1.2 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 1.3 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 2.0 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 2.1 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 2.2 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 2.3 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 2.4 | Y | Y | Y | Y |**N**|**N**|**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 2.5 | Y | Y | Y | Y | Y |**N**|**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 3.0 |**N**| Y | Y | Y | Y |**N**|**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 3.1 |**N**| Y | Y | Y | Y | Y |**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 3.2 |**N**| Y | Y | Y | Y | Y |**N**|**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 3.3 |**N**| Y | Y | Y | Y | Y | Y |**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 3.4 |**N**| Y | Y | Y | Y | Y | Y |**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 3.5 |**N**|**N**| Y | Y | Y | Y | Y |**N**| -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 3.6 |**N**|**N**| Y | Y | Y | Y | Y | Y | -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| | 3.7 |**N**|**N**|**N**| Y | Y | Y | Y | Y | -+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +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| 3.14| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| Motor Version | 1.0 | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 1.1 | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 1.2 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 1.3 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 2.0 | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 2.1 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 2.2 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 2.3 | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 2.4 | Y | Y | Y | Y |**N**|**N**|**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 2.5 | Y | Y | Y | Y | Y |**N**|**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 3.0 |**N**| Y | Y | Y | Y |**N**|**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 3.1 |**N**| Y | Y | Y | Y | Y |**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 3.2 |**N**| Y | Y | Y | Y | Y |**N**|**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 3.3 |**N**| Y | Y | Y | Y | Y | Y |**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 3.4 |**N**| Y | Y | Y | Y | Y | Y |**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 3.5 |**N**|**N**| Y | Y | Y | Y | Y |**N**|**N**| ++---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +| | 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 ------------- diff --git a/motor/_version.py b/motor/_version.py index c2a74f43..32dca63b 100644 --- a/motor/_version.py +++ b/motor/_version.py @@ -14,16 +14,16 @@ """Version-related data for motor.""" import re -from typing import List, Tuple, Union +from typing import Union __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\d+).(?P\d+).(?P\d+)(?P.*)" match = re.match(pattern, version) 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"]: parts.append(match["rest"]) elif re.match(r"\d+.\d+", version): diff --git a/motor/core.pyi b/motor/core.pyi index 744e83d1..51f24fc1 100644 --- a/motor/core.pyi +++ b/motor/core.pyi @@ -13,17 +13,12 @@ # limitations under the License. from asyncio import Future +from collections.abc import Callable, Coroutine, Iterable, Mapping, MutableMapping, Sequence from typing import ( Any, - Callable, - Coroutine, Generic, - Iterable, - Mapping, - MutableMapping, NoReturn, Optional, - Sequence, TypeVar, Union, overload, diff --git a/motor/metaprogramming.py b/motor/metaprogramming.py index d291826f..1881e76d 100644 --- a/motor/metaprogramming.py +++ b/motor/metaprogramming.py @@ -15,9 +15,10 @@ """Dynamic class-creation for Motor.""" import functools 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 diff --git a/motor/motor_asyncio.pyi b/motor/motor_asyncio.pyi index c85dce41..882feece 100644 --- a/motor/motor_asyncio.pyi +++ b/motor/motor_asyncio.pyi @@ -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.raw_bson import RawBSONDocument diff --git a/motor/motor_gridfs.pyi b/motor/motor_gridfs.pyi index fea0f8fb..5328847b 100644 --- a/motor/motor_gridfs.pyi +++ b/motor/motor_gridfs.pyi @@ -14,7 +14,8 @@ import datetime 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 gridfs import DEFAULT_CHUNK_SIZE, GridFSBucket, GridIn, GridOut, GridOutCursor # noqa: F401 diff --git a/motor/motor_tornado.pyi b/motor/motor_tornado.pyi index c2f8986e..05d484f7 100644 --- a/motor/motor_tornado.pyi +++ b/motor/motor_tornado.pyi @@ -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.raw_bson import RawBSONDocument diff --git a/pyproject.toml b/pyproject.toml index f9a910be..f856bdf7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version", "dependencies", "optional-dependencies"] description = "Non-blocking MongoDB driver for Tornado or asyncio" readme = "README.md" license = { file = "LICENSE" } -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "A. Jesse Jiryu Davis", email = "jesse@mongodb.com" }, ] @@ -33,13 +33,13 @@ classifiers = [ "Typing :: Typed", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] [project.urls] @@ -66,7 +66,7 @@ test = ["requirements/test.txt"] zstd = ["requirements/zstd.txt"] [tool.mypy] -python_version = "3.9" +python_version = "3.10" strict = true pretty = true show_error_context = true @@ -106,7 +106,6 @@ filterwarnings = [ ] [tool.ruff] -target-version = "py38" line-length = 100 [tool.ruff.lint] diff --git a/requirements/test.txt b/requirements/test.txt index df2ccddf..aa6a77f1 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -3,5 +3,5 @@ mockupdb tornado>=5 aiohttp>=3.8.7 motor[encryption] -cffi>=1.17.0rc1;python_version=="3.13" +cffi>=1.17.0rc1;python_version>="3.13" pytest_asyncio diff --git a/synchro/__init__.py b/synchro/__init__.py index 4e4c37ed..7096d3d2 100644 --- a/synchro/__init__.py +++ b/synchro/__init__.py @@ -299,7 +299,7 @@ class SynchroMeta(type): # Set DelegateProperties' and SynchroProperties' names. 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: attr.name = name diff --git a/test/asyncio_tests/test_asyncio_collection.py b/test/asyncio_tests/test_asyncio_collection.py index 432a6a04..8dd6abd4 100644 --- a/test/asyncio_tests/test_asyncio_collection.py +++ b/test/asyncio_tests/test_asyncio_collection.py @@ -334,7 +334,7 @@ class TestAsyncIOCollection(AsyncIOTestCase): await self.collection.explicit_encryption.find(find_payload).to_list(3), 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) diff --git a/test/check_runtime_types.py b/test/check_runtime_types.py index ba015a9f..11d0100e 100644 --- a/test/check_runtime_types.py +++ b/test/check_runtime_types.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from motor.motor_asyncio import ( AsyncIOMotorChangeStream, @@ -9,9 +9,9 @@ from motor.motor_asyncio import ( AsyncIOMotorDatabase, ) -client: AsyncIOMotorClient[Dict[str, Any]] -db: AsyncIOMotorDatabase[Dict[str, Any]] -cur: AsyncIOMotorCursor[Dict[str, Any]] -coll: AsyncIOMotorCollection[Dict[str, Any]] -cs: AsyncIOMotorChangeStream[Dict[str, Any]] -enc: AsyncIOMotorClientEncryption[Dict[str, Any]] +client: AsyncIOMotorClient[dict[str, Any]] +db: AsyncIOMotorDatabase[dict[str, Any]] +cur: AsyncIOMotorCursor[dict[str, Any]] +coll: AsyncIOMotorCollection[dict[str, Any]] +cs: AsyncIOMotorChangeStream[dict[str, Any]] +enc: AsyncIOMotorClientEncryption[dict[str, Any]] diff --git a/test/test_mypy_fails.py b/test/test_mypy_fails.py index 0f69ca4f..705f329f 100644 --- a/test/test_mypy_fails.py +++ b/test/test_mypy_fails.py @@ -1,7 +1,7 @@ import os import sys import unittest -from typing import Iterable +from collections.abc import Iterable try: from mypy import api diff --git a/test/test_typing.py b/test/test_typing.py index 488e1db3..1c989606 100644 --- a/test/test_typing.py +++ b/test/test_typing.py @@ -17,8 +17,9 @@ sample client code that uses Motor typings. """ import unittest +from collections.abc import Callable, Mapping 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.raw_bson import RawBSONDocument @@ -116,11 +117,11 @@ class TestMotor(AsyncIOTestCase): async def test_bulk_write(self) -> None: await self.collection.insert_one({}) 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) self.assertTrue(result_one.acknowledged) - new_requests: List[Union[InsertOne[Movie], ReplaceOne[Movie]]] = [] - input_list: List[Union[InsertOne[Movie], ReplaceOne[Movie]]] = [ + new_requests: list[Union[InsertOne[Movie], ReplaceOne[Movie]]] = [] + input_list: list[Union[InsertOne[Movie], ReplaceOne[Movie]]] = [ InsertOne(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] async def test_bulk_write_heterogeneous(self) -> None: coll = self.collection - requests: List[Union[InsertOne[Movie], ReplaceOne, DeleteOne]] = [ + requests: list[Union[InsertOne[Movie], ReplaceOne, DeleteOne]] = [ InsertOne(Movie(name="American Graffiti", year=1973)), ReplaceOne({}, {"name": "American Graffiti", "year": "WRONG_TYPE"}), DeleteOne({}), ] result_one = await coll.bulk_write(requests) 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)), ReplaceOne( {}, @@ -154,7 +155,7 @@ class TestMotor(AsyncIOTestCase): @asyncio_test # type:ignore[misc] async def test_command(self) -> None: - result: Dict = await self.cx.admin.command("ping") + result: dict = await self.cx.admin.command("ping") result.items() @asyncio_test # type:ignore[misc] @@ -192,7 +193,7 @@ class TestMotor(AsyncIOTestCase): ] ) - class mydict(Dict[str, Any]): + class mydict(dict[str, Any]): pass result = coll3.aggregate( @@ -226,7 +227,7 @@ class TestDocumentType(AsyncIOTestCase): @only_type_check async def test_explicit_document_type(self) -> None: - client: AsyncIOMotorClient[Dict[str, Any]] = AsyncIOMotorClient() + client: AsyncIOMotorClient[dict[str, Any]] = AsyncIOMotorClient() coll = client.test.test retrieved = await coll.find_one({"_id": "foo"}) assert retrieved is not None @@ -320,7 +321,7 @@ class TestDocumentType(AsyncIOTestCase): @only_type_check async def test_create_index(self) -> None: - client: AsyncIOMotorClient[Dict[str, str]] = AsyncIOMotorClient("test") + client: AsyncIOMotorClient[dict[str, str]] = AsyncIOMotorClient("test") db = client.test async with await client.start_session() as session: index = await db.test.create_index( @@ -361,13 +362,13 @@ class TestCommandDocumentType(AsyncIOTestCase): @only_type_check async def test_default(self) -> None: client: AsyncIOMotorClient = AsyncIOMotorClient() - result: Dict = await client.admin.command("ping") + result: dict = await client.admin.command("ping") result["a"] = 1 @only_type_check async def test_explicit_document_type(self) -> None: 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["a"] = 1 diff --git a/test/tornado_tests/test_motor_collection.py b/test/tornado_tests/test_motor_collection.py index 4a30ecd1..94fa9c2c 100644 --- a/test/tornado_tests/test_motor_collection.py +++ b/test/tornado_tests/test_motor_collection.py @@ -336,7 +336,7 @@ class MotorCollectionTest(MotorTest): await self.collection.explicit_encryption.find(find_payload).to_list(3), 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)