Compare commits

...

101 Commits

Author SHA1 Message Date
dependabot[bot]
3cd98d9b94
Update pytest requirement from >=7 to >=9.0.3 (#382)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-27 19:44:47 -04:00
dependabot[bot]
4fe13c6f25
Bump the actions group with 3 updates (#375)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-04 15:45:08 -08:00
dependabot[bot]
f45226d02f
Bump github/codeql-action from 4.32.2 to 4.32.3 in the actions group (#373)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-17 07:45:36 -06:00
dependabot[bot]
c869e5cd7d
Bump github/codeql-action from 4.32.0 to 4.32.2 in the actions group (#372)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-09 06:25:45 -06:00
dependabot[bot]
160dbce99f
Bump the actions group with 2 updates (#371)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-02 06:28:13 -06:00
Steven Silvester
2433f62fd9
PYTHON-5711 Add zizmor config (#370) 2026-01-30 14:58:55 -06:00
Pablo Castellano
99b56b4633
Fix link in README.md (#369) 2026-01-28 15:06:14 -06:00
dependabot[bot]
bb744b7b5c
Bump the actions group with 2 updates (#368)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-26 07:56:53 -06:00
dependabot[bot]
94a26fa9da
Bump the actions group across 1 directory with 2 updates (#367)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-20 12:12:43 -06:00
dependabot[bot]
b3b102a89d
Bump zizmorcore/zizmor-action from cb3d8e846e148d1111d90b03375b9c03deceda37 to 706c51b5bce7adb027de71ab36d865f5d3fcc7b7 in the actions group (#365)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-29 09:16:34 -06:00
dependabot[bot]
9bae93d875
Bump furo from 2025.9.25 to 2025.12.19 (#364)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-22 07:40:00 -06:00
dependabot[bot]
aab94545dd
Bump github/codeql-action from 4.31.8 to 4.31.9 in the actions group (#363)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-22 07:39:36 -06:00
dependabot[bot]
ccc861c5c7
Bump the actions group with 4 updates (#362)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 06:33:08 -06:00
dependabot[bot]
87d1c9ed87
Bump github/codeql-action from 4.31.6 to 4.31.7 in the actions group (#361)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-08 05:31:38 -06:00
dependabot[bot]
77bd8ba755
Bump the actions group with 2 updates (#360)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 08:24:25 -06:00
dependabot[bot]
61c0e5ccf0
Bump the actions group with 4 updates (#359)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 05:54:44 -06:00
dependabot[bot]
156f3b5f16
Bump zizmorcore/zizmor-action from da5ac40c5419dcf7f21630fb2f95e725ae8fb9d5 to 1aba86d8e1245be7a9ca003d46fcc85a76e6aa61 in the actions group (#358)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 09:24:02 -06:00
dependabot[bot]
bafa539ed0
Bump github/codeql-action from 4.31.0 to 4.31.2 in the actions group (#357)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-03 08:22:02 -06:00
Steven Silvester
d0ed67cd80
MOTOR-1478 Update feedback link (#356) 2025-10-29 07:57:51 -05:00
dependabot[bot]
7785176fdc
Bump the actions group with 3 updates (#355)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 07:45:42 -05:00
dependabot[bot]
536b2dd9d8
Bump github/codeql-action from 3.30.6 to 4.30.8 in the actions group (#354)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Steven Silvester <steve.silvester@mongodb.com>
2025-10-20 09:43:22 -05:00
Steven Silvester
224da029d5
MOTOR-1477 Add workaround for missing compression.zstd (#353) 2025-10-07 12:28:47 -05:00
Steven Silvester
52234081fb
MOTOR-1476 Fix linkcheck for JIRA release notes (#352) 2025-10-07 12:27:48 -05:00
dependabot[bot]
b81fd4e181
Bump the actions group with 2 updates (#351)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-06 16:50:30 -05:00
dependabot[bot]
820ba59552
Update sphinx requirement from <8,>=5.3 to >=5.3,<9 (#348)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Steven Silvester <steven.silvester@ieee.org>
Co-authored-by: Steven Silvester <steve.silvester@mongodb.com>
2025-09-30 10:26:01 -05:00
dependabot[bot]
144c460d8e
Bump furo from 2024.8.6 to 2025.9.25 (#349)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-30 06:29:33 -05:00
dependabot[bot]
d6c688b609
Update sphinx-rtd-theme requirement from <3,>=2 to >=2,<4 (#347)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-30 06:20:09 -05:00
dependabot[bot]
79fd03a68f
Bump the actions group with 7 updates (#350)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Steven Silvester <steve.silvester@mongodb.com>
2025-09-30 06:19:20 -05:00
Steven Silvester
33e4f112c7
MOTOR-1475 Update docs references (#346) 2025-09-15 15:54:53 -05:00
Steven Silvester
12d9d7db05
MOTOR-1474 Update evergreen tests to use RHEL8 (#345) 2025-08-15 13:19:58 -05:00
Noah Stapp
5bcdd4ad6a
MOTOR-1470 - Add Deprecation Warning to Motor (#340) 2025-08-04 14:35:30 -04:00
Steven Silvester
5fc7c41fb5
MOTOR-1473 Add support for Python 3.14 and drop 3.9 support (#344) 2025-07-30 14:55:53 -05:00
Jib
bbada7dace
PYTHON-5438: Create CODEOWNERS for motor (#342)
bypassing docs failures
2025-07-21 13:58:33 -04:00
Steven Silvester
4bb5e32e80
MOTOR-1471 Remove tests for MongoDB 4.0 (#343) 2025-07-15 09:00:32 -05:00
Steven Silvester
8412d20e28
PYTHON-5430 Use the zizmor action (#341) 2025-07-11 16:46:46 -05:00
Noah Stapp
4f1962faf2
MOTOR-1469 - Update the wording of Motor Deprecation Message (#339) 2025-06-26 10:08:37 -04:00
Steven Silvester
edcae5440d
MOTOR-1466 & MOTOR-1467 Skip test and move to MacOS 14 (#338) 2025-06-02 08:22:09 -05:00
mongodb-dbx-release-bot[bot]
71c3cf9dda
BUMP 3.7.2.dev0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2025-05-14 18:57:49 +00:00
Noah Stapp
1b2e255218
Bump version to 3.7.1 for release (#337) 2025-05-14 14:51:33 -04:00
Noah Stapp
c734a3a3f6
Update changelog for 3.7.1 (#336) 2025-05-14 14:34:36 -04:00
Noah Stapp
e20ae5fe5b
PYTHON-5377 - Further update assets to align with GA release of Async PyMongo (#335) 2025-05-14 13:37:35 -04:00
Noah Stapp
f6a70b8c3c
PYTHON-5377 - Update assets to align with GA release of Async PyMongo (#334) 2025-05-13 16:27:42 -04:00
Steven Silvester
83f735ad45
PYTHON-5353 Use pinned sources for GitHub Actions (#333) 2025-04-29 13:56:32 -05:00
Steven Silvester
c291853a13
PYTHON-5348 Fix CodeQL Scanning for GitHub Actions (#332) 2025-04-24 09:27:54 -05:00
Steven Silvester
d5d0995776
MOTOR-1453 & MOTOR-1454 Update expected attributes tests and links (#331) 2025-04-17 09:51:55 -05:00
Steven Silvester
eae1d25c5b
PYTHON-5188 Make version setting a part of the release process (#330) 2025-04-16 10:36:08 -05:00
Steven Silvester
a879ce528a
PYTHON-5131 Migrate off of Ubuntu 20.04 GitHub Actions Runners (#329) 2025-03-05 13:42:02 -06:00
Steven Silvester
6751d66ed3
PYTHON-5136 Add check-json to pre-commit checks (#328) 2025-03-03 12:19:26 -06:00
Steven Silvester
0c8a698fa4
PYTHON-5148 Update SBOM usage for Kondukto (#327) 2025-02-27 10:09:24 -06:00
Scott
e036c6382d
[DOCS] make an explicit warning not to create clients for each request (#324)
Co-authored-by: Steven Silvester <steve.silvester@mongodb.com>
2025-02-26 14:29:57 -08:00
Steven Silvester
e2e967b610
PYTHON-5047 Do not run nightly release check on forks (#325) 2025-02-06 09:23:01 -06:00
mongodb-dbx-release-bot[bot]
9491e32850
BUMP 3.8.0.dev0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2025-01-29 21:13:59 +00:00
mongodb-dbx-release-bot[bot]
251d2a088c
BUMP 3.7.0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2025-01-29 21:08:25 +00:00
Steven Silvester
381fdfbde0
PYTHON-5047 Fix dry run logic in releases yet again (#323) 2025-01-29 15:07:36 -06:00
nfsknight
9cf90a8898
In documentation clarify the purpose behind client creation. (#320)
Co-authored-by: Steven Silvester <steve.silvester@mongodb.com>
Co-authored-by: Steven Silvester <steven.silvester@ieee.org>
2025-01-29 14:03:56 -06:00
Steven Silvester
0259b9f5a8
PYTHON-5047 Improve testing of publish workflows (#322) 2025-01-29 13:39:42 -06:00
Steven Silvester
932bb7382e
MOTOR-1423 Restore compatibility with latest pymongo (#319) 2025-01-29 13:16:09 -06:00
Steven Silvester
1433773db4
PYTHON-5062 Add GitHub Actions CodeQL scanning (#321) 2025-01-27 09:41:00 -06:00
mongodb-dbx-release-bot[bot]
459760f4b4
BUMP 3.6.2.dev0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2025-01-14 18:47:17 +00:00
mongodb-dbx-release-bot[bot]
b9547fa9c2
BUMP 3.6.1
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2025-01-14 18:44:15 +00:00
Steven Silvester
b4f887bf95
Add changelog for 3.6.1 (#317) 2025-01-14 12:43:09 -06:00
Shane Harvey
c1df3a2105
MOTOR-1420 Use generic return type for to_list() (#318) 2025-01-13 12:45:59 -08:00
Ryu Juheon
777d2aadf8
MOTOR-1420 Add detailed return type to to_list method (#316) 2025-01-13 10:29:22 -06:00
Steven Silvester
5cd704ea74
PYTHON-5017 Fix post-publish step (#315) 2025-01-08 16:40:17 -06:00
Steven Silvester
a98698531a
PYTHON-5017 Use a separate PyPI publish step (#314) 2025-01-06 16:24:08 -06:00
Steven Silvester
ff9932ce6f
MOTOR-1413 Install pymongo from v4.9 branch when installing from source (#313) 2024-11-20 11:55:12 -06:00
Steven Silvester
cbef587966
PYTHON-4962 Adopt zizmor GitHub Actions security scanner (#312) 2024-11-12 13:00:11 -06:00
mongodb-dbx-release-bot[bot]
66658d962e
BUMP 3.6.1.dev0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2024-09-18 16:51:48 +00:00
mongodb-dbx-release-bot[bot]
20015b458e
BUMP 3.6.0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2024-09-18 16:47:23 +00:00
Steven Silvester
cf7b4cfe53
MOTOR-1351 Bump minimum PyMongo version to 3.9 (#308) 2024-09-18 11:44:43 -05:00
Steven Silvester
e13888b08e
MOTOR-1364 Add support for Python 3.13 (#307) 2024-09-11 14:45:09 -05:00
Steven Silvester
5c77c38016
MOTOR-1177 Add documentation for Type Annotation Support (#306) 2024-09-11 12:13:31 -05:00
Noah Stapp
2a9225d0ce
MOTOR-1363 - Pass correct parameter to AsyncIOMotorClientEncryption (#305) 2024-09-11 12:32:49 -04:00
Steven Silvester
7050d54dc4
MOTOR-1362 Use furo theme in ReadTheDocs (#304) 2024-09-09 10:08:26 -05:00
Steven Silvester
b652685be4
MOTOR-1360 Use AssumeRole for S3 Access in Evergreen Builds (#303) 2024-09-03 14:41:52 -05:00
Steven Silvester
48f34cbe02
MOTOR-1165 Add documentation for CSOT (#302) 2024-08-29 15:41:41 -05:00
Steven Silvester
a08b1aff9c
MOTOR-1260 Add Projection with aggregation expressions example (#301) 2024-08-23 13:43:16 -05:00
Steven Silvester
00f27f3294
MOTOR-940 Improved Bulk Write API (#300) 2024-08-21 14:27:52 -05:00
Bar Hochman
99cc1b5155
MOTOR-1358 Fix AgnosticGridFSBucket collection type (#299)
Co-authored-by: Shane Harvey <shnhrv@gmail.com>
2024-08-20 14:58:23 -05:00
Steven Silvester
633153954d
MOTOR-1353 Update Synchro Tests to support PyMongo 4.9 (#298) 2024-08-20 13:27:29 -05:00
Steven Silvester
972b7ffef9
MOTOR-1352 Update APIs to Support PyMongo 4.9 (#297) 2024-08-17 07:21:45 -05:00
Steven Silvester
72e68b7569
PYTHON-4541 Add attestations for Python Releases (#296) 2024-07-30 08:35:46 -05:00
Steven Silvester
feb49a882c
MOTOR-1337 Test against Python 3.13 beta (#295) 2024-07-11 07:18:07 -05:00
mongodb-dbx-release-bot[bot]
cc6dfabdde
BUMP 3.6.0.dev0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2024-07-10 20:36:50 +00:00
mongodb-dbx-release-bot[bot]
b1df705d6a
BUMP 3.5.1
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2024-07-10 20:32:54 +00:00
Steven Silvester
9065e68df8
MOTOR-1336 Add changelog for 3.5.1 (#294) 2024-07-10 13:40:18 -05:00
Steven Silvester
13ca542d5e
MOTOR-1335 AsyncIOMotorClient is not suscriptable (#293) 2024-07-09 11:17:36 -05:00
mongodb-dbx-release-bot[bot]
5146708820
BUMP 3.6.0.dev0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2024-06-26 11:40:04 +00:00
mongodb-dbx-release-bot[bot]
cd0316acd2
BUMP 3.5.0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2024-06-26 11:36:37 +00:00
Shane Harvey
73a4cf0e60
PYTHON-4499 Fix test_logging_without_listeners (#292) 2024-06-25 17:40:09 -07:00
mongodb-dbx-release-bot[bot]
fb67b5ff4a
BUMP 3.5.0.dev2
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2024-06-25 21:41:35 +00:00
mongodb-dbx-release-bot[bot]
0967365505
BUMP 3.5.0rc2
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2024-06-25 21:38:10 +00:00
Steven Silvester
fd55efee76
MOTOR-1332 Update changelog for 3.5 (#291) 2024-06-25 15:20:43 -05:00
mongodb-dbx-release-bot[bot]
3ff6c13a53
BUMP 3.5.0.dev1
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2024-06-25 19:21:14 +00:00
mongodb-dbx-release-bot[bot]
0e429b94b0
BUMP 3.5.0rc0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2024-06-25 19:12:57 +00:00
Steven Silvester
be2cd387a2
MOTOR-526 Add evergreen link and version handling for SSDLC (#289) 2024-06-25 14:11:48 -05:00
Steven Silvester
9b2b4f07a3
MOTOR-1332 Add changelog for 3.5 (#290) 2024-06-25 10:40:01 -05:00
Steven Silvester
d235a40f5a
MOTOR-526 SSDLC Conformance (#288)
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
Co-authored-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2024-06-24 10:29:22 -05:00
Steven Silvester
5ef6a15dc6
MOTOR-1314 Test against 8.0 builds (#287) 2024-06-20 08:02:19 -05:00
Steven Silvester
cac2145109
MOTOR-1299 Add components field to SBOM (#285) 2024-06-07 09:57:41 -05:00
Steven Silvester
96297ac947
MOTOR-1327 Use Hatch as Build Backend (#286) 2024-06-07 08:23:48 -05:00
104 changed files with 1735 additions and 780 deletions

View File

@ -54,21 +54,18 @@ functions:
export MONGO_ORCHESTRATION_HOME="$DRIVERS_TOOLS/.evergreen/orchestration"
export MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin"
export UPLOAD_BUCKET="${project}"
cat <<EOT > expansion.yml
CURRENT_VERSION: "$CURRENT_VERSION"
DRIVERS_TOOLS: "$DRIVERS_TOOLS"
MONGO_ORCHESTRATION_HOME: "$MONGO_ORCHESTRATION_HOME"
MONGODB_BINARIES: "$MONGODB_BINARIES"
UPLOAD_BUCKET: "$UPLOAD_BUCKET"
PROJECT_DIRECTORY: "$PROJECT_DIRECTORY"
PREPARE_SHELL: |
set -o errexit
export DRIVERS_TOOLS="$DRIVERS_TOOLS"
export MONGO_ORCHESTRATION_HOME="$MONGO_ORCHESTRATION_HOME"
export MONGODB_BINARIES="$MONGODB_BINARIES"
export UPLOAD_BUCKET="$UPLOAD_BUCKET"
export PROJECT_DIRECTORY="$PROJECT_DIRECTORY"
export TMPDIR="$MONGO_ORCHESTRATION_HOME/db"
export PATH="$MONGODB_BINARIES:$PATH"
@ -99,58 +96,6 @@ functions:
fi
echo "{ \"releases\": { \"default\": \"$MONGODB_BINARIES\" }}" > $MONGO_ORCHESTRATION_HOME/orchestration.config
"upload release":
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: ${project}.tar.gz
remote_file: ${UPLOAD_BUCKET}/${project}-${CURRENT_VERSION}.tar.gz
bucket: mciuploads
permissions: public-read
content_type: ${content_type|application/x-gzip}
# Upload build artifacts that other tasks may depend on
# Note this URL needs to be totally unique, while predictable for the next task
# so it can automatically download the artifacts
"upload build":
# Compress and upload the entire build directory
- command: archive.targz_pack
params:
# Example: mongo_c_driver_releng_9dfb7d741efbca16faa7859b9349d7a942273e43_16_11_08_19_29_52.tar.gz
target: "${build_id}.tar.gz"
source_dir: ${PROJECT_DIRECTORY}/
include:
- "./**"
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: ${build_id}.tar.gz
# Example: /mciuploads/${UPLOAD_BUCKET}/gcc49/9dfb7d741efbca16faa7859b9349d7a942273e43/debug-compile-nosasl-nossl/mongo_c_driver_releng_9dfb7d741efbca16faa7859b9349d7a942273e43_16_11_08_19_29_52.tar.gz
remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${task_name}/${build_id}.tar.gz
bucket: mciuploads
permissions: public-read
content_type: ${content_type|application/x-gzip}
"fetch build":
- command: shell.exec
params:
continue_on_err: true
script: "set -o xtrace && rm -rf ${PROJECT_DIRECTORY}"
- command: s3.get
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${BUILD_NAME}/${build_id}.tar.gz
bucket: mciuploads
local_file: build.tar.gz
- command: shell.exec
params:
continue_on_err: true
# EVG-1105: Use s3.get extract_to: ./
script: "set -o xtrace && cd .. && rm -rf ${PROJECT_DIRECTORY} && mkdir ${PROJECT_DIRECTORY}/ && tar xf build.tar.gz -C ${PROJECT_DIRECTORY}/"
"exec compile script" :
- command: shell.exec
type: test
@ -169,74 +114,10 @@ functions:
${PREPARE_SHELL}
[ -f ${PROJECT_DIRECTORY}/${file} ] && sh ${PROJECT_DIRECTORY}/${file} || echo "${PROJECT_DIRECTORY}/${file} not available, skipping"
"upload docs" :
- command: shell.exec
params:
silent: true
script: |
export AWS_ACCESS_KEY_ID=${aws_key}
export AWS_SECRET_ACCESS_KEY=${aws_secret}
aws s3 cp ${PROJECT_DIRECTORY}/doc/html s3://mciuploads/${UPLOAD_BUCKET}/docs/${CURRENT_VERSION} --recursive --acl public-read --region us-east-1
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: ${PROJECT_DIRECTORY}/doc/html/index.html
remote_file: ${UPLOAD_BUCKET}/docs/${CURRENT_VERSION}/index.html
bucket: mciuploads
permissions: public-read
content_type: text/html
display_name: "Rendered docs"
"upload coverage" :
- command: shell.exec
params:
silent: true
script: |
export AWS_ACCESS_KEY_ID=${aws_key}
export AWS_SECRET_ACCESS_KEY=${aws_secret}
aws s3 cp ${PROJECT_DIRECTORY}/coverage s3://mciuploads/${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/coverage/ --recursive --acl public-read --region us-east-1
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: ${PROJECT_DIRECTORY}/coverage/index.html
remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/coverage/index.html
bucket: mciuploads
permissions: public-read
content_type: text/html
display_name: "Coverage Report"
"upload scan artifacts" :
- command: shell.exec
type: test
params:
script: |
cd
if find ${PROJECT_DIRECTORY}/scan -name \*.html | grep -q html; then
(cd ${PROJECT_DIRECTORY}/scan && find . -name index.html -exec echo "<li><a href='{}'>{}</a></li>" \;) >> scan.html
else
echo "No issues found" > scan.html
fi
- command: shell.exec
params:
silent: true
script: |
export AWS_ACCESS_KEY_ID=${aws_key}
export AWS_SECRET_ACCESS_KEY=${aws_secret}
aws s3 cp ${PROJECT_DIRECTORY}/scan s3://mciuploads/${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/scan/ --recursive --acl public-read --region us-east-1
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: ${PROJECT_DIRECTORY}/scan.html
remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/scan/index.html
bucket: mciuploads
permissions: public-read
content_type: text/html
display_name: "Scan Build Report"
"upload mo artifacts":
- command: ec2.assume_role
params:
role_arn: ${assume_role_arn}
- command: shell.exec
params:
script: |
@ -244,62 +125,27 @@ functions:
find $MONGO_ORCHESTRATION_HOME -name \*.log | xargs tar czf mongodb-logs.tar.gz
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
aws_key: ${AWS_ACCESS_KEY_ID}
aws_secret: ${AWS_SECRET_ACCESS_KEY}
aws_session_token: ${AWS_SESSION_TOKEN}
local_file: mongodb-logs.tar.gz
remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-mongodb-logs.tar.gz
bucket: mciuploads
remote_file: ${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-mongodb-logs.tar.gz
bucket: ${aws_bucket}
permissions: public-read
content_type: ${content_type|application/x-gzip}
display_name: "mongodb-logs.tar.gz"
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
aws_key: ${AWS_ACCESS_KEY_ID}
aws_secret: ${AWS_SECRET_ACCESS_KEY}
aws_session_token: ${AWS_SESSION_TOKEN}
local_file: ${DRIVERS_TOOLS}/.evergreen/orchestration/server.log
remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-orchestration.log
bucket: mciuploads
remote_file: ${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-orchestration.log
bucket: ${aws_bucket}
permissions: public-read
content_type: ${content_type|text/plain}
display_name: "orchestration.log"
"upload working dir":
- command: archive.targz_pack
params:
target: "working-dir.tar.gz"
source_dir: ${PROJECT_DIRECTORY}/
include:
- "./**"
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: working-dir.tar.gz
remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/artifacts/${task_id}-${execution}-working-dir.tar.gz
bucket: mciuploads
permissions: public-read
content_type: ${content_type|application/x-gzip}
display_name: "working-dir.tar.gz"
- command: archive.targz_pack
params:
target: "drivers-dir.tar.gz"
source_dir: ${DRIVERS_TOOLS}
include:
- "./**"
exclude_files:
# Windows cannot read the mongod *.lock files because they are locked.
- "*.lock"
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: drivers-dir.tar.gz
remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/artifacts/${task_id}-${execution}-drivers-dir.tar.gz
bucket: mciuploads
permissions: public-read
content_type: ${content_type|application/x-gzip}
display_name: "drivers-dir.tar.gz"
"upload test results":
- command: attach.results
params:
@ -462,8 +308,6 @@ pre:
- func: "install dependencies"
post:
# Disabled, causing timeouts
# - func: "upload working dir"
- func: "upload mo artifacts"
- func: "upload test results"
- func: "stop mongo-orchestration"
@ -502,60 +346,6 @@ tasks:
# Test tasks {{{
- name: "test-3.6-standalone"
tags: ["3.6", "standalone"]
commands:
- func: "bootstrap mongo-orchestration"
vars:
VERSION: "3.6"
TOPOLOGY: "server"
- func: "run tox"
- name: "test-3.6-replica_set"
tags: ["3.6", "replica_set"]
commands:
- func: "bootstrap mongo-orchestration"
vars:
VERSION: "3.6"
TOPOLOGY: "replica_set"
- func: "run tox"
- name: "test-3.6-sharded_cluster"
tags: ["3.6", "sharded_cluster"]
commands:
- func: "bootstrap mongo-orchestration"
vars:
VERSION: "3.6"
TOPOLOGY: "sharded_cluster"
- func: "run tox"
- name: "test-4.0-standalone"
tags: ["4.0", "standalone"]
commands:
- func: "bootstrap mongo-orchestration"
vars:
VERSION: "4.0"
TOPOLOGY: "server"
- func: "run tox"
- name: "test-4.0-replica_set"
tags: ["4.0", "replica_set"]
commands:
- func: "bootstrap mongo-orchestration"
vars:
VERSION: "4.0"
TOPOLOGY: "replica_set"
- func: "run tox"
- name: "test-4.0-sharded_cluster"
tags: ["4.0", "sharded_cluster"]
commands:
- func: "bootstrap mongo-orchestration"
vars:
VERSION: "4.0"
TOPOLOGY: "sharded_cluster"
- func: "run tox"
- name: "test-4.2-standalone"
tags: ["4.2", "standalone"]
commands:
@ -691,6 +481,33 @@ tasks:
TOPOLOGY: "sharded_cluster"
- func: "run tox"
- name: "test-8.0-standalone"
tags: [ "8.0", "standalone" ]
commands:
- func: "bootstrap mongo-orchestration"
vars:
VERSION: "8.0"
TOPOLOGY: "server"
- func: "run tox"
- name: "test-8.0-replica_set"
tags: [ "8.0", "replica_set" ]
commands:
- func: "bootstrap mongo-orchestration"
vars:
VERSION: "8.0"
TOPOLOGY: "replica_set"
- func: "run tox"
- name: "test-8.0-sharded_cluster"
tags: [ "8.0", "sharded_cluster" ]
commands:
- func: "bootstrap mongo-orchestration"
vars:
VERSION: "8.0"
TOPOLOGY: "sharded_cluster"
- func: "run tox"
- name: "test-latest-standalone"
tags: ["latest", "standalone"]
commands:
@ -772,23 +589,6 @@ tasks:
vars:
TOX_ENV: doctest
- name: "assign-pr-reviewer"
tags: ["pr"]
allowed_requesters: ["patch", "github_pr"]
commands:
- command: shell.exec
type: test
params:
shell: "bash"
working_dir: src
script: |
${PREPARE_SHELL}
set -x
export CONFIG=$PROJECT_DIRECTORY/.github/reviewers.txt
export SCRIPT="$DRIVERS_TOOLS/.evergreen/github_app/assign-reviewer.sh"
bash $SCRIPT -p $CONFIG -h ${github_commit} -o "mongodb" -n "motor"
echo '{"results": [{ "status": "PASS", "test_file": "Build", "log_raw": "Test completed" } ]}' > ${PROJECT_DIRECTORY}/test-results.json
# }}}
axes:
@ -809,18 +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-py38"
variables:
TOX_ENV: "test"
PYTHON_BINARY: "/opt/python/3.8/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"
@ -833,47 +625,59 @@ axes:
variables:
TOX_ENV: "test"
PYTHON_BINARY: "/opt/python/3.12/bin/python3"
- id: "test-py313"
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.10/bin/python3"
- id: "test-pymongo-4.10"
variables:
TOX_ENV: "test-pymongo-4.10"
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.10/bin/python3"
- id: "test-pymongo-latest"
variables:
TOX_ENV: "test-pymongo-latest"
PYTHON_BINARY: "/opt/python/3.8/bin/python3"
- id: "synchro-py38"
PYTHON_BINARY: "/opt/python/3.10/bin/python3"
- id: "synchro-py310"
variables:
TOX_ENV: "synchro"
PYTHON_BINARY: "/opt/python/3.8/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"
- id: tox-env-rhel8
display_name: "Tox Env RHEL8"
values:
- 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.8 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.8/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-py38"
variables:
TOX_ENV: "test"
PYTHON_BINARY: "c:/python/Python39/python.exe"
- id: "test-py39"
variables:
TOX_ENV: "test"
PYTHON_BINARY: "c:/python/Python39/python.exe"
- id: "test-py310"
variables:
TOX_ENV: "test"
@ -886,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"
@ -893,15 +705,12 @@ axes:
- id: "rhel8"
display_name: "RHEL 8.x"
run_on: "rhel84-small"
- id: "rhel7"
display_name: "RHEL 7.x"
run_on: "rhel79-small"
- id: "win"
display_name: "Windows"
run_on: "windows-64-vsMulti-small"
- id: "macos-1100"
display_name: "macOS 11.00"
run_on: "macos-1100"
- id: "macos-1400"
display_name: "macOS 14.00"
run_on: "macos-1400"
buildvariants:
@ -916,24 +725,24 @@ buildvariants:
# TODO: synchro needs PyMongo's updated SSL test certs,
# which may require Motor test suite changes.
- os: "*"
tox-env: ["synchro-py38", "synchro-py312"]
tox-env: ["synchro-py310", "synchro-py313"]
ssl: "ssl"
tasks:
- ".rapid"
- ".latest"
- ".8.0"
- ".7.0"
- ".6.0"
- ".5.0"
- ".4.4"
- ".4.2"
- ".4.0"
- ".3.6"
- matrix_name: "test-rhel7"
display_name: "${os}-${tox-env-rhel7}-${ssl}"
- matrix_name: "test-rhel8"
display_name: "${os}-${tox-env-rhel8}-${ssl}"
matrix_spec:
os: "rhel7"
tox-env-rhel7: "*"
os: "rhel8"
tox-env-rhel8: "*"
ssl: "*"
tasks:
- ".rapid"
@ -952,6 +761,7 @@ buildvariants:
tasks:
- ".rapid"
- ".latest"
- ".8.0"
- ".7.0"
- ".6.0"
- ".5.0"
@ -963,18 +773,19 @@ buildvariants:
- matrix_name: "test-macos"
display_name: "${os}-${tox-env-osx}-${ssl}"
matrix_spec:
os: "macos-1100"
os: "macos-1400"
tox-env-osx: "*"
ssl: "*"
tasks:
- ".rapid"
- ".latest"
- ".8.0"
- ".7.0"
- ".6.0"
- matrix_name: "enterprise-auth"
display_name: "Enterprise Auth-${tox-env}"
matrix_spec: {"tox-env": ["synchro-py38", "synchro-py312"], ssl: "ssl"}
matrix_spec: {"tox-env": ["synchro-py310", "synchro-py313"], ssl: "ssl"}
run_on:
- "rhel84-small"
tasks:
@ -986,7 +797,7 @@ buildvariants:
- "rhel84-small"
expansions:
TOX_ENV: "docs"
PYTHON_BINARY: "/opt/python/3.8/bin/python3"
PYTHON_BINARY: "/opt/python/3.10/bin/python3"
tasks:
- name: "docs"
@ -996,12 +807,6 @@ buildvariants:
- "rhel84-small"
expansions:
TOX_ENV: "doctest"
PYTHON_BINARY: "/opt/python/3.8/bin/python3"
PYTHON_BINARY: "/opt/python/3.10/bin/python3"
tasks:
- name: "doctest"
- name: rhel8-pr-assign-reviewer
display_name: Assign PR Reviewer
run_on: rhel87-small
tasks:
- name: "assign-pr-reviewer"

View File

@ -63,21 +63,16 @@ createvirtualenv () {
fi
python -m pip install -q --upgrade pip
python -m pip install -q --upgrade setuptools wheel tox
python -m pip install -q --upgrade tox
}
if $PYTHON_BINARY -m tox --version; then
run_tox() {
$PYTHON_BINARY -m tox -m $TOX_ENV "$@"
}
else # No toolchain present, set up virtualenv before installing tox
createvirtualenv "$PYTHON_BINARY" toxenv
trap "deactivate; rm -rf toxenv" EXIT HUP
python -m pip install tox
run_tox() {
python -m tox -m $TOX_ENV "$@"
}
fi
# Set up a virtualenv and install tox.
createvirtualenv "$PYTHON_BINARY" toxenv
trap "deactivate; rm -rf toxenv" EXIT HUP
python -m pip install tox
run_tox() {
python -m tox -m $TOX_ENV "$@"
}
run_tox "${@:1}"

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
* @mongodb/dbx-python

View File

@ -18,10 +18,15 @@ on:
branches: [ "master", "*" ]
schedule:
- cron: '35 23 * * 5'
workflow_call:
inputs:
ref:
required: true
type: string
jobs:
analyze:
name: Analyze
name: Analyze ${{ matrix.language }}
runs-on: ubuntu-latest
timeout-minutes: 360
permissions:
@ -31,33 +36,41 @@ jobs:
packages: read
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: python
- language: actions
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref }}
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: 3.x
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@c793b717bc78562f491db7b0e93a3a178b099162 # v4
with:
languages: python
languages: ${{ matrix.language }}
build-mode: none
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
queries: security-extended
config: |
paths-ignore:
- '.github/**'
- 'test/**'
- shell: bash
if: matrix.language == 'python'
run: |
pip install -e .
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@c793b717bc78562f491db7b0e93a3a178b099162 # v4
with:
category: "/language:python"
category: "/language:${{matrix.language}}"

43
.github/workflows/dist.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: Python Dist
concurrency:
group: dist-${{ github.ref }}
cancel-in-progress: true
on:
workflow_dispatch:
workflow_call:
inputs:
ref:
required: true
type: string
pull_request:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+.post[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+[a-b][0-9]+"
- "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
jobs:
build:
name: "Build Dist"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.ref }}
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- name: Install dependencies
run: pip install build
- name: Create packages
run: python -m build .
- name: Store package artifacts
uses: actions/upload-artifact@v7
with:
name: all-dist-${{ github.run_id }}
path: "dist/*"

View File

@ -1,49 +1,115 @@
name: Build and Publish Dist Files to PyPI
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: true
name: Release
on:
workflow_dispatch:
pull_request:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+.post[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+[a-b][0-9]+"
- "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
inputs:
following_version:
description: "The post (dev) version to set"
dry_run:
description: "Dry Run?"
default: false
type: boolean
schedule:
- cron: '30 5 * * *'
env:
# Changes per repo
PRODUCT_NAME: Motor
# Changes per branch
EVERGREEN_PROJECT: motor
# Constant
# inputs will be empty on a scheduled run. so, we only set dry_run
# to 'false' when the input is set to 'false'.
DRY_RUN: ${{ ! contains(inputs.dry_run, 'false') }}
FOLLOWING_VERSION: ${{ inputs.following_version || '' }}
defaults:
run:
shell: bash -eux {0}
jobs:
build:
name: "Build Dist"
pre-publish:
environment: release
if: github.repository_owner == 'mongodb' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
outputs:
version: ${{ steps.pre-publish.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Install dependencies
run: pip install build
- name: Create packages
run: python -m build .
- name: Store package artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist
- uses: mongodb-labs/drivers-github-tools/secure-checkout@v3
with:
app_id: ${{ vars.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: mongodb-labs/drivers-github-tools/setup@v3
with:
aws_role_arn: ${{ secrets.AWS_ROLE_ARN }}
aws_region_name: ${{ vars.AWS_REGION_NAME }}
aws_secret_id: ${{ secrets.AWS_SECRET_ID }}
- uses: mongodb-labs/drivers-github-tools/python/pre-publish@v3
id: pre-publish
with:
dry_run: ${{ env.DRY_RUN }}
build-dist:
needs: [pre-publish]
uses: ./.github/workflows/dist.yml
with:
ref: ${{ needs.pre-publish.outputs.version }}
static-scan:
needs: [pre-publish]
uses: ./.github/workflows/codeql.yml
with:
ref: ${{ needs.pre-publish.outputs.version }}
publish:
needs: build
name: "Publish Dist"
if: startsWith(github.ref, 'refs/tags/')
needs: [build-dist, static-scan]
name: Upload release to PyPI
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write
steps:
- name: Retrieve package artifacts
uses: actions/download-artifact@v4
- name: Upload packages
uses: pypa/gh-action-pypi-publish@release/v1
- name: Download all the dists
uses: actions/download-artifact@v8
with:
name: all-dist-${{ github.run_id }}
path: dist/
- name: Publish package distributions to TestPyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
with:
repository-url: https://test.pypi.org/legacy/
skip-existing: true
attestations: ${{ env.DRY_RUN }}
- name: Publish package distributions to PyPI
if: startsWith(env.DRY_RUN, 'false')
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
post-publish:
needs: [publish]
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write
contents: write
attestations: write
security-events: write
steps:
- uses: mongodb-labs/drivers-github-tools/secure-checkout@v3
with:
app_id: ${{ vars.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: mongodb-labs/drivers-github-tools/setup@v3
with:
aws_role_arn: ${{ secrets.AWS_ROLE_ARN }}
aws_region_name: ${{ vars.AWS_REGION_NAME }}
aws_secret_id: ${{ secrets.AWS_SECRET_ID }}
- uses: mongodb-labs/drivers-github-tools/python/post-publish@v3
with:
following_version: ${{ env.FOLLOWING_VERSION }}
product_name: ${{ env.PRODUCT_NAME }}
evergreen_project: ${{ env.EVERGREEN_PROJECT }}
token: ${{ github.token }}
dry_run: ${{ env.DRY_RUN }}

View File

@ -19,23 +19,26 @@ jobs:
timeout-minutes: 10
strategy:
matrix:
os: [ubuntu-20.04]
python-version: ["3.8", "3.12"]
os: [ubuntu-latest]
python-version: ["3.10", "3.12", "3.14"]
fail-fast: false
name: CPython ${{ matrix.python-version }}-${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: 'pyproject.toml'
allow-prereleases: true
- name: Start MongoDB with Custom Options
run: |
mkdir data
mongod --fork --dbpath=$(pwd)/data --logpath=$PWD/mongo.log --setParameter enableTestCommands=1
- id: setup-mongodb
uses: mongodb-labs/drivers-evergreen-tools@master
with:
version: "8.0"
topology: replica_set
- name: Install Python dependencies
run: |
python -m pip install -U pip tox
@ -44,12 +47,14 @@ jobs:
tox -m test
lint:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v6
with:
python-version: 3.8
persist-credentials: false
- uses: actions/setup-python@v6
with:
python-version: '3.10'
cache: 'pip'
cache-dependency-path: 'pyproject.toml'
- name: Install Python dependencies
@ -58,15 +63,16 @@ jobs:
- name: Run linters
run: |
tox -m lint-manual
tox -m manifest
docs:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v6
with:
python-version: 3.8
persist-credentials: false
- uses: actions/setup-python@v6
with:
python-version: '3.10'
cache: 'pip'
cache-dependency-path: 'pyproject.toml'
- name: Install Python dependencies
@ -76,20 +82,22 @@ jobs:
run: tox -m docs
- name: Run linkcheck
run: tox -m linkcheck
- name: Start MongoDB with Custom Options
run: |
mkdir data
mongod --fork --dbpath=$(pwd)/data --logpath=$PWD/mongo.log --setParameter enableTestCommands=1
- name: Start MongoDB
uses: supercharge/mongodb-github-action@315db7fe45ac2880b7758f1933e6e5d59afd5e94 # 1.12.1
with:
mongodb-version: 5.0
- name: Run doctest
run: tox -m doctest
release:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v6
with:
python-version: 3.8
persist-credentials: false
- uses: actions/setup-python@v6
with:
python-version: '3.10'
cache: 'pip'
cache-dependency-path: 'pyproject.toml'
- name: Install Python dependencies
@ -103,9 +111,11 @@ jobs:
name: Typing Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: "3.10"
cache: 'pip'

21
.github/workflows/zizmor.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: GitHub Actions Security Analysis with zizmor 🌈
on:
push:
branches: ["master"]
pull_request:
branches: ["**"]
jobs:
zizmor:
name: zizmor latest via Cargo
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Run zizmor 🌈
uses: zizmorcore/zizmor-action@0dce2577a4760a2749d8cfb7a84b7d5585ebcb7d

7
.github/zizmor.yml vendored Normal file
View File

@ -0,0 +1,7 @@
rules:
unpinned-uses:
config:
policies:
actions/*: ref-pin
mongodb-labs/drivers-github-tools/*: ref-pin
mongodb-labs/drivers-evergreen-tools: ref-pin

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ doc/_build/
xunit-results
xunit-synchro-results
.eggs
toxenv

View File

@ -6,6 +6,7 @@ repos:
- id: check-added-large-files
- id: check-case-conflict
- id: check-toml
- id: check-json
- id: check-yaml
exclude: template.yaml
- id: debug-statements
@ -65,13 +66,11 @@ repos:
stages: [manual]
- repo: https://github.com/sirosen/check-jsonschema
rev: 0.27.0
rev: 0.29.0
hooks:
- id: check-jsonschema
name: "Check GitHub Workflows"
files: ^\.github/workflows/
types: [yaml]
args: ["--schemafile", "https://json.schemastore.org/github-workflow"]
- id: check-github-workflows
- id: check-github-actions
- id: check-dependabot
- repo: https://github.com/ariebovenberg/slotscheck
rev: v0.17.0

View File

@ -15,7 +15,7 @@ python:
# Install motor itself.
- method: pip
path: .
- requirements: requirements/docs-requirements.txt
- requirements: requirements/docs.txt
build:
os: ubuntu-22.04

View File

@ -35,7 +35,7 @@ Python version on your path, and run:
tox -m test
```
The doctests pass with Python 3.8+ 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

View File

@ -1,31 +0,0 @@
include README.md
include LICENSE
include tox.ini
include sbom.json
include pyproject.toml
include requirements.txt
include doc/Makefile
include doc/examples/tornado_change_stream_templates/index.html
recursive-include doc *.rst
recursive-include doc *.py
recursive-include doc *.png
recursive-include test *.py
recursive-include test *.pem
recursive-include doc *.conf
recursive-include doc *.css
recursive-include doc *.js
recursive-include doc *.txt
recursive-include doc *.bat
recursive-include synchro *.py
recursive-include motor *.pyi
recursive-include motor *.typed
recursive-include motor *.py
recursive-include requirements *.txt
exclude .readthedocs.yaml
exclude .git-blame-ignore-revs
exclude .pre-commit-config.yaml
exclude release.sh
exclude RELEASE.md
exclude CONTRIBUTING.md
exclude .evergreen/*

View File

@ -7,6 +7,13 @@
![image](https://raw.github.com/mongodb/motor/master/doc/_static/motor.png)
> [!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/.
## About
Motor is a full-featured, non-blocking [MongoDB](http://mongodb.org/)
@ -108,8 +115,8 @@ Motor works in all the environments officially supported by Tornado or
by asyncio. It requires:
- Unix (including macOS) or Windows.
- [PyMongo](http://pypi.python.org/pypi/pymongo/) >=4.1,<5
- Python 3.8+
- [PyMongo](http://pypi.python.org/pypi/pymongo/) >=4.9,<5
- Python 3.10+
Optional dependencies:
@ -185,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.8+. 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`.

View File

@ -1,6 +1,12 @@
:mod:`motor.aiohttp` - Integrate Motor with the aiohttp web framework
=====================================================================
.. 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/>`_.
.. currentmodule:: motor.aiohttp
.. automodule:: motor.aiohttp

View File

@ -1,6 +1,12 @@
asyncio GridFS Classes
======================
.. 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/>`_.
.. currentmodule:: motor.motor_asyncio
Store blobs of data in `GridFS <http://dochub.mongodb.org/core/gridfs>`_.

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_asyncio.AsyncIOMotorChangeStream`
======================================================
.. 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/>`_.
.. currentmodule:: motor.motor_asyncio
.. autoclass:: AsyncIOMotorChangeStream

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_asyncio.AsyncIOMotorClient` -- Connection to MongoDB
=========================================================================
.. 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/>`_.
.. autoclass:: motor.motor_asyncio.AsyncIOMotorClient
:members:

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_asyncio.AsyncIOMotorClientEncryption`
==========================================================
.. 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/>`_.
.. currentmodule:: motor.motor_asyncio
.. autoclass:: AsyncIOMotorClientEncryption

View File

@ -1,5 +1,11 @@
:class:`~motor.motor_asyncio.AsyncIOMotorClientSession` -- Sequence of operations
=================================================================================
.. 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/>`_.
.. autoclass:: motor.motor_asyncio.AsyncIOMotorClientSession
:members:

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_asyncio.AsyncIOMotorCollection`
====================================================
.. 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/>`_.
.. currentmodule:: motor.motor_asyncio
.. autoclass:: AsyncIOMotorCollection

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_asyncio.AsyncIOMotorDatabase`
==================================================
.. 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/>`_.
.. currentmodule:: motor.motor_asyncio
.. autoclass:: AsyncIOMotorDatabase

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_asyncio.AsyncIOMotorCursor`
================================================
.. 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/>`_.
.. currentmodule:: motor.motor_asyncio
.. autoclass:: AsyncIOMotorCursor

View File

@ -1,6 +1,12 @@
Motor asyncio API
=================
.. 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/>`_.
.. toctree::
asyncio_motor_client

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_tornado.MotorCursor`
=========================================
.. 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/>`_.
.. currentmodule:: motor.motor_tornado
.. autoclass:: MotorCursor

View File

@ -1,6 +1,12 @@
Motor GridFS Classes
====================
.. 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/>`_.
.. currentmodule:: motor.motor_tornado
Store blobs of data in `GridFS <http://dochub.mongodb.org/core/gridfs>`_.

View File

@ -1,6 +1,12 @@
Motor Tornado API
=================
.. 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/>`_.
.. toctree::
motor_client

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_tornado.MotorChangeStream`
===============================================
.. 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/>`_.
.. currentmodule:: motor.motor_tornado
.. autoclass:: MotorChangeStream

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_tornado.MotorClient` -- Connection to MongoDB
==================================================================
.. 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/>`_.
.. currentmodule:: motor.motor_tornado
.. autoclass:: MotorClient

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_tornado.MotorClientEncryption`
===================================================
.. 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/>`_.
.. currentmodule:: motor.motor_tornado
.. autoclass:: MotorClientEncryption

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_tornado.MotorClientSession` -- Sequence of operations
==========================================================================
.. 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/>`_.
.. currentmodule:: motor.motor_tornado
.. autoclass:: motor.motor_tornado.MotorClientSession

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_tornado.MotorCollection`
=============================================
.. 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/>`_.
.. currentmodule:: motor.motor_tornado
.. autoclass:: MotorCollection

View File

@ -1,6 +1,12 @@
:class:`~motor.motor_tornado.MotorDatabase`
===========================================
.. 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/>`_.
.. currentmodule:: motor.motor_tornado
.. autoclass:: MotorDatabase

View File

@ -1,6 +1,12 @@
:mod:`motor.web` - Integrate Motor with the Tornado web framework
=================================================================
.. 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/>`_.
.. currentmodule:: motor.web
.. automodule:: motor.web

View File

@ -3,10 +3,64 @@ 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 <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
-----------
The 3.7.1 release contains only documentation changes.
.. 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/>`_.
Motor 3.7.0
-----------
- Add support for PyMongo 4.10.
- Drop support for Python 3.8.
- Drop support for MongoDB 3.6.
Motor 3.6.1
-----------
- Add return type to to_list method in stub file.
- Fix ability to install pymongo from source while testing.
Motor 3.6.0
-----------
- Add support for MongoDB 8.0 and PyMongo 4.9.
- The length parameter in :meth:`MotorCursor.to_list` is now optional.
.. note::
This is the last planned minor version of Motor. We are sunsetting Motor in favor of native
asyncio support in PyMongo 4.9+. We will continue to provide security releases and bug fixes for
Motor, but it will not gain new features.
Motor 3.5.1
-----------
- Fix runtime behavior of Motor generic class typing, e.g. ``client: AsyncIOMotorClient[Dict[str, Any]]``.
Motor 3.5.0
-----------
- Drop support for Python 3.7.
- Switch to using Hatchling as a build backend and remove ``setup.py``.
- Add Secure Software Development Life Cycle automation to release process.
GitHub Releases for pymongocrypt now include a Software Bill of Materials, and signature
files corresponding to the distribution files released on PyPI.
Motor 3.4.0
-----------
@ -187,8 +241,8 @@ 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
:class:`~gridfs.GridFS`. See :ref:`removed-gridfs-checksum` for details.
- Removed the ``disable_md5`` parameter for :class:`~pymongo.GridFSBucket` and
:class:`~pymongo.GridFS`. See :ref:`removed-gridfs-checksum` for details.
- PyMongoCrypt 1.2.0 or later is now required for client side field level
encryption support.
@ -655,9 +709,9 @@ Highlights include:
- Complete support for MongoDB 3.4:
- Unicode aware string comparison using collations. See :ref:`PyMongo's examples for collation <collation-on-operation>`.
- Unicode aware string comparison using collations.
- :class:`MotorCursor` and :class:`MotorGridOutCursor` have a new attribute :meth:`~MotorCursor.collation`.
- Support for the new :class:`~bson.decimal128.Decimal128` BSON type.
- Support for the new :class:`~pymongo.decimal128.Decimal128` BSON type.
- A new maxStalenessSeconds read preference option.
- A username is no longer required for the MONGODB-X509 authentication
mechanism when connected to MongoDB >= 3.4.
@ -687,8 +741,8 @@ Highlights include:
- 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
with :class:`~bson.json_util.JSONOptions`.
- Allow :class:`~bson.code.Code` objects to have a scope of ``None``,
with :class:`~pymongo.json_util.JSONOptions`.
- Allow :class:`~pymongo.code.Code` objects to have a scope of ``None``,
signifying no scope. Also allow encoding Code objects with an empty scope
(i.e. ``{}``).
@ -711,7 +765,7 @@ Motor 1.0
Motor now depends on PyMongo 3.3 and later. The move from PyMongo 2 to 3 brings
a large number of API changes, read the `the PyMongo 3 changelog`_ carefully.
.. _the PyMongo 3 changelog: https://pymongo.readthedocs.io/en/stable/changelog.html#changes-in-version-3-0
.. _the PyMongo 3 changelog: https://pymongo.readthedocs.io/en/4.0/changelog.html#changes-in-version-3-0
:class:`MotorReplicaSetClient` is removed
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -808,9 +862,9 @@ The following find/find_one options have been removed:
- await_data (use the new ``cursor_type`` option instead)
- exhaust (use the new ``cursor_type`` option instead)
- as_class (use :meth:`~motor.motor_tornado.MotorCollection.with_options` with
:class:`~bson.codec_options.CodecOptions` instead)
:class:`~pymongo.codec_options.CodecOptions` instead)
- compile_re (BSON regular expressions are always decoded to
:class:`~bson.regex.Regex`)
:class:`~pymongo.regex.Regex`)
The following find/find_one options are deprecated:
@ -1247,8 +1301,7 @@ therefore inheriting
`PyMongo 2.7.2's bug fixes <https://jira.mongodb.org/browse/PYTHON/fixforversion/14005>`_
and
`PyMongo 2.8's bug fixes <https://jira.mongodb.org/browse/PYTHON/fixforversion/14223>`_
and `features
<https://pymongo.readthedocs.io/en/stable/changelog.html#changes-in-version-2-8>`_.
and features.
Fixes `a connection-pool timeout when waitQueueMultipleMS is set
<https://jira.mongodb.org/browse/MOTOR-62>`_ and `two bugs in replica set

View File

@ -4,11 +4,10 @@
# This file is execfile()d with the current directory set to its containing dir.
import os
import sys
from importlib.metadata import metadata
sys.path[0:0] = [os.path.abspath("..")]
from pymongo import version as pymongo_version # noqa: E402
import motor # noqa: E402
# -- General configuration -----------------------------------------------------
@ -74,7 +73,14 @@ pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
linkcheck_ignore = [r"http://localhost:\d+"]
# Links to release notes in jira give 401 error: unauthorized. MOTOR-1476
linkcheck_ignore = [
r"http://localhost:\d+",
r"https://jira\.mongodb\.org/secure/ReleaseNote\.jspa.*",
]
# Allow for flaky links.
linkcheck_retries = 3
# -- Options for extensions ----------------------------------------------------
autoclass_content = "init"
@ -124,15 +130,20 @@ from motor import MotorClient
html_copy_source = False
# Theme gratefully vendored from CPython source.
html_theme = "pydoctheme"
html_theme_path = ["."]
html_theme_options = {"collapsiblesidebar": True}
html_static_path = ["static"]
try:
import furo # noqa: F401
html_sidebars = {
"index": ["globaltoc.html", "searchbox.html"],
}
html_theme = "furo"
except ImportError:
# Theme gratefully vendored from CPython source.
html_theme = "pydoctheme"
html_theme_path = ["."]
html_theme_options = {"collapsiblesidebar": True}
html_static_path = ["static"]
html_sidebars = {
"index": ["globaltoc.html", "searchbox.html"],
}
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
@ -213,11 +224,10 @@ autodoc_default_options = {
"member-order": "groupwise",
}
pymongo_version = metadata("pymongo")["version"]
pymongo_inventory = ("https://pymongo.readthedocs.io/en/%s/" % pymongo_version, None)
intersphinx_mapping = {
"bson": pymongo_inventory,
"gridfs": pymongo_inventory,
"pymongo": pymongo_inventory,
"aiohttp": ("http://aiohttp.readthedocs.io/en/stable/", None),
"tornado": ("http://www.tornadoweb.org/en/stable/", None),

View File

@ -1,6 +1,13 @@
Configuration
=============
.. 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/>`_.
TLS Protocol Version
''''''''''''''''''''

View File

@ -18,3 +18,4 @@ The following is a list of people who have contributed to
- Steven Silvester
- Julius Park
- Doeke Buursma
- Scott Luu

View File

@ -2,6 +2,13 @@
Developer Guide
===============
.. 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/>`_.
Some explanations for those who would like to contribute to Motor development.
Compatibility

View File

@ -4,6 +4,12 @@
Differences between Motor and PyMongo
=====================================
.. 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/>`_.
.. important:: This page describes using Motor with Tornado. Beginning in
version 0.5 Motor can also integrate with asyncio instead of Tornado.
@ -43,8 +49,8 @@ GridFS
- File-like
PyMongo's :class:`~gridfs.grid_file.GridIn` and
:class:`~gridfs.grid_file.GridOut` strive to act like Python's built-in
PyMongo's :class:`~pymongo.grid_file.GridIn` and
:class:`~pymongo.grid_file.GridOut` strive to act like Python's built-in
file objects, so they can be passed to many functions that expect files.
But the I/O methods of :class:`MotorGridIn` and
:class:`MotorGridOut` are asynchronous, so they cannot obey the
@ -53,7 +59,7 @@ GridFS
- Setting properties
In PyMongo, you can set arbitrary attributes on
a :class:`~gridfs.grid_file.GridIn` and they're stored as metadata on
a :class:`~pymongo.grid_file.GridIn` and they're stored as metadata on
the server, even after the ``GridIn`` is closed::
fs = gridfs.GridFSBucket(db)

View File

@ -1,6 +1,13 @@
AIOHTTPGridFS Example
=====================
.. 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/>`_.
Serve pre-compressed static content from GridFS over HTTP. Uses the `aiohttp`_
web framework and :class:`~motor.aiohttp.AIOHTTPGridFS`.

View File

@ -3,6 +3,13 @@
Authentication With Motor
=========================
.. 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/>`_.
This page describes using Motor with Tornado. Beginning in
version 0.5 Motor can also integrate with asyncio instead of Tornado.

View File

@ -5,6 +5,13 @@
Bulk Write Operations
=====================
.. 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/>`_.
.. testsetup::
client = MotorClient()

View File

@ -3,6 +3,13 @@
Client-Side Field Level Encryption
==================================
.. 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/>`_.
Starting in MongoDB 4.2, client-side field level encryption allows an application
to encrypt specific data fields in addition to pre-existing MongoDB
encryption features such as `Encryption at Rest

View File

@ -1,6 +1,13 @@
Motor Examples
==============
.. 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/>`_.
.. seealso:: :doc:`../tutorial-tornado`
.. toctree::
@ -12,5 +19,7 @@ Motor Examples
authentication
aiohttp_gridfs_example
encryption
timeouts
type_hints
See also :ref:`example-web-application-aiohttp`.

View File

@ -3,6 +3,13 @@
Application Performance Monitoring (APM)
========================================
.. 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/>`_.
Motor implements the same `Command Monitoring`_ and `Topology Monitoring`_ specifications as other MongoDB drivers.
Therefore, you can register callbacks to be notified of every MongoDB query or command your program sends, and the server's reply to each, as well as getting a notification whenever the driver checks a server's status or detects a change in your replica set.

View File

@ -3,6 +3,13 @@
Motor Tailable Cursor Example
=============================
.. 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/>`_.
By default, MongoDB will automatically close a cursor when the client has
exhausted all results in the cursor. However, for capped collections you may
use a tailable cursor that remains open after the client exhausts the results

87
doc/examples/timeouts.rst Normal file
View File

@ -0,0 +1,87 @@
.. _timeout-example:
Client Side Operation Timeout
=============================
.. 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/>`_.
PyMongo 4.2 introduced :meth:`~pymongo.timeout` and the ``timeoutMS``
URI and keyword argument to :class:`~pymongo.mongo_client.MongoClient`.
These features allow applications to more easily limit the amount of time that
one or more operations can execute before control is returned to the app. This
timeout applies to all of the work done to execute the operation, including
but not limited to server selection, connection checkout, serialization, and
server-side execution.
:meth:`~pymongo.timeout` is asyncio safe; the timeout only applies to current
Task and multiple Tasks can configure different timeouts concurrently.
:meth:`~pymongo.timeout` can be used identically in Motor 3.1+.
For more information and troubleshooting, see the PyMongo docs on
`Client Side Operation Timeout`_.
.. _Client Side Operation Timeout: https://pymongo.readthedocs.io/en/stable/examples/timeouts.html
Basic Usage
-----------
The following example uses :meth:`~pymongo.timeout` to configure a 10-second
timeout for an :meth:`~pymongo.collection.Collection.insert_one` operation::
import pymongo
import motor.motor_asyncio
client = motor.motor_asyncio.AsyncIOMotorClient()
coll = client.test.test
with pymongo.timeout(10):
await coll.insert_one({"name": "Nunu"})
The :meth:`~pymongo.timeout` applies to all pymongo operations within the block.
The following example ensures that both the ``insert`` and the ``find`` complete
within 10 seconds total, or raise a timeout error::
with pymongo.timeout(10):
await coll.insert_one({"name": "Nunu"})
await coll.find_one({"name": "Nunu"})
When nesting :func:`~pymongo.timeout`, the nested deadline is capped by the outer
deadline. The deadline can only be shortened, not extended.
When exiting the block, the previous deadline is restored::
with pymongo.timeout(5):
await coll.find_one() # Uses the 5 second deadline.
with pymongo.timeout(3):
await coll.find_one() # Uses the 3 second deadline.
await coll.find_one() # Uses the original 5 second deadline.
with pymongo.timeout(10):
await coll.find_one() # Still uses the original 5 second deadline.
await coll.find_one() # Uses the original 5 second deadline.
Timeout errors
--------------
When the :meth:`~pymongo.timeout` with-statement is entered, a deadline is set
for the entire block. When that deadline is exceeded, any blocking pymongo operation
will raise a timeout exception. For example::
try:
with pymongo.timeout(10):
await coll.insert_one({"name": "Nunu"})
await asyncio.sleep(10)
# The deadline has now expired, the next operation will raise
# a timeout exception.
await coll.find_one({"name": "Nunu"})
except PyMongoError as exc:
if exc.timeout:
print(f"block timed out: {exc!r}")
else:
print(f"failed with non-timeout error: {exc!r}")
The :attr:`pymongo.errors.PyMongoError.timeout` property (added in PyMongo 4.2)
will be ``True`` when the error was caused by a timeout and ``False`` otherwise.

View File

@ -3,6 +3,13 @@
Tornado Change Stream Example
=============================
.. 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/>`_.
.. currentmodule:: motor.motor_tornado
Watch a collection for changes with :meth:`MotorCollection.watch` and display

393
doc/examples/type_hints.rst Normal file
View File

@ -0,0 +1,393 @@
.. _type_hints-example:
Type Hints
==========
.. 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/>`_.
As of version 3.3.0, Motor ships with `type hints`_. With type hints, Python
type checkers can easily find bugs before they reveal themselves in your code.
If your IDE is configured to use type hints,
it can suggest more appropriate completions and highlight errors in your code.
Some examples include `PyCharm`_, `Sublime Text`_, and `Visual Studio Code`_.
You can also use the `mypy`_ tool from your command line or in Continuous Integration tests.
All of the public APIs in Motor are fully type hinted, and
several of them support generic parameters for the
type of document object returned when decoding BSON documents.
Due to `limitations in mypy`_, the default
values for generic document types are not yet provided (they will eventually be ``Dict[str, any]``).
For a larger set of examples that use types, see the Motor `test_typing module`_.
If you would like to opt out of using the provided types, add the following to
your `mypy config`_: ::
[mypy-motor]
follow_imports = False
Basic Usage
-----------
Note that a type for :class:`~motor.motor_asyncio.AsyncIOMotorClient` must be specified. Here we use the
default, unspecified document type:
.. code-block:: python
from motor.motor_asyncio import AsyncIOMotorClient
async def main():
client: AsyncIOMotorClient = AsyncIOMotorClient()
collection = client.test.test
inserted = await collection.insert_one({"x": 1, "tags": ["dog", "cat"]})
retrieved = await collection.find_one({"x": 1})
assert isinstance(retrieved, dict)
For a more accurate typing for document type you can use:
.. code-block:: python
from typing import Any, Dict
from motor.motor_asyncio import AsyncIOMotorClient
async def main():
client: AsyncIOMotorClient[Dict[str, Any]] = AsyncIOMotorClient()
collection = client.test.test
inserted = await collection.insert_one({"x": 1, "tags": ["dog", "cat"]})
retrieved = await collection.find_one({"x": 1})
assert isinstance(retrieved, dict)
Typed Client
------------
:class:`~motor.motor_asyncio.AsyncIOMotorClient` is generic on the document type used to decode BSON documents.
You can specify a :class:`~pymongo.raw_bson.RawBSONDocument` document type:
.. code-block:: python
from motor.motor_asyncio import AsyncIOMotorClient
from bson.raw_bson import RawBSONDocument
async def main():
client = AsyncIOMotorClient(document_class=RawBSONDocument)
collection = client.test.test
inserted = await collection.insert_one({"x": 1, "tags": ["dog", "cat"]})
result = await collection.find_one({"x": 1})
assert isinstance(result, RawBSONDocument)
Subclasses of :py:class:`collections.abc.Mapping` can also be used, such as :class:`~pymongo.son.SON`:
.. code-block:: python
from bson import SON
from motor.motor_asyncio import AsyncIOMotorClient
async def main():
client = AsyncIOMotorClient(document_class=SON[str, int])
collection = client.test.test
inserted = await collection.insert_one({"x": 1, "y": 2})
result = await collection.find_one({"x": 1})
assert result is not None
assert result["x"] == 1
Note that when using :class:`~pymongo.son.SON`, the key and value types must be given, e.g. ``SON[str, Any]``.
Typed Collection
----------------
You can use :py:class:`~typing.TypedDict` when using a well-defined schema for the data in a
:class:`~motor.motor_asyncio.AsyncIOMotorClient`. Note that all `schema validation`_ for inserts and updates is done on the server.
These methods automatically add an "_id" field.
.. code-block:: python
from typing import TypedDict
from motor.motor_asyncio import AsyncIOMotorClient
from motor.motor_asyncio import AsyncIOMotorCollection
class Movie(TypedDict):
name: str
year: int
async def main():
client: AsyncIOMotorClient = AsyncIOMotorClient()
collection: AsyncIOMotorCollection[Movie] = client.test.test
inserted = await collection.insert_one(Movie(name="Jurassic Park", year=1993))
result = await collection.find_one({"name": "Jurassic Park"})
assert result is not None
assert result["year"] == 1993
# This will raise a type-checking error, despite being present, because it is added by Motor.
assert result["_id"] # type:ignore[typeddict-item]
This same typing scheme works for all of the insert methods (:meth:`~motor.motor_asyncio.AsyncIOMotorCollection.insert_one`,
:meth:`~motor.motor_asyncio.AsyncIOMotorCollection.insert_many`, and :meth:`~motor.motor_asyncio.AsyncIOMotorCollection.bulk_write`).
For ``bulk_write`` both :class:`~pymongo.operations.InsertOne` and :class:`~pymongo.operations.ReplaceOne` operators are generic.
.. code-block:: python
from typing import TypedDict
from motor.motor_asyncio import AsyncIOMotorClient
from motor.motor_asyncio import AsyncIOMotorCollection
from pymongo.operations import InsertOne
async def main():
client: AsyncIOMotorClient = AsyncIOMotorClient()
collection: AsyncIOMotorCollection[Movie] = client.test.test
inserted = await collection.bulk_write(
[InsertOne(Movie(name="Jurassic Park", year=1993))]
)
result = await collection.find_one({"name": "Jurassic Park"})
assert result is not None
assert result["year"] == 1993
# This will raise a type-checking error, despite being present, because it is added by Motor.
assert result["_id"] # type:ignore[typeddict-item]
Modeling Document Types with TypedDict
--------------------------------------
You can use :py:class:`~typing.TypedDict` to model structured data.
As noted above, Motor will automatically add an ``_id`` field if it is not present. This also applies to TypedDict.
There are three approaches to this:
1. Do not specify ``_id`` at all. It will be inserted automatically, and can be retrieved at run-time, but will yield a type-checking error unless explicitly ignored.
2. Specify ``_id`` explicitly. This will mean that every instance of your custom TypedDict class will have to pass a value for ``_id``.
3. Make use of :py:class:`~typing.NotRequired`. This has the flexibility of option 1, but with the ability to access the ``_id`` field without causing a type-checking error.
Note: to use :py:class:`~typing.NotRequired` in earlier versions of Python (<3.11), use the ``typing_extensions`` package.
.. code-block:: python
from typing import TypedDict, NotRequired
from motor.motor_asyncio import AsyncIOMotorClient
from motor.motor_asyncio import AsyncIOMotorCollection
from bson import ObjectId
class Movie(TypedDict):
name: str
year: int
class ExplicitMovie(TypedDict):
_id: ObjectId
name: str
year: int
class NotRequiredMovie(TypedDict):
_id: NotRequired[ObjectId]
name: str
year: int
async def main():
client: AsyncIOMotorClient = AsyncIOMotorClient()
collection: AsyncIOMotorCollection[Movie] = client.test.test
inserted = await collection.insert_one(Movie(name="Jurassic Park", year=1993))
result = await collection.find_one({"name": "Jurassic Park"})
assert result is not None
# This will yield a type-checking error, despite being present, because it is added by Motor.
assert result["_id"] # type:ignore[typeddict-item]
collection: AsyncIOMotorCollection[ExplicitMovie] = client.test.test
# Note that the _id keyword argument must be supplied
inserted = await collection.insert_one(
ExplicitMovie(_id=ObjectId(), name="Jurassic Park", year=1993)
)
result = await collection.find_one({"name": "Jurassic Park"})
assert result is not None
# This will not raise a type-checking error.
assert result["_id"]
collection: AsyncIOMotorCollection[NotRequiredMovie] = client.test.test
# Note the lack of _id, similar to the first example
inserted = await collection.insert_one(
NotRequiredMovie(name="Jurassic Park", year=1993)
)
result = await collection.find_one({"name": "Jurassic Park"})
assert result is not None
# This will not raise a type-checking error, despite not being provided explicitly.
assert result["_id"]
Typed Database
--------------
While less common, you could specify that the documents in an entire database
match a well-defined schema using :py:class:`~typing.TypedDict`.
.. code-block:: python
from typing import TypedDict
from motor.motor_asyncio import AsyncIOMotorClient
from motor.motor_asyncio import AsyncIOMotorDatabase
class Movie(TypedDict):
name: str
year: int
async def main():
client: AsyncIOMotorClient = AsyncIOMotorClient()
db: AsyncIOMotorDatabase[Movie] = client.test
collection = db.test
inserted = await collection.insert_one({"name": "Jurassic Park", "year": 1993})
result = await collection.find_one({"name": "Jurassic Park"})
assert result is not None
assert result["year"] == 1993
Typed Command
-------------
When using the :meth:`~motor.motor_asyncio.AsyncIOMotorDatabase.command`, you can specify the document type by providing a custom :class:`~pymongo.codec_options.CodecOptions`:
.. code-block:: python
from motor.motor_asyncio import AsyncIOMotorClient
from bson.raw_bson import RawBSONDocument
from bson import CodecOptions
async def main():
client: AsyncIOMotorClient = AsyncIOMotorClient()
options = CodecOptions(RawBSONDocument)
result = await client.admin.command("ping", codec_options=options)
assert isinstance(result, RawBSONDocument)
Custom :py:class:`collections.abc.Mapping` subclasses and :py:class:`~typing.TypedDict` are also supported.
For :py:class:`~typing.TypedDict`, use the form: ``options: CodecOptions[MyTypedDict] = CodecOptions(...)``.
Typed BSON Decoding
-------------------
You can specify the document type returned by :mod:`bson` decoding functions by providing :class:`~pymongo.codec_options.CodecOptions`:
.. code-block:: python
from typing import Any, Dict
from bson import CodecOptions, encode, decode
class MyDict(Dict[str, Any]):
pass
def foo(self):
return "bar"
options = CodecOptions(document_class=MyDict)
doc = {"x": 1, "y": 2}
bsonbytes = encode(doc, codec_options=options)
rt_document = decode(bsonbytes, codec_options=options)
assert rt_document.foo() == "bar"
:class:`~pymongo.raw_bson.RawBSONDocument` and :py:class:`~typing.TypedDict` are also supported.
For :py:class:`~typing.TypedDict`, use the form: ``options: CodecOptions[MyTypedDict] = CodecOptions(...)``.
Troubleshooting
---------------
Client Type Annotation
~~~~~~~~~~~~~~~~~~~~~~
If you forget to add a type annotation for a :class:`~motor.motor_asyncio.AsyncIOMotorClient` object you may get the following ``mypy`` error:
.. code-block:: python
from motor.motor_asyncio import AsyncIOMotorClient
client = AsyncIOMotorClient() # error: Need type annotation for "client"
The solution is to annotate the type as ``client: AsyncIOMotorClient`` or ``client: AsyncIOMotorClient[Dict[str, Any]]``. See `Basic Usage`_.
Incompatible Types
~~~~~~~~~~~~~~~~~~
If you use the generic form of :class:`~motor.motor_asyncio.AsyncIOMotorClient` you
may encounter a ``mypy`` error like:
.. code-block:: python
from motor.motor_asyncio import AsyncIOMotorClient
async def main():
client: AsyncIOMotorClient = AsyncIOMotorClient()
await client.test.test.insert_many(
{"a": 1}
) # error: Dict entry 0 has incompatible type "str": "int";
# expected "Mapping[str, Any]": "int"
The solution is to use ``client: AsyncIOMotorClient[Dict[str, Any]]`` as used in
`Basic Usage`_ .
Actual Type Errors
~~~~~~~~~~~~~~~~~~
Other times ``mypy`` will catch an actual error, like the following code:
.. code-block:: python
from motor.motor_asyncio import AsyncIOMotorClient
from typing import Mapping
async def main():
client: AsyncIOMotorClient = AsyncIOMotorClient()
await client.test.test.insert_one(
[{}]
) # error: Argument 1 to "insert_one" of "Collection" has
# incompatible type "List[Dict[<nothing>, <nothing>]]";
# expected "Mapping[str, Any]"
In this case the solution is to use ``insert_one({})``, passing a document instead of a list.
Another example is trying to set a value on a :class:`~pymongo.raw_bson.RawBSONDocument`, which is read-only.:
.. code-block:: python
from bson.raw_bson import RawBSONDocument
from motor.motor_asyncio import AsyncIOMotorClient
async def main():
client = AsyncIOMotorClient(document_class=RawBSONDocument)
coll = client.test.test
doc = {"my": "doc"}
await coll.insert_one(doc)
retrieved = await coll.find_one({"_id": doc["_id"]})
assert retrieved is not None
assert len(retrieved.raw) > 0
retrieved["foo"] = "bar" # error: Unsupported target for indexed assignment
# ("RawBSONDocument") [index]
.. _PyCharm: https://www.jetbrains.com/help/pycharm/type-hinting-in-product.html
.. _Visual Studio Code: https://code.visualstudio.com/docs/languages/python
.. _Sublime Text: https://github.com/sublimelsp/LSP-pyright
.. _type hints: https://docs.python.org/3/library/typing.html
.. _mypy: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html
.. _limitations in mypy: https://github.com/python/mypy/issues/3737
.. _mypy config: https://mypy.readthedocs.io/en/stable/config_file.html
.. _test_typing module: https://github.com/mongodb/motor/blob/master/test/test_typing.py
.. _schema validation: https://www.mongodb.com/docs/manual/core/schema-validation/#when-to-use-schema-validation

View File

@ -4,6 +4,13 @@ Motor Features
.. currentmodule:: motor.motor_tornado
.. 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/>`_.
Non-Blocking
============
Motor is an asynchronous driver for MongoDB. It can be used from Tornado_ or

View File

@ -4,6 +4,12 @@ Motor: Asynchronous Python driver for MongoDB
.. image:: _static/motor.png
:align: center
.. 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/>`_.
About
-----
@ -54,7 +60,7 @@ project.
Feature Requests / Feedback
---------------------------
Use our `feedback engine <https://feedback.mongodb.com/forums/924286-drivers>`_
Use our `feedback engine <https://feedback.mongodb.com/?category=7548141816650747033>`_
to send us feature requests and general feedback about PyMongo.
Contributing

View File

@ -1,6 +1,13 @@
Installation
============
.. 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/>`_.
Install Motor from PyPI_ with pip_::
$ python3 -m pip install motor
@ -19,8 +26,8 @@ Motor works in all the environments officially supported by Tornado or by
asyncio. It requires:
* Unix (including macOS) or Windows.
* PyMongo_ >=4.1,<5
* Python 3.8+
* PyMongo_ >=4.9,<5
* Python 3.10+
Optional dependencies:
@ -34,7 +41,7 @@ dependency can be installed automatically along with Motor::
similarly,
`MONGODB-AWS <https://pymongo.readthedocs.io/en/stable/examples/authentication.html#mongodb-aws>`_
`MONGODB-AWS <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/security/authentication/aws-iam/#std-label-pymongo-mongodb-aws>`_
authentication requires ``aws`` extra dependency::
$ pip install "motor[aws]"

View File

@ -1,6 +1,13 @@
Motor 2.0 Migration Guide
=========================
.. 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/>`_.
.. currentmodule:: motor.motor_tornado
Motor 2.0 brings a number of changes to Motor 1.0's API. The major version is

View File

@ -1,6 +1,13 @@
Motor 3.0 Migration Guide
=========================
.. 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/>`_.
.. currentmodule:: motor.motor_tornado
Motor 3.0 brings a number of changes to Motor 2.0's API. The major version is
@ -179,7 +186,7 @@ can be changed to this::
``tz_aware`` defaults to ``False``
..................................
``tz_aware``, an argument for :class:`~bson.json_util.JSONOptions`,
``tz_aware``, an argument for :class:`~pymongo.json_util.JSONOptions`,
now defaults to ``False`` instead of ``True``. ``json_util.loads`` now
decodes datetime as naive by default.
@ -409,10 +416,11 @@ documents in your own code before passing them to PyMongo, and transform
incoming documents after receiving them from PyMongo.
Alternatively, if your application uses the ``SONManipulator`` API to convert
custom types to BSON, the :class:`~bson.codec_options.TypeCodec` and
:class:`~bson.codec_options.TypeRegistry` APIs may be a suitable alternative.
For more information, see the
:external:pymongo:doc:`custom type example <examples/custom_type>`.
custom types to BSON, the :class:`~pymongo.codec_options.TypeCodec` and
:class:`~pymongo.codec_options.TypeRegistry` APIs may be a suitable alternative.
For more information, see the `Custom Types documentation`_.
.. _Custom Types documentation: https://www.mongodb.com/docs/languages/python/pymongo-driver/current/data-formats/custom-types/type-codecs/
GridFS changes
--------------
@ -446,13 +454,12 @@ Removed features with no migration path
Encoding a UUID raises an error by default
..........................................
The default uuid_representation for :class:`~bson.codec_options.CodecOptions`,
:class:`~bson.json_util.JSONOptions`, and
The default uuid_representation for :class:`~pymongo.codec_options.CodecOptions`,
:class:`~pymongo.json_util.JSONOptions`, and
:class:`~motor.motor_tornado.MotorClient` has been changed from
:data:`bson.binary.UuidRepresentation.PYTHON_LEGACY` to
:data:`bson.binary.UuidRepresentation.UNSPECIFIED`. Attempting to encode a
:class:`uuid.UUID` instance to BSON or JSON now produces an error by default.
See :ref:`handling-uuid-data-example` for details.
Upgrade to Motor 3.0

View File

@ -1,14 +1,21 @@
Requirements
============
.. 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/>`_.
The current version of Motor requires:
* CPython 3.8 and later.
* PyMongo_ 4.5 and later.
* CPython 3.10 and later.
* PyMongo_ 4.9 and later.
Motor can integrate with either Tornado or asyncio.
The default authentication mechanism for MongoDB 3.0+ is SCRAM-SHA-1.
The default authentication mechanism for MongoDB is SCRAM-SHA-1.
Building the docs requires `sphinx`_.
@ -37,29 +44,41 @@ Motor and PyMongo
+-------------------+-----------------+
| 3.3 | 4.5+ |
+-------------------+-----------------+
| 3.4 | 4.6+ |
| 3.4 | 4.5+ |
+-------------------+-----------------+
| 3.5 | 4.5+ |
+-------------------+-----------------+
| 3.6 | 4.9 |
+-------------------+-----------------+
| 3.7 | 4.9+ |
+-------------------+-----------------+
Motor and MongoDB
`````````````````
+---------------------------------------------------------------+
| MongoDB Version |
+=====================+=====+=====+=====+=====+=====+=====+=====+
| | 3.6 | 4.0 | 4.2 | 4.4 | 5.0 | 6.0 | 7.0 |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| Motor Version | 2.5 | Y | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.0 | Y | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.1 | Y | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.2 | Y | Y | Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.3 | Y | Y | Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.4 | Y | Y | Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
+---------------------------------------------------------------------+
| MongoDB Version |
+=====================+=====+=====+=====+=====+=====+=====+=====+=====+
| | 3.6 | 4.0 | 4.2 | 4.4 | 5.0 | 6.0 | 7.0 | 8.0 |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| Motor Version | 2.5 | Y | Y | Y | Y | Y |**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.0 | Y | Y | Y | Y | Y |**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.1 | Y | Y | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.2 | Y | Y | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.3 | Y | Y | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.4 | Y | Y | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.5 | Y | Y | Y | Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.6 | Y | Y | Y | Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.7 | N | Y | Y | Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
There is no relationship between PyMongo and MongoDB version numbers, although
the numbers happen to be close or equal in recent releases of PyMongo and MongoDB.
@ -67,7 +86,7 @@ Use `the PyMongo compatibility matrix`_ to determine what MongoDB version is
supported by PyMongo. Use the compatibility matrix above to determine what
MongoDB version Motor supports.
.. _the PyMongo compatibility matrix: https://mongodb.com/docs/drivers/pymongo#mongodb-compatibility
.. _the PyMongo compatibility matrix: https://www.mongodb.com/docs/languages/python/pymongo-driver/current/reference/compatibility/
Motor and Tornado
`````````````````
@ -102,45 +121,55 @@ Motor 3.1.1 added support for Python 3.11.
Motor 3.3 added support for Python 3.12.
Motor 3.5 dropped support for Python 3.7.
Motor 3.5 dropped support for Python 3.7 and added support for Python 3.13.
+---------------------------------------------------------------+
| Python Version |
+=====================+=====+=====+=====+=====+=====+=====+=====+
| | 3.6 | 3.7 | 3.8 | 3.9 | 3.10| 3.11| 3.12|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| Motor Version | 1.0 | Y |**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 1.1 | Y |**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 1.2 | Y | Y |**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 1.3 | Y | Y |**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.0 | Y | Y |**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.1 | Y | Y | Y |**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.2 | Y | Y | Y |**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.3 | Y | Y | Y |**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.4 | Y | Y | Y | Y |**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.5 | Y | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.0 |**N**| Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.1 |**N**| Y | Y | Y | Y |**Y**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.2 |**N**| Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.3 |**N**| Y | Y | Y | Y | Y |**Y**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.4 |**N**| Y | Y | Y | Y | Y |**Y**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.5 |**N**|**N**| Y | Y | Y | Y |**Y**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
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| 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
-------------

View File

@ -3,6 +3,13 @@
Tutorial: Using Motor With :mod:`asyncio`
=========================================
.. 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/>`_.
.. These setups are redundant because I can't figure out how to make doctest
run a common setup *before* the setup for the two groups. A "testsetup:: *"
is the obvious answer, but it's run *after* group-specific setup.
@ -78,7 +85,9 @@ Motor, like PyMongo, represents data with a 4-level object hierarchy:
Creating a Client
-----------------
You typically create a single instance of :class:`~motor.motor_asyncio.AsyncIOMotorClient` at the time your
Creating a client is what establishes a connection to MongoDB and tells your
app what deployment (i.e. cluster) to connect to. You typically create a single
instance of :class:`~motor.motor_asyncio.AsyncIOMotorClient` at the time your
application starts up.
.. doctest:: before-inserting-2000-docs
@ -263,7 +272,7 @@ You can apply a sort, limit, or skip to a query before you begin iterating:
The cursor does not actually retrieve each document from the server
individually; it gets documents efficiently in `large batches`_.
.. _`large batches`: https://mongodb.com/docs/manual/tutorial/iterate-a-cursor/#cursor-batches
.. _`large batches`: https://www.mongodb.com/docs/manual/core/cursors/#cursor-batches
Counting Documents
------------------
@ -452,11 +461,9 @@ to an :class:`aiohttp.web.Application`:
:start-after: main-start
:end-before: main-end
Note that it is a common mistake to create a new client object for every
request; this comes at a dire performance cost. Create the client
when your application starts and reuse that one client for the lifetime
of the process. You can maintain the client by storing a database handle
from the client on your application object, as shown in this example.
.. warning:: It is a common mistake to create a new client object for every request; this comes at a dire performance cost.
Create the client when your application starts and reuse that one client for the lifetime of the process.
You can maintain the client by storing a database handle from the client on your application object, as shown in this example.
Visit ``localhost:8080/pages/page-one`` and the server responds "Hello!".
At ``localhost:8080/pages/page-two`` it responds "Goodbye." At other URLs it

View File

@ -3,6 +3,13 @@
Tutorial: Using Motor With Tornado
==================================
.. 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/>`_.
.. These setups are redundant because I can't figure out how to make doctest
run a common setup *before* the setup for the two groups. A "testsetup:: *"
is the obvious answer, but it's run *after* group-specific setup.
@ -86,8 +93,9 @@ Motor, like PyMongo, represents data with a 4-level object hierarchy:
Creating a Client
-----------------
You typically create a single instance of :class:`MotorClient` at the time your
application starts up.
Creating a client is what establishes a connection to MongoDB and tells your
app what deployment (i.e. cluster) to connect to. You typically create a single
instance of :class:`MotorClient` at the time your application starts up.
.. doctest:: before-inserting-2000-docs
@ -151,10 +159,10 @@ makes it available to request handlers::
def get(self):
db = self.settings['db']
It is a common mistake to create a new client object for every
request; **this comes at a dire performance cost**. Create the client
when your application starts and reuse that one client for the lifetime
of the process, as shown in these examples.
.. warning:: It is a common mistake to create a new client object for every
request; **this comes at a dire performance cost**. Create the client
when your application starts and reuse that one client for the lifetime
of the process, as shown in these examples.
The Tornado :class:`~tornado.httpserver.HTTPServer` class's :meth:`start`
method is a simple way to fork multiple web servers and use all of your
@ -351,7 +359,7 @@ You can apply a sort, limit, or skip to a query before you begin iterating:
The cursor does not actually retrieve each document from the server
individually; it gets documents efficiently in `large batches`_.
.. _`large batches`: https://mongodb.com/docs/manual/tutorial/iterate-a-cursor/#cursor-batches
.. _`large batches`: https://www.mongodb.com/docs/manual/core/cursors/#cursor-batches
Counting Documents
------------------

View File

@ -13,13 +13,29 @@
# limitations under the License.
"""Version-related data for motor."""
version_tuple = (3, 5, 0, ".dev0")
import re
from typing import Union
__version__ = "3.7.2.dev0"
def get_version_tuple(version: str) -> tuple[Union[int, str], ...]:
pattern = r"(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)"
match = re.match(pattern, version)
if match:
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):
parts = [int(part) for part in version.split(".")]
else:
raise ValueError("Could not parse version")
return tuple(parts)
version_tuple = get_version_tuple(__version__)
version = __version__
def get_version_string() -> str:
if isinstance(version_tuple[-1], str):
return ".".join(map(str, version_tuple[:-1])) + version_tuple[-1]
return ".".join(map(str, version_tuple)) # type:ignore[unreachable]
version = get_version_string()
return __version__

View File

@ -27,7 +27,8 @@ from pymongo.change_stream import ChangeStream
from pymongo.client_session import ClientSession
from pymongo.collection import Collection
from pymongo.command_cursor import CommandCursor, RawBatchCommandCursor
from pymongo.cursor import _QUERY_OPTIONS, Cursor, RawBatchCursor
from pymongo.cursor import Cursor, RawBatchCursor
from pymongo.cursor_shared import _QUERY_OPTIONS
from pymongo.database import Database
from pymongo.driver_info import DriverInfo
from pymongo.encryption import ClientEncryption
@ -90,6 +91,11 @@ class AgnosticBase:
class AgnosticBaseProperties(AgnosticBase):
# Allow the use of generics at runtime.
@classmethod
def __class_getitem__(cls, key: str) -> object:
return cls
codec_options = ReadOnlyProperty()
read_preference = ReadOnlyProperty()
read_concern = ReadOnlyProperty()
@ -104,6 +110,7 @@ class AgnosticClient(AgnosticBaseProperties):
arbiters = ReadOnlyProperty()
close = DelegateMethod()
__hash__ = DelegateMethod()
bulk_write = AsyncRead()
drop_database = AsyncCommand().unwrap("MotorDatabase")
options = ReadOnlyProperty()
get_database = DelegateMethod(doc=docstrings.get_database_doc).wrap(Database)
@ -121,6 +128,7 @@ class AgnosticClient(AgnosticBaseProperties):
server_info = AsyncRead()
topology_description = ReadOnlyProperty()
start_session = AsyncCommand(doc=docstrings.start_session_doc).wrap(ClientSession)
_connect = AsyncRead()
def __init__(self, *args, **kwargs):
"""Create a new connection to a single MongoDB instance at *host:port*.
@ -163,6 +171,21 @@ class AgnosticClient(AgnosticBaseProperties):
delegate = self.__delegate_class__(*args, **kwargs)
super().__init__(delegate)
warnings.warn(
DeprecationWarning(
"""Motor is deprecated as of May 14th, 2025,
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 here:
https://www.mongodb.com/docs/languages/python/pymongo-driver/current/reference/migration/
"""
),
stacklevel=2,
)
@property
def io_loop(self):
if self._io_loop is None:
@ -219,7 +242,7 @@ class AgnosticClient(AgnosticBaseProperties):
to use for the aggregation.
- `start_at_operation_time` (optional): If provided, the resulting
change stream will only return changes that occurred at or after
the specified :class:`~bson.timestamp.Timestamp`. Requires
the specified :class:`~pymongo.timestamp.Timestamp`. Requires
MongoDB >= 4.0.
- `session` (optional): a
:class:`~pymongo.client_session.ClientSession`.
@ -685,7 +708,7 @@ class AgnosticDatabase(AgnosticBaseProperties):
to use for the aggregation.
- `start_at_operation_time` (optional): If provided, the resulting
change stream will only return changes that occurred at or after
the specified :class:`~bson.timestamp.Timestamp`. Requires
the specified :class:`~pymongo.timestamp.Timestamp`. Requires
MongoDB >= 4.0.
- `session` (optional): a
:class:`~pymongo.client_session.ClientSession`.
@ -763,7 +786,7 @@ class AgnosticDatabase(AgnosticBaseProperties):
.. note:: the order of keys in the `command` document is
significant (the "verb" must come first), so commands
which require multiple keys (e.g. `findandmodify`)
should use an instance of :class:`~bson.son.SON` or
should use an instance of :class:`~pymongo.son.SON` or
a string and kwargs instead of a Python `dict`.
- `value` (optional): value to use for the command verb when
@ -774,7 +797,7 @@ class AgnosticDatabase(AgnosticBaseProperties):
read preference configured for the transaction.
Otherwise, defaults to
:attr:`~pymongo.read_preferences.ReadPreference.PRIMARY`.
- `codec_options`: A :class:`~bson.codec_options.CodecOptions`
- `codec_options`: A :class:`~pymongo.codec_options.CodecOptions`
instance.
- `session` (optional): A
:class:`MotorClientSession`.
@ -1376,6 +1399,11 @@ class AgnosticCollection(AgnosticBaseProperties):
class AgnosticBaseCursor(AgnosticBase):
"""Base class for AgnosticCursor and AgnosticCommandCursor"""
# Allow the use of generics at runtime.
@classmethod
def __class_getitem__(cls, key: str) -> object:
return cls
_async_close = AsyncRead(attr_name="close")
_refresh = AsyncRead()
address = ReadOnlyProperty()
@ -1484,7 +1512,7 @@ class AgnosticBaseCursor(AgnosticBase):
.. versionchanged:: 2.2
Deprecated.
.. _`large batches`: https://www.mongodb.com/docs/manual/tutorial/iterate-a-cursor/#cursor-batches
.. _`large batches`: https://www.mongodb.com/docs/manual/core/cursors/#cursor-batches
.. _`gen.coroutine`: http://tornadoweb.org/en/stable/gen.html
"""
warnings.warn(
@ -1608,7 +1636,7 @@ class AgnosticBaseCursor(AgnosticBase):
self._framework.call_soon(self.get_io_loop(), functools.partial(callback, None, None))
@coroutine_annotation
def to_list(self, length):
def to_list(self, length=None):
"""Get a list of documents.
.. testsetup:: to_list
@ -1759,8 +1787,6 @@ class AgnosticCursor(AgnosticBaseCursor):
comment = MotorCursorChainingMethod()
allow_disk_use = MotorCursorChainingMethod()
_Cursor__die = AsyncRead()
def rewind(self):
"""Rewind this cursor to its unevaluated state."""
self.delegate.rewind()
@ -1778,13 +1804,13 @@ class AgnosticCursor(AgnosticBaseCursor):
return self.__class__(self.delegate.__deepcopy__(memo), self.collection)
def _query_flags(self):
return self.delegate._Cursor__query_flags
return self.delegate._query_flags
def _data(self):
return self.delegate._Cursor__data
return self.delegate._data
def _killed(self):
return self.delegate._Cursor__killed
return self.delegate._killed
class AgnosticRawBatchCursor(AgnosticCursor):
@ -1796,8 +1822,6 @@ class AgnosticCommandCursor(AgnosticBaseCursor):
__motor_class_name__ = "MotorCommandCursor"
__delegate_class__ = CommandCursor
_CommandCursor__die = AsyncRead()
async def try_next(self):
"""Advance the cursor without blocking indefinitely.
@ -1824,10 +1848,10 @@ class AgnosticCommandCursor(AgnosticBaseCursor):
return 0
def _data(self):
return self.delegate._CommandCursor__data
return self.delegate._data
def _killed(self):
return self.delegate._CommandCursor__killed
return self.delegate._killed
class AgnosticRawBatchCommandCursor(AgnosticCommandCursor):
@ -1839,25 +1863,25 @@ class _LatentCursor:
"""Take the place of a PyMongo CommandCursor until aggregate() begins."""
alive = True
_CommandCursor__data = []
_CommandCursor__id = None
_CommandCursor__killed = False
_CommandCursor__sock_mgr = None
_CommandCursor__session = None
_CommandCursor__explicit_session = None
_data = []
_id = None
_killed = False
_sock_mgr = None
_session = None
_explicit_session = None
cursor_id = None
def __init__(self, collection):
self._CommandCursor__collection = collection.delegate
self._collection = collection.delegate
def _CommandCursor__end_session(self, *args, **kwargs):
def _end_session(self, *args, **kwargs):
pass
def _CommandCursor__die(self, *args, **kwargs):
def _die_lock(self, *args, **kwargs):
pass
def clone(self):
return _LatentCursor(self._CommandCursor__collection)
return _LatentCursor(self._collection)
def rewind(self):
pass
@ -1914,9 +1938,9 @@ class AgnosticLatentCommandCursor(AgnosticCommandCursor):
# Return early if the task was cancelled.
if original_future.done():
return
if self.delegate._CommandCursor__data or not self.delegate.alive:
if self.delegate._data or not self.delegate.alive:
# _get_more is complete.
original_future.set_result(len(self.delegate._CommandCursor__data))
original_future.set_result(len(self.delegate._data))
else:
# Send a getMore.
future = super()._get_more()
@ -1940,6 +1964,11 @@ class AgnosticChangeStream(AgnosticBase):
resume_token = ReadOnlyProperty()
# Allow the use of generics at runtime.
@classmethod
def __class_getitem__(cls, key: str) -> object:
return cls
def __init__(
self,
target,
@ -2131,6 +2160,11 @@ class AgnosticClientEncryption(AgnosticBase):
get_key_by_alt_name = AsyncCommand()
remove_key_alt_name = AsyncCommand()
# Allow the use of generics at runtime.
@classmethod
def __class_getitem__(cls, key: str) -> object:
return cls
def __init__(
self,
kms_providers,

View File

@ -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,
@ -40,10 +35,11 @@ from bson.raw_bson import RawBSONDocument
from pymongo import IndexModel, ReadPreference, WriteConcern
from pymongo.change_stream import ChangeStream
from pymongo.client_options import ClientOptions
from pymongo.client_session import _T, ClientSession, SessionOptions, TransactionOptions
from pymongo.collection import Collection, ReturnDocument, _WriteOp # noqa: F401
from pymongo.client_session import ClientSession, SessionOptions, TransactionOptions
from pymongo.collection import Collection, ReturnDocument # noqa: F401
from pymongo.command_cursor import CommandCursor, RawBatchCommandCursor
from pymongo.cursor import Cursor, RawBatchCursor, _Hint, _Sort
from pymongo.cursor import Cursor, RawBatchCursor
from pymongo.cursor_shared import _Hint, _Sort
from pymongo.database import Database
from pymongo.encryption import ClientEncryption, RewrapManyDataKeyResult
from pymongo.encryption_options import RangeOpts
@ -52,11 +48,14 @@ from pymongo.read_concern import ReadConcern
from pymongo.read_preferences import _ServerMode
from pymongo.results import (
BulkWriteResult,
ClientBulkWriteResult,
DeleteResult,
InsertManyResult,
InsertOneResult,
UpdateResult,
)
from pymongo.synchronous.client_session import _T
from pymongo.synchronous.collection import _WriteOp
from pymongo.topology_description import TopologyDescription
from pymongo.typings import (
_Address,
@ -121,6 +120,17 @@ class AgnosticClient(AgnosticBaseProperties[_DocumentType]):
write_concern: Optional[WriteConcern] = None,
read_concern: Optional[ReadConcern] = None,
) -> AgnosticDatabase[_DocumentType]: ...
async def bulk_write(
self,
models: Sequence[_WriteOp[_DocumentType]],
session: Optional[ClientSession] = None,
ordered: bool = True,
verbose_results: bool = False,
bypass_document_validation: Optional[bool] = None,
comment: Optional[Any] = None,
let: Optional[Mapping] = None,
write_concern: Optional[WriteConcern] = None,
) -> ClientBulkWriteResult: ...
HOST: str
@ -694,7 +704,7 @@ class AgnosticBaseCursor(AgnosticBase, Generic[_DocumentType]):
def next_object(self) -> Any: ...
def each(self, callback: Callable) -> None: ...
def _each_got_more(self, callback: Callable, future: Any) -> None: ...
def to_list(self, length: Union[int, None]) -> Future[list]: ...
def to_list(self, length: Optional[int] = ...) -> Future[list[_DocumentType]]: ...
def _to_list(
self, length: Union[int, None], the_list: list, future: Any, get_more_result: Any
) -> None: ...
@ -756,8 +766,7 @@ class AgnosticRawBatchCommandCursor(AgnosticCommandCursor[_DocumentType]):
class _LatentCursor(Generic[_DocumentType]):
def __init__(self, collection: AgnosticCollection[_DocumentType]): ...
def _CommandCursor__end_session(self, *args: Any, **kwargs: Any) -> None: ...
def _CommandCursor__die(self, *args: Any, **kwargs: Any) -> None: ...
def _end_session(self, *args: Any, **kwargs: Any) -> None: ...
def clone(self) -> _LatentCursor[_DocumentType]: ...
def rewind(self) -> _LatentCursor[_DocumentType]: ...

View File

@ -31,7 +31,7 @@ read preference, and/or write concern from this :class:`MotorClient`.
:Parameters:
- `name`: The name of the database - a string.
- `codec_options` (optional): An instance of
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
:class:`~pymongo.codec_options.CodecOptions`. If ``None`` (the
default) the :attr:`codec_options` of this :class:`MotorClient` is
used.
- `read_preference` (optional): The read preference to use. If
@ -66,7 +66,7 @@ based only on the URI in a configuration file.
- `default` (optional): the database name to use if no database name
was provided in the URI.
- `codec_options` (optional): An instance of
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
:class:`~pymongo.codec_options.CodecOptions`. If ``None`` (the
default) the :attr:`codec_options` of this :class:`MotorClient` is
used.
- `read_preference` (optional): The read preference to use. If
@ -187,7 +187,7 @@ This will print something like::
:Returns:
An instance of :class:`~pymongo.results.BulkWriteResult`.
.. seealso:: :ref:`writes-and-ids`
.. seealso:: `Writes and ids <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/crud/insert/#overview>`_
.. note:: `bypass_document_validation` requires server version
**>= 3.2**
@ -344,7 +344,7 @@ kwargs. So ``{count: collection_name, query: query}`` becomes::
.. note:: the order of keys in the `command` document is
significant (the "verb" must come first), so commands
which require multiple keys (e.g. `findandmodify`)
should use an instance of :class:`~bson.son.SON` or
should use an instance of :class:`~pymongo.son.SON` or
a string and kwargs instead of a Python :class:`dict`.
- `value` (optional): value to use for the command verb when
@ -829,7 +829,7 @@ This prints something like::
:Returns:
An instance of :class:`~pymongo.results.InsertManyResult`.
.. seealso:: :ref:`writes-and-ids`
.. seealso:: `Writes and ids <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/crud/insert/#overview>`_
.. note:: `bypass_document_validation` requires server version
**>= 3.2**
@ -866,7 +866,7 @@ This code outputs the new document's ``_id``::
:Returns:
- An instance of :class:`~pymongo.results.InsertOneResult`.
.. seealso:: :ref:`writes-and-ids`
.. seealso:: `Writes and ids <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/crud/insert/#overview>`_
.. note:: `bypass_document_validation` requires server version
**>= 3.2**
@ -890,7 +890,7 @@ response from the server to the `map reduce command`_.
- `reduce`: reduce function (as a JavaScript string)
- `out`: output collection name or `out object` (dict). See
the `map reduce command`_ documentation for available options.
Note: `out` options are order sensitive. :class:`~bson.son.SON`
Note: `out` options are order sensitive. :class:`~pymongo.son.SON`
can be used to specify multiple options.
e.g. SON([('replace', <collection name>), ('db', <database name>)])
- `full_response` (optional): if ``True``, return full response to
@ -1149,7 +1149,7 @@ Pass a field name and a direction, either
>>> async def f():
... cursor = collection.find().sort("_id", pymongo.DESCENDING)
... docs = await cursor.to_list(None)
... docs = await cursor.to_list()
... print([d["_id"] for d in docs])
...
>>> IOLoop.current().run_sync(f)
@ -1163,7 +1163,7 @@ To sort by multiple fields, pass a list of (key, direction) pairs:
... cursor = collection.find().sort(
... [("field1", pymongo.ASCENDING), ("field2", pymongo.DESCENDING)]
... )
... docs = await cursor.to_list(None)
... docs = await cursor.to_list()
... print([(d["field1"], d["field2"]) for d in docs])
...
>>> IOLoop.current().run_sync(f)
@ -1277,7 +1277,7 @@ started it.
where_doc = """Adds a `$where`_ clause to this query.
The `code` argument must be an instance of :class:`str`
:class:`~bson.code.Code` containing a JavaScript expression.
:class:`~pymongo.code.Code` containing a JavaScript expression.
This expression will be evaluated for each document scanned.
Only those documents for which the expression evaluates to *true*
will be returned as results. The keyword *this* refers to the object
@ -1293,7 +1293,7 @@ if this :class:`~motor.motor_tornado.MotorCursor` has already been used.
Only the last call to :meth:`where` applied to a
:class:`~motor.motor_tornado.MotorCursor` has any effect.
.. note:: MongoDB 4.4 drops support for :class:`~bson.code.Code`
.. note:: MongoDB 4.4 drops support for :class:`~pymongo.code.Code`
with scope variables. Consider using `$expr`_ instead.
:Parameters:
@ -1337,7 +1337,7 @@ gridfs_delete_doc = """Delete a file's metadata and data chunks from a GridFS bu
b"data I want to store!")
await fs.delete(file_id)
Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists.
Raises :exc:`~pymongo.errors.NoFile` if no file with file_id exists.
:Parameters:
- `file_id`: The _id of the file to be deleted.
@ -1361,7 +1361,7 @@ gridfs_download_to_stream_doc = """Downloads the contents of the stored file spe
file.seek(0)
contents = file.read()
Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists.
Raises :exc:`~pymongo.errors.NoFile` if no file with file_id exists.
:Parameters:
- `file_id`: The _id of the file to be downloaded.
@ -1383,7 +1383,7 @@ gridfs_download_to_stream_by_name_doc = """ Write the contents of `filename
file = open('myfile','wb')
await fs.download_to_stream_by_name("test_file", file)
Raises :exc:`~gridfs.errors.NoFile` if no such version of
Raises :exc:`~pymongo.errors.NoFile` if no such version of
that file exists.
Raises :exc:`~ValueError` if `filename` is not a string.
@ -1419,7 +1419,7 @@ gridfs_open_download_stream_doc = """Opens a stream to read the contents of the
grid_out = await fs.open_download_stream(file_id)
contents = await grid_out.read()
Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists.
Raises :exc:`~pymongo.errors.NoFile` if no file with file_id exists.
:Parameters:
- `file_id`: The _id of the file to be downloaded.
@ -1441,7 +1441,7 @@ gridfs_open_download_stream_by_name_doc = """Opens a stream to read the contents
grid_out = await fs.open_download_stream_by_name(file_id)
contents = await grid_out.read()
Raises :exc:`~gridfs.errors.NoFile` if no such version of
Raises :exc:`~pymongo.errors.NoFile` if no such version of
that file exists.
Raises :exc:`~ValueError` filename is not a string.
@ -1484,7 +1484,7 @@ gridfs_open_upload_stream_doc = """Opens a stream for writing.
Returns an instance of :class:`AsyncIOMotorGridIn`.
Raises :exc:`~gridfs.errors.NoFile` if no such version of
Raises :exc:`~pymongo.errors.NoFile` if no such version of
that file exists.
Raises :exc:`~ValueError` if `filename` is not a string.
@ -1528,7 +1528,7 @@ gridfs_open_upload_stream_with_id_doc = """Opens a stream for writing.
Returns an instance of :class:`AsyncIOMotorGridIn`.
Raises :exc:`~gridfs.errors.NoFile` if no such version of
Raises :exc:`~pymongo.errors.NoFile` if no such version of
that file exists.
Raises :exc:`~ValueError` if `filename` is not a string.
@ -1560,7 +1560,7 @@ gridfs_rename_doc = """Renames the stored file with the specified file_id.
await fs.rename(file_id, "new_test_name")
Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists.
Raises :exc:`~pymongo.errors.NoFile` if no file with file_id exists.
:Parameters:
- `file_id`: The _id of the file to be renamed.
@ -1581,7 +1581,7 @@ gridfs_upload_from_stream_doc = """Uploads a user file to a GridFS bucket.
b"data I want to store!",
metadata={"contentType": "text/plain"})
Raises :exc:`~gridfs.errors.NoFile` if no such version of
Raises :exc:`~pymongo.errors.NoFile` if no such version of
that file exists.
Raises :exc:`~ValueError` if `filename` is not a string.
@ -1616,7 +1616,7 @@ gridfs_upload_from_stream_with_id_doc = """Uploads a user file to a GridFS bucke
b"data I want to store!",
metadata={"contentType": "text/plain"})
Raises :exc:`~gridfs.errors.NoFile` if no such version of
Raises :exc:`~pymongo.errors.NoFile` if no such version of
that file exists.
Raises :exc:`~ValueError` if `filename` is not a string.

View File

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

View File

@ -1,9 +1,10 @@
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
from pymongo.client_session import TransactionOptions
from pymongo.cursor import _Hint, _Sort
from pymongo.cursor_shared import _Hint, _Sort
from pymongo.read_concern import ReadConcern
from pymongo.read_preferences import ReadPreference, _ServerMode
from pymongo.typings import _CollationIn, _DocumentType, _DocumentTypeArg, _Pipeline

View File

@ -37,9 +37,6 @@ class AgnosticGridOutCursor(AgnosticCursor):
__motor_class_name__ = "MotorGridOutCursor"
__delegate_class__ = gridfs.GridOutCursor
# PyMongo's GridOutCursor inherits __die from Cursor.
_Cursor__die = AsyncCommand()
def next_object(self):
"""**DEPRECATED** - Get next GridOut object from cursor."""
# Note: the super() call will raise a warning for the deprecation.
@ -76,7 +73,7 @@ class AgnosticGridOut:
"""Class to read data out of GridFS.
MotorGridOut supports the same attributes as PyMongo's
:class:`~gridfs.grid_file.GridOut`, such as ``_id``, ``content_type``,
:class:`~pymongo.grid_file.GridOut`, such as ``_id``, ``content_type``,
etc.
You don't need to instantiate this class directly - use the
@ -88,7 +85,6 @@ class AgnosticGridOut:
__motor_class_name__ = "MotorGridOut"
__delegate_class__ = gridfs.GridOut
_ensure_file = AsyncCommand()
_id = MotorGridOutProperty()
aliases = MotorGridOutProperty()
chunk_size = MotorGridOutProperty()
@ -98,6 +94,7 @@ class AgnosticGridOut:
length = MotorGridOutProperty()
metadata = MotorGridOutProperty()
name = MotorGridOutProperty()
_open = AsyncCommand(attr_name="open")
read = AsyncRead()
readable = DelegateMethod()
readchunk = AsyncRead()
@ -160,7 +157,7 @@ class AgnosticGridOut:
:class:`~motor.MotorGridOut` now opens itself on demand, calling
``open`` explicitly is rarely needed.
"""
return self._framework.chain_return_value(self._ensure_file(), self.get_io_loop(), self)
return self._framework.chain_return_value(self._open(), self.get_io_loop(), self)
def get_io_loop(self):
return self.io_loop
@ -258,7 +255,7 @@ Metadata set on the file appears as attributes on a
arguments include:
- ``"_id"``: unique ID for this file (default:
:class:`~bson.objectid.ObjectId`) - this ``"_id"`` must
:class:`~pymongo.objectid.ObjectId`) - this ``"_id"`` must
not have already been used for another file
- ``"filename"``: human name for the file

View File

@ -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
@ -35,7 +36,6 @@ _SEEK_END = os.SEEK_END
class AgnosticGridOutCursor(AgnosticCursor):
__motor_class_name__: str
__delegate_class__: type[GridOutCursor]
async def _Cursor__die(self, synchronous: bool = False) -> None: ...
def next_object(self) -> AgnosticGridOutCursor: ...
class AgnosticGridOut:
@ -50,7 +50,7 @@ class AgnosticGridOut:
length: int
upload_date: datetime.datetime
metadata: Optional[Mapping[str, Any]]
async def _ensure_file(self) -> None: ...
async def _open(self) -> None: ...
def close(self) -> None: ...
async def read(self, size: int = -1) -> NoReturn: ...
def readable(self) -> bool: ...
@ -173,7 +173,7 @@ class AgnosticGridFSBucket:
chunk_size_bytes: int = ...,
write_concern: Optional[WriteConcern] = None,
read_preference: Optional[_ServerMode] = None,
collection: Optional[AgnosticCollection] = None,
collection: Optional[str] = None,
) -> None: ...
def get_io_loop(self) -> Any: ...
def wrap(self, obj: Any) -> Any: ...

View File

@ -1,9 +1,10 @@
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
from pymongo.client_session import TransactionOptions
from pymongo.cursor import _Hint, _Sort
from pymongo.cursor_shared import _Hint, _Sort
from pymongo.read_concern import ReadConcern
from pymongo.read_preferences import ReadPreference, _ServerMode
from pymongo.typings import _CollationIn, _DocumentType, _DocumentTypeArg, _Pipeline

View File

@ -140,6 +140,7 @@ class GridFSHandler(tornado.web.RequestHandler):
ims_value = self.request.headers.get("If-Modified-Since")
if ims_value is not None:
date_tuple = email.utils.parsedate(ims_value)
assert date_tuple is not None
# If our MotorClient is tz-aware, assume the naive ims_value is in
# its time zone.

View File

@ -1,6 +1,6 @@
[build-system]
requires = ["setuptools>61.0"]
build-backend = "setuptools.build_meta"
requires = ["hatchling>1.24","hatch-requirements-txt>=0.4.1"]
build-backend = "hatchling.build"
[project]
name = "motor"
@ -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.8"
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.8",
"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]
@ -48,14 +48,25 @@ Documentation = "https://motor.readthedocs.io"
Source = "https://github.com/mongodb/motor"
Tracker = "https://jira.mongodb.org/projects/MOTOR/issues"
[tool.setuptools.dynamic]
version = {attr = "motor._version.version"}
[tool.hatch.version]
path = "motor/_version.py"
validate-bump = false
[tool.setuptools.packages.find]
include = ["motor"]
[tool.hatch.metadata.hooks.requirements_txt]
files = ["requirements.txt"]
[tool.hatch.metadata.hooks.requirements_txt.optional-dependencies]
aws = ["requirements/aws.txt"]
docs = ["requirements/docs.txt"]
encryption = ["requirements/encryption.txt"]
gssapi = ["requirements/gssapi.txt"]
ocsp = ["requirements/ocsp.txt"]
snappy = ["requirements/snappy.txt"]
test = ["requirements/test.txt"]
zstd = ["requirements/zstd.txt"]
[tool.mypy]
python_version = "3.8"
python_version = "3.10"
strict = true
pretty = true
show_error_context = true
@ -88,11 +99,15 @@ filterwarnings = [
"ignore:GridOut property 'aliases' is deprecated and will be removed in PyMongo 5.0:DeprecationWarning",
# From older versions aiohttp.
"ignore:\"@coroutine\" decorator is deprecated since Python 3.8:DeprecationWarning",
"ignore:The loop argument is deprecated since Python 3.8:DeprecationWarning"
"ignore:The loop argument is deprecated since Python 3.8:DeprecationWarning",
# TODO: Remove both of these in https://jira.mongodb.org/browse/PYTHON-4731
"ignore:Unclosed AsyncMongoClient*",
"ignore:Unclosed MongoClient*",
# Deprecation warning now that Motor is officially deprecated
"ignore:Motor is deprecated*",
]
[tool.ruff]
target-version = "py38"
line-length = 100
[tool.ruff.lint]

View File

@ -1 +1 @@
pymongo>=4.5,<5
pymongo>=4.9,<5.0

View File

@ -1,5 +0,0 @@
tornado
aiohttp
sphinx>=5.3,<8
sphinx_rtd_theme>=2,<3
readthedocs-sphinx-search~=0.3

6
requirements/docs.txt Normal file
View File

@ -0,0 +1,6 @@
tornado
aiohttp
sphinx>=5.3,<9
sphinx_rtd_theme>=2,<4
readthedocs-sphinx-search~=0.3
furo==2025.12.19

View File

@ -1,5 +0,0 @@
pytest>=7
mockupdb
tornado>=5
aiohttp!=3.8.6
motor[encryption]

7
requirements/test.txt Normal file
View File

@ -0,0 +1,7 @@
pytest>=9.0.3
mockupdb
tornado>=5
aiohttp>=3.8.7
motor[encryption]
cffi>=1.17.0rc1;python_version>="3.13"
pytest_asyncio

View File

@ -2,10 +2,11 @@
{
"metadata": {
"timestamp": "2024-05-02T17:36:34.918032+00:00"
}
},
"components": [],
"serialNumber": "urn:uuid:1678d437-cbf0-425b-809f-cab93fa0f931",
"version": 1,
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5"
}
}

View File

@ -1,21 +0,0 @@
from setuptools import setup
def parse_reqs_file(fname):
with open(fname) as fid: # noqa:PTH123
lines = [li.strip() for li in fid.readlines()]
return [li for li in lines if li and not li.startswith("#")]
extras_require = dict( # noqa:C408
aws=parse_reqs_file("requirements/aws-requirements.txt"),
encryption=parse_reqs_file("requirements/encryption-requirements.txt"),
gssapi=parse_reqs_file("requirements/gssapi-requirements.txt"),
ocsp=parse_reqs_file("requirements/ocsp-requirements.txt"),
snappy=parse_reqs_file("requirements/snappy-requirements.txt"),
srv=parse_reqs_file("requirements/srv-requirements.txt"),
test=parse_reqs_file("requirements/test-requirements.txt"),
zstd=parse_reqs_file("requirements/zstd-requirements.txt"),
)
setup(install_requires=parse_reqs_file("requirements.txt"), extras_require=extras_require)

View File

@ -28,9 +28,8 @@ from typing import Generic, TypeVar
# Make e.g. "from pymongo.errors import AutoReconnect" work. Note that
# importing * won't pick up underscore-prefixed attrs.
from gridfs import *
from gridfs import _disallow_transactions
from gridfs.errors import *
from gridfs.grid_file import (
from gridfs.synchronous.grid_file import (
_SEEK_CUR,
_SEEK_END,
_UPLOAD_BUFFER_CHUNKS,
@ -57,33 +56,32 @@ from pymongo import (
saslprep,
server_selectors,
server_type,
srv_resolver,
ssl_support,
uri_parser,
write_concern,
)
from pymongo.synchronous import srv_resolver
# Added for API compat with pymongo.
try:
from pymongo import _csot
except ImportError:
pass
from pymongo import client_session
from pymongo import client_session, synchronous
from pymongo.auth import *
from pymongo.auth import _build_credentials_tuple, _password_digest
from pymongo.client_session import TransactionOptions, _TxnState
from pymongo.client_session import TransactionOptions
from pymongo.collation import *
from pymongo.common import *
from pymongo.common import _MAX_END_SESSIONS, _UUID_REPRESENTATIONS
from pymongo.compression_support import _have_snappy, _have_zlib, _have_zstd
from pymongo.cursor import *
from pymongo.cursor import _QUERY_OPTIONS
from pymongo.cursor_shared import _QUERY_OPTIONS
from pymongo.encryption import *
from pymongo.encryption import _MONGOCRYPTD_TIMEOUT_MS, _Encrypter
from pymongo.encryption_options import *
from pymongo.encryption_options import _HAVE_PYMONGOCRYPT
from pymongo.errors import *
from pymongo.event_loggers import *
from pymongo.helpers import _check_command_response
from pymongo.helpers_shared import _SENSITIVE_COMMANDS, _check_command_response
from pymongo.lock import _create_lock
from pymongo.message import (
_COMMAND_OVERHEAD,
@ -91,30 +89,32 @@ from pymongo.message import (
_gen_find_command,
_maybe_add_read_preference,
)
from pymongo.monitor import *
from pymongo.monitoring import *
from pymongo.monitoring import _LISTENERS, _SENSITIVE_COMMANDS, _Listeners
from pymongo.monitoring import _LISTENERS, _Listeners
from pymongo.ocsp_cache import _OCSPCache
from pymongo.operations import *
from pymongo.periodic_executor import *
from pymongo.periodic_executor import _EXECUTORS
from pymongo.pool import *
from pymongo.pool import _METADATA, Connection, Pool, _PoolClosedError
from pymongo.pool import Connection, Pool
from pymongo.read_concern import *
from pymongo.read_preferences import *
from pymongo.read_preferences import _ServerMode
from pymongo.results import *
from pymongo.results import _WriteResult
from pymongo.saslprep import *
from pymongo.server import *
from pymongo.server_selectors import *
from pymongo.settings import *
from pymongo.srv_resolver import _resolve, _SrvResolver
from pymongo.ssl_support import *
from pymongo.topology import *
from pymongo.synchronous.client_session import _TxnState
from pymongo.synchronous.encryption import _Encrypter
from pymongo.synchronous.monitor import *
from pymongo.synchronous.pool import _PoolClosedError
from pymongo.synchronous.server import *
from pymongo.synchronous.settings import *
from pymongo.synchronous.srv_resolver import _have_dnspython, _resolve, _SrvResolver
from pymongo.synchronous.topology import *
from pymongo.topology_description import *
from pymongo.uri_parser import *
from pymongo.uri_parser import _have_dnspython
from pymongo.write_concern import *
from tornado.ioloop import IOLoop
@ -131,6 +131,7 @@ from motor.motor_tornado import create_motor_class
_MotorRawBatchCursor = create_motor_class(_AgnosticRawBatchCursor)
_MotorRawBatchCommandCursor = create_motor_class(_AgnosticRawBatchCommandCursor)
get_hosts_and_min_ttl = _SrvResolver.get_hosts_and_min_ttl
def wrap_synchro(fn):
@ -262,9 +263,6 @@ class SynchroMeta(type):
- Motor methods which return Motor class instances are wrapped to return
Synchro class instances.
- Certain internals accessed by PyMongo's unittests, such as _Cursor__data,
are delegated from Synchro directly to PyMongo.
"""
def __new__(cls, name, bases, attrs):
@ -301,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
@ -395,9 +393,10 @@ class MongoClient(Synchro, Generic[_T]):
return Database(self, name, delegate=self.delegate[name])
# For PyMongo tests that access client internals.
_MongoClient__all_credentials = SynchroProperty()
_MongoClient__kill_cursors_queue = SynchroProperty()
_MongoClient__options = SynchroProperty()
_connect = Sync()
_all_credentials = SynchroProperty()
_kill_cursors_queue = SynchroProperty()
_options = SynchroProperty()
_cache_credentials = SynchroProperty()
_close_cursor_now = SynchroProperty()
_get_topology = SynchroProperty()
@ -405,6 +404,7 @@ class MongoClient(Synchro, Generic[_T]):
_kill_cursors_executor = SynchroProperty()
_topology_settings = SynchroProperty()
_process_periodic_tasks = SynchroProperty()
_event_listeners = SynchroProperty()
class _SynchroTransactionContext(Synchro):
@ -576,6 +576,16 @@ class Cursor(Synchro):
rewind = WrapOutgoing()
clone = WrapOutgoing()
close = Sync("close")
to_list = Sync("to_list")
_query_flags = SynchroProperty()
_data = SynchroProperty()
_max_time_ms = SynchroProperty()
_max_await_time_ms = SynchroProperty()
_retrieved = SynchroProperty()
_spec = SynchroProperty()
_exhaust = SynchroProperty()
_query_spec = SynchroProperty()
_next = Sync("next")
@ -617,22 +627,12 @@ class Cursor(Synchro):
# Don't suppress exceptions.
return False
# For PyMongo tests that access cursor internals.
_Cursor__data = SynchroProperty()
_Cursor__exhaust = SynchroProperty()
_Cursor__max_await_time_ms = SynchroProperty()
_Cursor__max_time_ms = SynchroProperty()
_Cursor__query_flags = SynchroProperty()
_Cursor__query_spec = SynchroProperty()
_Cursor__retrieved = SynchroProperty()
_Cursor__spec = SynchroProperty()
_read_preference = SynchroProperty()
class CommandCursor(Cursor):
__delegate_class__ = motor.motor_tornado.MotorCommandCursor
try_next = Sync("try_next")
to_list = Sync("to_list")
class GridOutCursor(Cursor):

View File

@ -197,6 +197,28 @@ excluded_tests = [
"TestClient.test_handshake*",
# This test is not a valid unittest target.
"TestRangeQueryProse.run_test_cases",
# The test logic interferes with the SynchroLoader.
"ClientUnitTest.test_detected_environment_logging",
"ClientUnitTest.test_detected_environment_warning",
# The batch_size portion of this test does not work with synchro.
"TestCursor.test_to_list_length",
# These tests hang due to internal incompatibilities in to_list.
"TestCursor.test_command_cursor_to_list*",
# Motor does not allow calling to_list on a tailable cursor.
"TestCursor.test_max_await_time_ms",
"TestCursor.test_to_list_tailable",
# This test relies on a private api"
"TestTransactions.test_transaction_pool_cleared_error_labelled_transient",
# Newer tests not implemented in Motor
"TestClient.test_repr_srv_host",
"TestGridfs.test_delete_by_name",
"TestGridfs.test_rename_by_name",
"TestGridfsDeleteByName.*",
"TestGridfsRenameByName.*",
"TestMongosAndReadPreference.test_read_preference_hedge_deprecated",
"TestUnifiedTestFormatValidPassKmsProvidersExplicitKmsCredentials.*",
"TestUnifiedTestFormatValidPassKmsProvidersMixedKmsCredentialFields.*",
"TestUnifiedTestFormatValidPassKmsProvidersPlaceholderKmsCredentials.*",
]
@ -230,11 +252,7 @@ class SynchroModuleFinder(importlib.abc.MetaPathFinder):
class SynchroModuleLoader(importlib.abc.Loader):
def patch_spec(self, fullname):
parts = fullname.split(".")
if parts[-1] in ("gridfs", "pymongo"):
# E.g. "import pymongo"
return True
elif len(parts) >= 2 and parts[-2] in ("gridfs", "pymongo"):
# E.g. "import pymongo.mongo_client"
if ("pymongo" in parts or "gridfs" in parts) and "asynchronous" not in parts:
return True
return False
@ -327,9 +345,18 @@ if __name__ == "__main__":
"pymongo.encryption_options",
"pymongo.mongo_client",
"pymongo.database",
"pymongo.srv_resolver",
"pymongo.synchronous.srv_resolver",
"pymongo.synchronous.collection",
"pymongo.synchronous.client_session",
"pymongo.synchronous.command_cursor",
"pymongo.synchronous.change_stream",
"pymongo.synchronous.cursor",
"pymongo.synchronous.encryption",
"pymongo.synchronous.mongo_client",
"pymongo.synchronous.database",
"gridfs",
"gridfs.grid_file",
"gridfs.synchronous.grid_file",
]:
sys.modules.pop(n)
@ -341,7 +368,13 @@ if __name__ == "__main__":
# Run the tests from the pymongo target dir with our custom plugin.
os.chdir(sys.argv[1])
code = pytest.main(sys.argv[2:], plugins=[SynchroPytestPlugin()])
if "-m" in sys.argv[2:]:
markers = []
else:
markers = ["-m", "default"]
code = pytest.main(
sys.argv[2:] + markers + ["-p", "no:warnings"], plugins=[SynchroPytestPlugin()]
)
if code != 0:
sys.exit(code)

View File

@ -18,10 +18,22 @@ import asyncio
import datetime
import email
import logging
import sys
import test
import time
from test.asyncio_tests import AsyncIOTestCase, asyncio_test
import pytest
# MOTOR-1477 - after libzstd is supported on build hosts
# we can remove this guard.
if sys.version_info >= (3, 14):
try:
import compression.zstd # noqa: F401
except ModuleNotFoundError:
pytest.skip(allow_module_level=True)
import aiohttp
import aiohttp.web
import gridfs

View File

@ -98,7 +98,7 @@ class AIOMotorTestBasic(AsyncIOTestCase):
motor_cursor = collection.find()
cursor = motor_cursor.delegate
self.assertEqual(Nearest(tag_sets=[{"yay": "jesse"}]), cursor._read_preference())
self.assertEqual(Nearest(tag_sets=[{"yay": "jesse"}]), cursor._get_read_preference())
cx.close()

View File

@ -33,7 +33,7 @@ from test.asyncio_tests import (
remove_all_users,
)
from test.test_environment import db_password, db_user, env
from test.utils import get_primary_pool
from test.utils import AUTO_ISMASTER, get_primary_pool
import pymongo
from bson import CodecOptions
@ -257,7 +257,7 @@ class TestAsyncIOClient(AsyncIOTestCase):
class TestAsyncIOClientTimeout(AsyncIOMockServerTestCase):
@asyncio_test
async def test_timeout(self):
server = self.server(auto_ismaster=True)
server = self.server(auto_ismaster=AUTO_ISMASTER)
client = motor_asyncio.AsyncIOMotorClient(
server.uri, socketTimeoutMS=100, io_loop=self.loop
)
@ -281,7 +281,7 @@ class TestAsyncIOClientHandshake(AsyncIOMockServerTestCase):
future = client.db.command("ping")
ismaster = await self.run_thread(server.receives, "ismaster")
meta = ismaster.doc["client"]
self.assertEqual("PyMongo|Motor", meta["driver"]["name"])
self.assertIn("|Motor", meta["driver"]["name"])
# AsyncIOMotorClient adds nothing to platform.
self.assertNotIn("Tornado", meta["platform"])
self.assertTrue(

View File

@ -270,7 +270,7 @@ class TestAsyncIOCollection(AsyncIOTestCase):
@env.require_no_standalone
@asyncio_test
async def test_async_create_encrypted_collection(self):
c = self.collection
c = self.cx
KMS_PROVIDERS = {"local": {"key": b"\x00" * 96}}
self.cx.drop_database("db")
async with AsyncIOMotorClientEncryption(
@ -287,9 +287,10 @@ class TestAsyncIOCollection(AsyncIOTestCase):
self.assertEqual(exc.exception.code, 121)
await self.db.drop_collection("testing1", encrypted_fields=ef)
@env.require_version_min(8, 0, -1, -1)
@asyncio_test
async def test_async_encrypt_expression(self):
c = self.collection
c = self.cx
KMS_PROVIDERS = {"local": {"key": b"\x00" * 96}}
self.cx.drop_database("db")
async with AsyncIOMotorClientEncryption(
@ -299,12 +300,12 @@ class TestAsyncIOCollection(AsyncIOTestCase):
"local", key_alt_names=["pymongo_encryption_example_1"]
)
name = "DoubleNoPrecision"
range_opts = RangeOpts(sparsity=1)
range_opts = RangeOpts(sparsity=1, trim_factor=1)
for i in [6.0, 30.0, 200.0]:
insert_payload = await client_encryption.encrypt(
float(i),
key_id=data_key,
algorithm=Algorithm.RANGEPREVIEW,
algorithm=Algorithm.RANGE,
contention_factor=0,
range_opts=range_opts,
)
@ -323,8 +324,8 @@ class TestAsyncIOCollection(AsyncIOTestCase):
]
},
key_id=data_key,
algorithm=Algorithm.RANGEPREVIEW,
query_type=QueryType.RANGEPREVIEW,
algorithm=Algorithm.RANGE,
query_type=QueryType.RANGE,
contention_factor=0,
range_opts=range_opts,
)
@ -333,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)

View File

@ -29,6 +29,7 @@ from test.asyncio_tests import (
)
from test.test_environment import env
from test.utils import (
AUTO_ISMASTER,
FailPoint,
TestListener,
get_async_test_timeout,
@ -86,7 +87,7 @@ class TestAsyncIOCursor(AsyncIOMockServerTestCase):
@unittest.skipIf("PyPy" in sys.version, "PyPy")
@asyncio_test
async def test_fetch_next_delete(self):
client, server = self.client_server(auto_ismaster=True)
client, server = self.client_server(auto_ismaster=AUTO_ISMASTER)
cursor = client.test.coll.find()
self.fetch_next(cursor)
@ -133,7 +134,7 @@ class TestAsyncIOCursor(AsyncIOMockServerTestCase):
self.assertTrue(cursor.next_object())
# Not valid on server, causes CursorNotFound.
cursor.delegate._Cursor__id = bson.int64.Int64(1234)
cursor.delegate._id = bson.int64.Int64(1234)
with self.assertRaises(OperationFailure):
await cursor.fetch_next
@ -192,7 +193,6 @@ class TestAsyncIOCursor(AsyncIOMockServerTestCase):
self.assertEqual(expected(100, 101), (await cursor.to_list(1)))
self.assertEqual(expected(101, 102), (await cursor.to_list(1)))
self.assertEqual(expected(102, 103), (await cursor.to_list(1)))
self.assertEqual([], (await cursor.to_list(0)))
self.assertEqual(expected(103, 105), (await cursor.to_list(2)))
# Only 95 docs left, make sure length=100 doesn't error or hang
@ -309,7 +309,7 @@ class TestAsyncIOCursor(AsyncIOMockServerTestCase):
@asyncio_test
async def test_cursor_explicit_close(self):
client, server = self.client_server(auto_ismaster=True)
client, server = self.client_server(auto_ismaster=AUTO_ISMASTER)
collection = client.test.coll
cursor = collection.find()
@ -355,7 +355,7 @@ class TestAsyncIOCursor(AsyncIOMockServerTestCase):
def canceled():
try:
self.assertFalse(cursor.delegate._Cursor__killed)
self.assertFalse(cursor.delegate._killed)
self.assertTrue(cursor.alive)
# Resume iteration
@ -412,7 +412,7 @@ class TestAsyncIOCursor(AsyncIOMockServerTestCase):
@unittest.skipIf("PyPy" in sys.version, "PyPy")
@asyncio_test
async def test_cursor_del(self):
client, server = self.client_server(auto_ismaster=True)
client, server = self.client_server(auto_ismaster=AUTO_ISMASTER)
cursor = client.test.coll.find()
future = self.fetch_next(cursor)
@ -586,7 +586,7 @@ class TestAsyncIOCursor(AsyncIOMockServerTestCase):
@asyncio_test
async def test_generate_keys(self):
c = self.collection
c = self.cx
KMS_PROVIDERS = {"local": {"key": b"\x00" * 96}}
async with motor_asyncio.AsyncIOMotorClientEncryption(

View File

@ -127,7 +127,7 @@ class MotorGridFileTest(AsyncIOTestCase):
# The call tree should include PyMongo code we ran on a thread.
formatted = "\n".join(traceback.format_tb(tb))
self.assertTrue("_ensure_file" in formatted)
self.assertTrue("open" in formatted)
@asyncio_test
async def test_alternate_collection(self):

View File

@ -893,6 +893,38 @@ class TestExamples(AsyncIOTestCase):
print(doc)
# End Aggregation Example 4
@env.require_version_min(4, 4)
def test_aggregate_projection_example(self):
db = self.db
# Start Aggregation Projection Example 1
db.inventory.find(
{},
{
"_id": 0,
"item": 1,
"status": {
"$switch": {
"branches": [
{"case": {"$eq": ["$status", "A"]}, "then": "Available"},
{"case": {"$eq": ["$status", "D"]}, "then": "Discontinued"},
],
"default": "No status found",
}
},
"area": {
"$concat": [
{"$toString": {"$multiply": ["$size.h", "$size.w"]}},
" ",
"$size.uom",
]
},
"reportNumber": {"$literal": 1},
},
)
# End Aggregation Projection Example 1
@asyncio_test
async def test_commands(self):
db = self.db

View File

@ -0,0 +1,17 @@
from typing import Any
from motor.motor_asyncio import (
AsyncIOMotorChangeStream,
AsyncIOMotorClient,
AsyncIOMotorClientEncryption,
AsyncIOMotorCollection,
AsyncIOMotorCursor,
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]]

View File

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

View File

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

View File

@ -100,7 +100,7 @@ class MotorTestBasic(MotorTest):
motor_cursor = collection.find()
cursor = motor_cursor.delegate
self.assertEqual(Nearest(tag_sets=[{"yay": "jesse"}]), cursor._read_preference())
self.assertEqual(Nearest(tag_sets=[{"yay": "jesse"}]), cursor._get_read_preference())
cx.close()

View File

@ -20,13 +20,14 @@ import unittest
from test import SkipTest
from test.test_environment import db_password, db_user, env
from test.tornado_tests import MotorMockServerTest, MotorTest, remove_all_users
from test.utils import get_primary_pool, one
from test.utils import AUTO_ISMASTER, get_primary_pool, one
import pymongo
import pymongo.mongo_client
from bson import CodecOptions
from mockupdb import OpQuery
from mockupdb import OpMsg, OpQuery
from pymongo import CursorType, ReadPreference, WriteConcern
from pymongo.common import MIN_SUPPORTED_WIRE_VERSION
from pymongo.driver_info import DriverInfo
from pymongo.errors import ConnectionFailure, OperationFailure
from tornado import gen
@ -191,7 +192,7 @@ class MotorClientTest(MotorTest):
class MotorClientTimeoutTest(MotorMockServerTest):
@gen_test
async def test_timeout(self):
server = self.server(auto_ismaster=True)
server = self.server(auto_ismaster=AUTO_ISMASTER)
client = motor.MotorClient(server.uri, socketTimeoutMS=100)
with self.assertRaises(pymongo.errors.AutoReconnect) as context:
@ -205,7 +206,13 @@ class MotorClientExhaustCursorTest(MotorMockServerTest):
def primary_server(self):
primary = self.server()
hosts = [primary.address_string]
primary.autoresponds("ismaster", ismaster=True, setName="rs", hosts=hosts, maxWireVersion=6)
primary.autoresponds(
"ismaster",
ismaster=True,
setName="rs",
hosts=hosts,
maxWireVersion=MIN_SUPPORTED_WIRE_VERSION,
)
return primary
@ -213,7 +220,7 @@ class MotorClientExhaustCursorTest(MotorMockServerTest):
if rs:
return self.primary_server()
else:
return self.server(auto_ismaster=True)
return self.server(auto_ismaster=AUTO_ISMASTER)
async def _test_exhaust_query_server_error(self, rs):
# When doing an exhaust query, the socket stays checked out on success
@ -227,7 +234,8 @@ class MotorClientExhaustCursorTest(MotorMockServerTest):
# With Tornado, simply accessing fetch_next starts the fetch.
fetch_next = cursor.fetch_next
request = await self.run_thread(server.receives, OpQuery)
expected = OpQuery if pymongo.version_tuple[0:2] < (4, 14) else OpMsg({})
request = await self.run_thread(server.receives, expected)
request.fail(code=1)
with self.assertRaises(pymongo.errors.OperationFailure):
@ -238,10 +246,12 @@ class MotorClientExhaustCursorTest(MotorMockServerTest):
@gen_test
async def test_exhaust_query_server_error_standalone(self):
raise self.skipTest("MOTOR-1472")
await self._test_exhaust_query_server_error(rs=False)
@gen_test
async def test_exhaust_query_server_error_rs(self):
raise self.skipTest("MOTOR-1472")
await self._test_exhaust_query_server_error(rs=True)
async def _test_exhaust_query_network_error(self, rs):
@ -260,7 +270,8 @@ class MotorClientExhaustCursorTest(MotorMockServerTest):
# With Tornado, simply accessing fetch_next starts the fetch.
fetch_next = cursor.fetch_next
request = await self.run_thread(server.receives, OpQuery)
expected = OpQuery if pymongo.version_tuple[0:2] < (4, 14) else OpMsg({})
request = await self.run_thread(server.receives, expected)
request.hangs_up()
with self.assertRaises(pymongo.errors.ConnectionFailure):
@ -290,7 +301,7 @@ class MotorClientHandshakeTest(MotorMockServerTest):
future = client.db.command("ping")
ismaster = await self.run_thread(server.receives, "ismaster")
meta = ismaster.doc["client"]
self.assertEqual("PyMongo|Motor", meta["driver"]["name"])
self.assertIn("|Motor", meta["driver"]["name"])
self.assertIn("Tornado", meta["platform"])
self.assertTrue(
meta["driver"]["version"].endswith(motor.version),
@ -316,7 +327,7 @@ class MotorClientHandshakeTest(MotorMockServerTest):
future = client.db.command("ping")
handshake = await self.run_thread(server.receives, "ismaster")
meta = handshake.doc["client"]
self.assertEqual(f"PyMongo|Motor|{driver_info.name}", meta["driver"]["name"])
self.assertIn(f"|Motor|{driver_info.name}", meta["driver"]["name"])
self.assertIn("Tornado", meta["platform"])
self.assertIn(f"|{driver_info.platform}", meta["platform"])
self.assertTrue(

View File

@ -272,7 +272,7 @@ class MotorCollectionTest(MotorTest):
@gen_test
async def test_async_create_encrypted_collection(self):
await self.db.drop_collection("test_collection")
c = self.collection
c = self.cx
KMS_PROVIDERS = {"local": {"key": b"\x00" * 96}}
self.cx.drop_database("db")
async with motor.MotorClientEncryption(
@ -289,9 +289,10 @@ class MotorCollectionTest(MotorTest):
self.assertEqual(exc.exception.code, 121)
await self.db.drop_collection("testing1", encrypted_fields=ef)
@env.require_version_min(8, 0, -1, -1)
@gen_test
async def test_async_encrypt_expression(self):
c = self.collection
c = self.cx
KMS_PROVIDERS = {"local": {"key": b"\x00" * 96}}
self.cx.drop_database("db")
async with motor.MotorClientEncryption(
@ -301,12 +302,12 @@ class MotorCollectionTest(MotorTest):
"local", key_alt_names=["pymongo_encryption_example_1"]
)
name = "DoubleNoPrecision"
range_opts = RangeOpts(sparsity=1)
range_opts = RangeOpts(sparsity=1, trim_factor=1)
for i in [6.0, 30.0, 200.0]:
insert_payload = await client_encryption.encrypt(
float(i),
key_id=data_key,
algorithm=Algorithm.RANGEPREVIEW,
algorithm=Algorithm.RANGE,
contention_factor=0,
range_opts=range_opts,
)
@ -325,8 +326,8 @@ class MotorCollectionTest(MotorTest):
]
},
key_id=data_key,
algorithm=Algorithm.RANGEPREVIEW,
query_type=QueryType.RANGEPREVIEW,
algorithm=Algorithm.RANGE,
query_type=QueryType.RANGE,
contention_factor=0,
range_opts=range_opts,
)
@ -335,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)

View File

@ -31,17 +31,19 @@ motor_only = set(["delegate", "get_io_loop", "io_loop", "wrap"])
pymongo_only = set(["next"])
pymongo_gridfs_only = set(["delete_by_name", "rename_by_name"])
motor_client_only = motor_only.union(["open"])
pymongo_client_only = set([]).union(pymongo_only)
pymongo_client_only = set(["eq_props", "append_metadata"]).union(pymongo_only)
pymongo_database_only = set([]).union(pymongo_only)
pymongo_collection_only = set([]).union(pymongo_only)
motor_cursor_only = set(
["fetch_next", "to_list", "each", "started", "next_object", "closed"]
).union(motor_only)
motor_cursor_only = set(["fetch_next", "each", "started", "next_object", "closed"]).union(
motor_only
)
pymongo_cursor_only = set(["retrieved"])
@ -113,12 +115,12 @@ class MotorCoreTestGridFS(MotorTest):
motor_gridfs_only = set(["collection"]).union(motor_only)
self.assertEqual(
attrs(GridFSBucket(env.sync_cx.test)),
attrs(GridFSBucket(env.sync_cx.test)) - pymongo_gridfs_only,
attrs(MotorGridFSBucket(self.cx.test)) - motor_gridfs_only,
)
def test_gridin_attrs(self):
motor_gridin_only = set(["set"]).union(motor_only)
motor_gridin_only = set([]).union(motor_only)
gridin_only = set(["md5"])
self.assertEqual(
@ -137,6 +139,7 @@ class MotorCoreTestGridFS(MotorTest):
"truncate",
"flush",
"fileno",
"open",
"closed",
"writelines",
"isatty",

Some files were not shown because too many files have changed in this diff Show More