Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbabb00550 | ||
|
|
7ab5080b62 | ||
|
|
62cf1742fc |
@ -35,7 +35,6 @@ functions:
|
||||
params:
|
||||
working_dir: "src"
|
||||
script: |
|
||||
set -o xtrace
|
||||
# Get the current unique version of this checkout
|
||||
if [ "${is_patch}" = "true" ]; then
|
||||
CURRENT_VERSION=$(git describe)-patch-${version_id}
|
||||
@ -52,28 +51,37 @@ functions:
|
||||
export PROJECT_DIRECTORY=$(cygpath -m $PROJECT_DIRECTORY)
|
||||
fi
|
||||
|
||||
# Installation of cryptography requires a rust compiler on some machines
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
rustup toolchain install nightly -c rustc
|
||||
|
||||
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
|
||||
set -o xtrace
|
||||
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"
|
||||
export PATH="$MONGODB_BINARIES:$HOME/.cargo/bin:$PATH"
|
||||
export PROJECT="${project}"
|
||||
export ASYNC_TEST_TIMEOUT=30
|
||||
export PIP_QUIET=1
|
||||
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||
EOT
|
||||
# See what we've done.
|
||||
# See what we've done
|
||||
cat expansion.yml
|
||||
|
||||
# Load the expansion file to make an evergreen variable with the current unique version
|
||||
@ -96,6 +104,58 @@ 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
|
||||
@ -114,10 +174,74 @@ functions:
|
||||
${PREPARE_SHELL}
|
||||
[ -f ${PROJECT_DIRECTORY}/${file} ] && sh ${PROJECT_DIRECTORY}/${file} || echo "${PROJECT_DIRECTORY}/${file} not available, skipping"
|
||||
|
||||
"upload mo artifacts":
|
||||
- command: ec2.assume_role
|
||||
"upload docs" :
|
||||
- command: shell.exec
|
||||
params:
|
||||
role_arn: ${assume_role_arn}
|
||||
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: shell.exec
|
||||
params:
|
||||
script: |
|
||||
@ -125,27 +249,62 @@ functions:
|
||||
find $MONGO_ORCHESTRATION_HOME -name \*.log | xargs tar czf mongodb-logs.tar.gz
|
||||
- command: s3.put
|
||||
params:
|
||||
aws_key: ${AWS_ACCESS_KEY_ID}
|
||||
aws_secret: ${AWS_SECRET_ACCESS_KEY}
|
||||
aws_session_token: ${AWS_SESSION_TOKEN}
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: mongodb-logs.tar.gz
|
||||
remote_file: ${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-mongodb-logs.tar.gz
|
||||
bucket: ${aws_bucket}
|
||||
remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-mongodb-logs.tar.gz
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/x-gzip}
|
||||
display_name: "mongodb-logs.tar.gz"
|
||||
- command: s3.put
|
||||
params:
|
||||
aws_key: ${AWS_ACCESS_KEY_ID}
|
||||
aws_secret: ${AWS_SECRET_ACCESS_KEY}
|
||||
aws_session_token: ${AWS_SESSION_TOKEN}
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: ${DRIVERS_TOOLS}/.evergreen/orchestration/server.log
|
||||
remote_file: ${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-orchestration.log
|
||||
bucket: ${aws_bucket}
|
||||
remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-orchestration.log
|
||||
bucket: mciuploads
|
||||
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:
|
||||
@ -161,12 +320,7 @@ functions:
|
||||
params:
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
MONGODB_VERSION=${VERSION} \
|
||||
TOPOLOGY=${TOPOLOGY} \
|
||||
AUTH=${AUTH} \
|
||||
SSL=${SSL} \
|
||||
STORAGE_ENGINE=${STORAGE_ENGINE} \
|
||||
bash ${DRIVERS_TOOLS}/.evergreen/run-orchestration.sh
|
||||
MONGODB_VERSION=${VERSION} TOPOLOGY=${TOPOLOGY} AUTH=${AUTH} SSL=${SSL} STORAGE_ENGINE=${STORAGE_ENGINE} sh ${DRIVERS_TOOLS}/.evergreen/run-orchestration.sh
|
||||
# run-orchestration generates expansion file with the MONGODB_URI for the cluster
|
||||
- command: expansions.update
|
||||
params:
|
||||
@ -177,72 +331,110 @@ functions:
|
||||
params:
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
bash ${DRIVERS_TOOLS}/.evergreen/stop-orchestration.sh
|
||||
sh ${DRIVERS_TOOLS}/.evergreen/stop-orchestration.sh
|
||||
|
||||
"run tox":
|
||||
- command: ec2.assume_role
|
||||
# If testing FLE, start the KMS mock servers, first create the virtualenv.
|
||||
- command: shell.exec
|
||||
params:
|
||||
role_arn: ${aws_test_secrets_role}
|
||||
- command: subprocess.exec
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
cd ${DRIVERS_TOOLS}/.evergreen/csfle
|
||||
. ./activate_venv.sh
|
||||
# Run in the background so the mock servers don't block the EVG task.
|
||||
- command: shell.exec
|
||||
params:
|
||||
working_dir: "src"
|
||||
binary: bash
|
||||
include_expansions_in_env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
|
||||
args:
|
||||
- ${DRIVERS_TOOLS}/.evergreen/csfle/setup-secrets.sh
|
||||
- command: subprocess.exec
|
||||
params:
|
||||
working_dir: "src"
|
||||
binary: bash
|
||||
background: true
|
||||
args:
|
||||
- ${DRIVERS_TOOLS}/.evergreen/csfle/start-servers.sh
|
||||
- command: subprocess.exec
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
cd ${DRIVERS_TOOLS}/.evergreen/csfle
|
||||
. ./activate_venv.sh
|
||||
# The -u options forces the stdout and stderr streams to be unbuffered.
|
||||
# TMPDIR is required to avoid "AF_UNIX path too long" errors.
|
||||
TMPDIR="$(dirname $DRIVERS_TOOLS)" python -u kms_kmip_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 5698 &
|
||||
python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8000 &
|
||||
python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 8001 &
|
||||
python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 8002 --require_client_cert &
|
||||
# Wait up to 10 seconds for the KMIP server to start.
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
cd ${DRIVERS_TOOLS}/.evergreen/csfle
|
||||
. ./activate_venv.sh
|
||||
for i in $(seq 1 1 10); do
|
||||
sleep 1
|
||||
if python -u kms_kmip_client.py; then
|
||||
echo 'KMS KMIP server started!'
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
echo 'Failed to start KMIP server!'
|
||||
exit 1
|
||||
- command: shell.exec
|
||||
type: test
|
||||
params:
|
||||
silent: true
|
||||
working_dir: "src"
|
||||
binary: bash
|
||||
args:
|
||||
- ${DRIVERS_TOOLS}/.evergreen/csfle/await-servers.sh
|
||||
script: |
|
||||
cat <<EOT > fle_creds.sh
|
||||
export FLE_AWS_KEY="${fle_aws_key}"
|
||||
export FLE_AWS_SECRET="${fle_aws_secret}"
|
||||
export FLE_AZURE_CLIENTID="${fle_azure_clientid}"
|
||||
export FLE_AZURE_TENANTID="${fle_azure_tenantid}"
|
||||
export FLE_AZURE_CLIENTSECRET="${fle_azure_clientsecret}"
|
||||
export FLE_GCP_EMAIL="${fle_gcp_email}"
|
||||
export FLE_GCP_PRIVATEKEY="${fle_gcp_privatekey}"
|
||||
# Needed for generating temporary aws credentials.
|
||||
export AWS_ACCESS_KEY_ID="${fle_aws_key}"
|
||||
export AWS_SECRET_ACCESS_KEY="${fle_aws_secret}"
|
||||
export AWS_DEFAULT_REGION=us-east-1
|
||||
EOT
|
||||
- command: shell.exec
|
||||
type: test
|
||||
params:
|
||||
working_dir: "src"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
LIBMONGOCRYPT_URL="${libmongocrypt_url}" \
|
||||
TEST_ENCRYPTION=1 \
|
||||
PYTHON_BINARY="${PYTHON_BINARY}" \
|
||||
TOX_ENV="${TOX_ENV}" \
|
||||
VIRTUALENV="${VIRTUALENV}" \
|
||||
CHECK_EXCLUDE_PATTERNS=1 \
|
||||
AUTH="${AUTH}" \
|
||||
SSL="${SSL}" \
|
||||
CERT_DIR="${DRIVERS_TOOLS}/.evergreen/x509gen" \
|
||||
bash ${PROJECT_DIRECTORY}/.evergreen/run-tox.sh
|
||||
# Disable xtrace (just in case it was accidentally set).
|
||||
set +x
|
||||
. ./fle_creds.sh
|
||||
rm -f ./fle_creds.sh
|
||||
set -x
|
||||
export LIBMONGOCRYPT_URL="${libmongocrypt_url}"
|
||||
export TEST_ENCRYPTION=1
|
||||
PYTHON_BINARY="${PYTHON_BINARY}" \
|
||||
TOX_BINARY="${TOX_BINARY}" \
|
||||
TOX_ENV="${TOX_ENV}" \
|
||||
INSTALL_TOX="${INSTALL_TOX}" \
|
||||
VIRTUALENV="${VIRTUALENV}" \
|
||||
AUTH="${AUTH}" \
|
||||
SSL="${SSL}" \
|
||||
CERT_DIR="${DRIVERS_TOOLS}/.evergreen/x509gen" \
|
||||
bash ${PROJECT_DIRECTORY}/.evergreen/run-tox.sh
|
||||
|
||||
"run enterprise auth tests":
|
||||
- command: ec2.assume_role
|
||||
params:
|
||||
role_arn: ${aws_test_secrets_role}
|
||||
- command: shell.exec
|
||||
type: test
|
||||
params:
|
||||
working_dir: "src"
|
||||
include_expansions_in_env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
|
||||
script: |
|
||||
bash ${DRIVERS_TOOLS}/.evergreen/secrets_handling/setup-secrets.sh drivers/enterprise_auth
|
||||
- command: shell.exec
|
||||
type: test
|
||||
params:
|
||||
silent: true
|
||||
working_dir: "src"
|
||||
script: |
|
||||
set +x # disable xtrace
|
||||
# DO NOT ECHO WITH XTRACE (which PREPARE_SHELL does)
|
||||
CLIENT_PEM=${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem \
|
||||
CA_PEM=${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem \
|
||||
PYTHON_BINARY=${PYTHON_BINARY} \
|
||||
TOX_ENV=${TOX_ENV} \
|
||||
PROJECT_DIRECTORY=${PROJECT_DIRECTORY} \
|
||||
bash ${PROJECT_DIRECTORY}/.evergreen/run-enterprise-auth-tests.sh
|
||||
CA_PEM=${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem \
|
||||
PYTHON_BINARY=${PYTHON_BINARY} \
|
||||
TOX_ENV=${TOX_ENV} \
|
||||
SASL_HOST=${sasl_host} \
|
||||
SASL_PORT=${sasl_port} \
|
||||
SASL_USER=${sasl_user} \
|
||||
SASL_PASS=${sasl_pass} \
|
||||
SASL_DB=${sasl_db} \
|
||||
PRINCIPAL=${principal} \
|
||||
GSSAPI_DB=${gssapi_db} \
|
||||
KEYTAB_BASE64=${keytab_base64} \
|
||||
PROJECT_DIRECTORY=${PROJECT_DIRECTORY} \
|
||||
sh ${PROJECT_DIRECTORY}/.evergreen/run-enterprise-auth-tests.sh
|
||||
|
||||
"cleanup":
|
||||
- command: shell.exec
|
||||
@ -308,6 +500,8 @@ pre:
|
||||
- func: "install dependencies"
|
||||
|
||||
post:
|
||||
# Disabled, causing timeouts
|
||||
# - func: "upload working dir"
|
||||
- func: "upload mo artifacts"
|
||||
- func: "upload test results"
|
||||
- func: "stop mongo-orchestration"
|
||||
@ -346,6 +540,60 @@ 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:
|
||||
@ -481,33 +729,6 @@ 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:
|
||||
@ -563,7 +784,6 @@ tasks:
|
||||
- func: "run tox"
|
||||
|
||||
- name: "test-enterprise-auth"
|
||||
tags: ["pr"]
|
||||
commands:
|
||||
- func: "bootstrap mongo-orchestration"
|
||||
vars:
|
||||
@ -572,14 +792,12 @@ tasks:
|
||||
- func: "run enterprise auth tests"
|
||||
|
||||
- name: "docs"
|
||||
tags: ["pr"]
|
||||
commands:
|
||||
- func: "run tox"
|
||||
vars:
|
||||
TOX_ENV: docs
|
||||
|
||||
- name: "doctest"
|
||||
tags: ["pr"]
|
||||
commands:
|
||||
- func: "bootstrap mongo-orchestration"
|
||||
vars:
|
||||
@ -609,10 +827,26 @@ axes:
|
||||
- id: tox-env
|
||||
display_name: "Tox Env RHEL8"
|
||||
values:
|
||||
- id: "test-pypy310"
|
||||
- id: "test-pypy38"
|
||||
variables:
|
||||
TOX_ENV: "test"
|
||||
PYTHON_BINARY: "/opt/python/pypy3.10/bin/python3"
|
||||
PYTHON_BINARY: "/opt/python/pypy3.8/bin/python3"
|
||||
- id: "test-py37"
|
||||
variables:
|
||||
TOX_ENV: "test"
|
||||
PYTHON_BINARY: "/opt/python/3.7/bin/python3"
|
||||
- id: "test-py37"
|
||||
variables:
|
||||
TOX_ENV: "test"
|
||||
PYTHON_BINARY: "/opt/python/3.7/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"
|
||||
- id: "test-py310"
|
||||
variables:
|
||||
TOX_ENV: "test"
|
||||
@ -625,59 +859,51 @@ 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.10/bin/python3"
|
||||
- id: "synchro-py310"
|
||||
PYTHON_BINARY: "/opt/python/3.7/bin/python3"
|
||||
- id: "synchro-py37"
|
||||
variables:
|
||||
TOX_ENV: "synchro"
|
||||
PYTHON_BINARY: "/opt/python/3.10/bin/python3"
|
||||
- id: "synchro-py313"
|
||||
PYTHON_BINARY: "/opt/python/3.7/bin/python3"
|
||||
- id: "synchro-py312"
|
||||
variables:
|
||||
TOX_ENV: "synchro"
|
||||
PYTHON_BINARY: "/opt/python/3.13/bin/python3"
|
||||
PYTHON_BINARY: "/opt/python/3.12/bin/python3"
|
||||
|
||||
- id: tox-env-rhel8
|
||||
display_name: "Tox Env RHEL8"
|
||||
- id: tox-env-rhel7
|
||||
display_name: "Tox Env RHEL7"
|
||||
values:
|
||||
- id: "test"
|
||||
variables:
|
||||
TOX_ENV: "test"
|
||||
PYTHON_BINARY: "/opt/python/3.10/bin/python3"
|
||||
PYTHON_BINARY: "/opt/python/3.9/bin/python3"
|
||||
|
||||
# Test Python 3.10 only on Mac.
|
||||
# Test Python 3.8 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.10/bin/python3"
|
||||
PYTHON_BINARY: "/Library/Frameworks/Python.framework/Versions/3.8/bin/python3"
|
||||
|
||||
- id: tox-env-win
|
||||
display_name: "Tox Env Windows"
|
||||
values:
|
||||
- id: "test-py37"
|
||||
variables:
|
||||
TOX_ENV: "test"
|
||||
PYTHON_BINARY: "c:/python/Python37/python.exe"
|
||||
- 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"
|
||||
@ -690,27 +916,22 @@ 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"
|
||||
values:
|
||||
- id: "rhel8"
|
||||
display_name: "RHEL 8.x"
|
||||
- id: "rhel84"
|
||||
display_name: "RHEL 8.4"
|
||||
run_on: "rhel84-small"
|
||||
- id: "rhel76"
|
||||
display_name: "RHEL 7.6"
|
||||
run_on: "rhel76-small"
|
||||
- id: "win"
|
||||
display_name: "Windows"
|
||||
run_on: "windows-64-vsMulti-small"
|
||||
- id: "macos-1400"
|
||||
display_name: "macOS 14.00"
|
||||
run_on: "macos-1400"
|
||||
- id: "macos-1100"
|
||||
display_name: "macOS 11.00"
|
||||
run_on: "macos-1100"
|
||||
|
||||
buildvariants:
|
||||
|
||||
@ -718,31 +939,31 @@ buildvariants:
|
||||
- matrix_name: "main"
|
||||
display_name: "${os}-${tox-env}-${ssl}"
|
||||
matrix_spec:
|
||||
os: "rhel8"
|
||||
os: "rhel84"
|
||||
tox-env: "*"
|
||||
ssl: "*"
|
||||
exclude_spec:
|
||||
# TODO: synchro needs PyMongo's updated SSL test certs,
|
||||
# which may require Motor test suite changes.
|
||||
- os: "*"
|
||||
tox-env: ["synchro-py310", "synchro-py313"]
|
||||
tox-env: ["synchro-py37", "synchro-py312"]
|
||||
ssl: "ssl"
|
||||
tasks:
|
||||
- ".rapid"
|
||||
- ".latest"
|
||||
- ".8.0"
|
||||
- ".7.0"
|
||||
- ".6.0"
|
||||
- ".5.0"
|
||||
- ".4.4"
|
||||
- ".4.2"
|
||||
- ".4.0"
|
||||
- ".3.6"
|
||||
|
||||
- matrix_name: "test-rhel8"
|
||||
display_name: "${os}-${tox-env-rhel8}-${ssl}"
|
||||
- matrix_name: "test-rhel7"
|
||||
display_name: "${os}-${tox-env-rhel7}-${ssl}"
|
||||
matrix_spec:
|
||||
os: "rhel8"
|
||||
tox-env-rhel8: "*"
|
||||
os: "rhel76"
|
||||
tox-env-rhel7: "*"
|
||||
ssl: "*"
|
||||
tasks:
|
||||
- ".rapid"
|
||||
@ -761,7 +982,6 @@ buildvariants:
|
||||
tasks:
|
||||
- ".rapid"
|
||||
- ".latest"
|
||||
- ".8.0"
|
||||
- ".7.0"
|
||||
- ".6.0"
|
||||
- ".5.0"
|
||||
@ -773,19 +993,18 @@ buildvariants:
|
||||
- matrix_name: "test-macos"
|
||||
display_name: "${os}-${tox-env-osx}-${ssl}"
|
||||
matrix_spec:
|
||||
os: "macos-1400"
|
||||
os: "macos-1100"
|
||||
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-py310", "synchro-py313"], ssl: "ssl"}
|
||||
matrix_spec: {"tox-env": ["synchro-py37", "synchro-py312"], ssl: "ssl"}
|
||||
run_on:
|
||||
- "rhel84-small"
|
||||
tasks:
|
||||
@ -797,7 +1016,7 @@ buildvariants:
|
||||
- "rhel84-small"
|
||||
expansions:
|
||||
TOX_ENV: "docs"
|
||||
PYTHON_BINARY: "/opt/python/3.10/bin/python3"
|
||||
PYTHON_BINARY: "/opt/python/3.7/bin/python3"
|
||||
tasks:
|
||||
- name: "docs"
|
||||
|
||||
@ -807,6 +1026,6 @@ buildvariants:
|
||||
- "rhel84-small"
|
||||
expansions:
|
||||
TOX_ENV: "doctest"
|
||||
PYTHON_BINARY: "/opt/python/3.10/bin/python3"
|
||||
PYTHON_BINARY: "/opt/python/3.7/bin/python3"
|
||||
tasks:
|
||||
- name: "doctest"
|
||||
|
||||
@ -2,10 +2,8 @@
|
||||
|
||||
# Don't trace to avoid secrets showing up in the logs
|
||||
set -o errexit
|
||||
set +x
|
||||
|
||||
echo "Running enterprise authentication tests"
|
||||
source ./secrets-export.sh
|
||||
|
||||
export DB_USER="bob"
|
||||
export DB_PASSWORD="pwd123"
|
||||
@ -25,6 +23,7 @@ export GSSAPI_PORT=${SASL_PORT}
|
||||
export GSSAPI_PRINCIPAL=${PRINCIPAL}
|
||||
|
||||
# Pass needed env variables to the test environment.
|
||||
export TOX_ENV="enterprise-synchro"
|
||||
export TOX_TESTENV_PASSENV="*"
|
||||
|
||||
bash ${PROJECT_DIRECTORY}/.evergreen/run-tox.sh
|
||||
# --sitepackages allows use of pykerberos without a test dep.
|
||||
/opt/python/3.7/bin/python3 -m tox -m "$TOX_ENV" --sitepackages -- -x test.test_auth
|
||||
|
||||
@ -5,8 +5,8 @@ set -o errexit # Exit the script with error if any of the commands fail
|
||||
# Supported/used environment variables:
|
||||
# AUTH Set to enable authentication. Defaults to "noauth"
|
||||
# SSL Set to enable SSL. Defaults to "nossl"
|
||||
# TOX_ENV Tox environment name, e.g. "synchro", required.
|
||||
# PYTHON_BINARY Path to python, required.
|
||||
# TOX_ENV Tox environment name, e.g. "tornado5-py37"
|
||||
# PYTHON_BINARY Path to python
|
||||
|
||||
AUTH=${AUTH:-noauth}
|
||||
SSL=${SSL:-nossl}
|
||||
@ -16,11 +16,6 @@ if [ -z $PYTHON_BINARY ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z $TOX_ENV ]; then
|
||||
echo "TOX_ENV is undefined!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$AUTH" != "noauth" ]; then
|
||||
export DB_USER="bob"
|
||||
export DB_PASSWORD="pwd123"
|
||||
@ -31,9 +26,6 @@ if [ "$SSL" != "nossl" ]; then
|
||||
export CA_PEM="$DRIVERS_TOOLS/.evergreen/x509gen/ca.pem"
|
||||
fi
|
||||
|
||||
if [ -f secrets-export.sh ]; then
|
||||
source secrets-export.sh
|
||||
fi
|
||||
|
||||
# Usage:
|
||||
# createvirtualenv /path/to/python /output/path/for/venv
|
||||
@ -62,17 +54,22 @@ createvirtualenv () {
|
||||
. $VENVPATH/bin/activate
|
||||
fi
|
||||
|
||||
python -m pip install -q --upgrade pip
|
||||
python -m pip install -q --upgrade tox
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install --upgrade setuptools wheel tox
|
||||
}
|
||||
|
||||
|
||||
# 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 "$@"
|
||||
}
|
||||
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
|
||||
|
||||
run_tox "${@:1}"
|
||||
|
||||
31
.flake8
Normal file
31
.flake8
Normal file
@ -0,0 +1,31 @@
|
||||
[flake8]
|
||||
max-line-length = 100
|
||||
enable-extensions = G
|
||||
extend-ignore =
|
||||
G200, G202, G001
|
||||
# black adds spaces around ':'
|
||||
E203,
|
||||
# E501 line too long (let black handle line length)
|
||||
E501
|
||||
# B305 `.next()` is not a thing on Python 3. Use the `next()` builtin. For Python 2 compatibility, use `six.next()`.
|
||||
B305
|
||||
per-file-ignores =
|
||||
# F841 local variable 'foo' is assigned to but never used
|
||||
# E731 do not assign a lambda expression, use a def
|
||||
# F811 redefinition of unused 'foo' from line XXX
|
||||
test/*/test_examples.py: F841,E731,F811
|
||||
|
||||
# F811 redefinition of unused 'foo' from line XXX
|
||||
# B011 Do not call assert False since python -O removes these calls. Instead callers should raise AssertionError().
|
||||
|
||||
test/*: F811,B011
|
||||
|
||||
# E402 module level import not at top of file
|
||||
doc/examples/monitoring_example.py: E402
|
||||
|
||||
# F403 'from foo import *' used; unable to detect undefined names
|
||||
# F401 'foo' imported but unused
|
||||
synchro/__init__.py: F403,F401
|
||||
|
||||
# F401 'foo' imported but unused
|
||||
motor/__init__.py: F401
|
||||
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -1 +1,2 @@
|
||||
# Global owner for repo
|
||||
* @mongodb/dbx-python
|
||||
|
||||
16
.github/dependabot.yml
vendored
16
.github/dependabot.yml
vendored
@ -1,16 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
# GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- "*"
|
||||
# Python
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
5
.github/reviewers.txt
vendored
5
.github/reviewers.txt
vendored
@ -1,5 +0,0 @@
|
||||
# List of reviewers for auto-assignment of reviews.
|
||||
caseyclements
|
||||
blink1073
|
||||
Jibola
|
||||
NoahStapp
|
||||
76
.github/workflows/codeql.yml
vendored
76
.github/workflows/codeql.yml
vendored
@ -1,76 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master", "*" ]
|
||||
pull_request:
|
||||
branches: [ "master", "*" ]
|
||||
schedule:
|
||||
- cron: '35 23 * * 5'
|
||||
workflow_call:
|
||||
inputs:
|
||||
ref:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze ${{ matrix.language }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 360
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
actions: read
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: python
|
||||
- language: actions
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
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
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@c793b717bc78562f491db7b0e93a3a178b099162 # v4
|
||||
with:
|
||||
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:
|
||||
- 'test/**'
|
||||
|
||||
- shell: bash
|
||||
if: matrix.language == 'python'
|
||||
run: |
|
||||
pip install -e .
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@c793b717bc78562f491db7b0e93a3a178b099162 # v4
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
43
.github/workflows/dist.yml
vendored
43
.github/workflows/dist.yml
vendored
@ -1,43 +0,0 @@
|
||||
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/*"
|
||||
115
.github/workflows/release.yml
vendored
115
.github/workflows/release.yml
vendored
@ -1,115 +0,0 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
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:
|
||||
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: 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-dist, static-scan]
|
||||
name: Upload release to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
permissions:
|
||||
id-token: write
|
||||
steps:
|
||||
- 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 }}
|
||||
70
.github/workflows/test-python.yml
vendored
70
.github/workflows/test-python.yml
vendored
@ -19,26 +19,23 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
python-version: ["3.10", "3.12", "3.14"]
|
||||
os: [ubuntu-20.04]
|
||||
python-version: ["3.7", "3.12"]
|
||||
fail-fast: false
|
||||
name: CPython ${{ matrix.python-version }}-${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'pyproject.toml'
|
||||
allow-prereleases: true
|
||||
- id: setup-mongodb
|
||||
uses: mongodb-labs/drivers-evergreen-tools@master
|
||||
with:
|
||||
version: "8.0"
|
||||
topology: replica_set
|
||||
- name: Start MongoDB with Custom Options
|
||||
run: |
|
||||
mkdir data
|
||||
mongod --fork --dbpath=$(pwd)/data --logpath=$PWD/mongo.log --setParameter enableTestCommands=1
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install -U pip tox
|
||||
@ -47,14 +44,12 @@ jobs:
|
||||
tox -m test
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: 3.8
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'pyproject.toml'
|
||||
- name: Install Python dependencies
|
||||
@ -63,16 +58,15 @@ jobs:
|
||||
- name: Run linters
|
||||
run: |
|
||||
tox -m lint-manual
|
||||
tox -m manifest
|
||||
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: 3.7
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'pyproject.toml'
|
||||
- name: Install Python dependencies
|
||||
@ -82,22 +76,20 @@ jobs:
|
||||
run: tox -m docs
|
||||
- name: Run linkcheck
|
||||
run: tox -m linkcheck
|
||||
- name: Start MongoDB
|
||||
uses: supercharge/mongodb-github-action@315db7fe45ac2880b7758f1933e6e5d59afd5e94 # 1.12.1
|
||||
with:
|
||||
mongodb-version: 5.0
|
||||
- name: Start MongoDB with Custom Options
|
||||
run: |
|
||||
mkdir data
|
||||
mongod --fork --dbpath=$(pwd)/data --logpath=$PWD/mongo.log --setParameter enableTestCommands=1
|
||||
- name: Run doctest
|
||||
run: tox -m doctest
|
||||
|
||||
release:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: 3.7
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'pyproject.toml'
|
||||
- name: Install Python dependencies
|
||||
@ -110,14 +102,16 @@ jobs:
|
||||
typing:
|
||||
name: Typing Tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.7', '3.12']
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'pyproject.toml'
|
||||
allow-prereleases: true
|
||||
|
||||
21
.github/workflows/zizmor.yml
vendored
21
.github/workflows/zizmor.yml
vendored
@ -1,21 +0,0 @@
|
||||
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
7
.github/zizmor.yml
vendored
@ -1,7 +0,0 @@
|
||||
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
1
.gitignore
vendored
@ -14,4 +14,3 @@ doc/_build/
|
||||
xunit-results
|
||||
xunit-synchro-results
|
||||
.eggs
|
||||
toxenv
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
rev: v4.1.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-case-conflict
|
||||
- id: check-toml
|
||||
- id: check-json
|
||||
- id: check-yaml
|
||||
exclude: template.yaml
|
||||
- id: debug-statements
|
||||
- id: end-of-file-fixer
|
||||
exclude: WHEEL
|
||||
@ -18,72 +16,44 @@ repos:
|
||||
exclude: .patch
|
||||
exclude_types: [json]
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.1.3
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.3.0
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: ["--fix", "--show-fixes"]
|
||||
- id: ruff-format
|
||||
- id: black
|
||||
files: \.(py|pyi)$
|
||||
args: [--line-length=100]
|
||||
|
||||
- repo: https://github.com/adamchainz/blacken-docs
|
||||
rev: "1.16.0"
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: blacken-docs
|
||||
additional_dependencies:
|
||||
- black==22.3.0
|
||||
- id: isort
|
||||
files: \.(py|pyi)$
|
||||
args: [--profile=black]
|
||||
|
||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||
rev: "v1.10.0"
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 3.9.2
|
||||
hooks:
|
||||
- id: rst-backticks
|
||||
- id: rst-directive-colons
|
||||
- id: rst-inline-touching-normal
|
||||
|
||||
- repo: https://github.com/rstcheck/rstcheck
|
||||
rev: v6.2.0
|
||||
hooks:
|
||||
- id: rstcheck
|
||||
additional_dependencies: [sphinx]
|
||||
args: ["--ignore-directives=doctest,testsetup,todo,automodule,mongodoc,autodoc,testcleanup,autoclass","--ignore-substitutions=release", "--report-level=error"]
|
||||
exclude: '^doc/migrate-to-motor-3.rst'
|
||||
- id: flake8
|
||||
additional_dependencies: [
|
||||
'flake8-bugbear==20.1.4',
|
||||
'flake8-logging-format==0.6.0',
|
||||
'flake8-implicit-str-concat==0.2.0',
|
||||
]
|
||||
|
||||
# We use the Python version instead of the original version which seems to require Docker
|
||||
# https://github.com/koalaman/shellcheck-precommit
|
||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||
rev: v0.9.0.6
|
||||
rev: v0.8.0.4
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
name: shellcheck
|
||||
args: ["--severity=warning"]
|
||||
stages: [manual]
|
||||
|
||||
- repo: https://github.com/PyCQA/doc8
|
||||
rev: v1.1.1
|
||||
hooks:
|
||||
- id: doc8
|
||||
args: ["--ignore=D001"] # ignore line length
|
||||
stages: [manual]
|
||||
|
||||
- repo: https://github.com/sirosen/check-jsonschema
|
||||
rev: 0.29.0
|
||||
rev: 0.14.1
|
||||
hooks:
|
||||
- id: check-github-workflows
|
||||
- id: check-github-actions
|
||||
- id: check-dependabot
|
||||
|
||||
- repo: https://github.com/ariebovenberg/slotscheck
|
||||
rev: v0.17.0
|
||||
hooks:
|
||||
- id: slotscheck
|
||||
files: \.py$
|
||||
exclude: "^(doc|test)/"
|
||||
stages: [manual]
|
||||
args: ["--no-strict-imports"]
|
||||
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: "v2.2.6"
|
||||
hooks:
|
||||
- id: codespell
|
||||
args: ["-L", "fle"]
|
||||
- id: check-jsonschema
|
||||
name: "Check GitHub Workflows"
|
||||
files: ^\.github/workflows/
|
||||
types: [yaml]
|
||||
args: ["--schemafile", "https://json.schemastore.org/github-workflow"]
|
||||
|
||||
@ -15,7 +15,7 @@ python:
|
||||
# Install motor itself.
|
||||
- method: pip
|
||||
path: .
|
||||
- requirements: requirements/docs.txt
|
||||
- requirements: doc/docs-requirements.txt
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
# Contributing to Motor
|
||||
|
||||
Contributions are encouraged. Please read these guidelines before
|
||||
sending a pull request.
|
||||
|
||||
## Bugfixes and New Features
|
||||
|
||||
Before starting to write code, look for existing tickets or create one
|
||||
in [Jira](https://jira.mongodb.org/browse/MOTOR) for your specific issue
|
||||
or feature request.
|
||||
|
||||
## Running Tests
|
||||
|
||||
Install a recent version of MongoDB and run it on the default port from
|
||||
a clean data directory. Pass "--setParameter enableTestCommands=1" to
|
||||
mongod to enable testing MotorCursor's `max_time_ms` method.
|
||||
|
||||
Control how the tests connect to MongoDB with these environment
|
||||
variables:
|
||||
|
||||
- `DB_IP`: Defaults to "localhost", can be a domain name or IP
|
||||
- `DB_PORT`: Defaults to 27017
|
||||
- `DB_USER`, `DB_PASSWORD`: To test with authentication, create an
|
||||
admin user and set these environment variables to the username and
|
||||
password
|
||||
- `CERT_DIR`: Path with alternate client.pem and ca.pem for testing.
|
||||
Otherwise the suite uses those in test/certificates/.
|
||||
|
||||
Install [tox](https://testrun.org/tox/) and run it from the command line
|
||||
in the repository directory. You will need a variety of Python
|
||||
interpreters installed. For a minimal test, ensure you have your desired
|
||||
Python version on your path, and run:
|
||||
|
||||
```bash
|
||||
tox -m test
|
||||
```
|
||||
|
||||
The doctests pass with Python 3.10+ and a MongoDB 5.0 instance running on
|
||||
port 27017:
|
||||
|
||||
```bash
|
||||
tox -m doctest
|
||||
```
|
||||
|
||||
## Running Linters
|
||||
|
||||
Motor uses [pre-commit](https://pypi.org/project/pre-commit/) for
|
||||
managing linting of the codebase. `pre-commit` performs various checks
|
||||
on all files in Motor and uses tools that help follow a consistent code
|
||||
style within the codebase.
|
||||
|
||||
To set up `pre-commit` locally, run:
|
||||
|
||||
```bash
|
||||
pip install pre-commit # or brew install pre-commit for global install.
|
||||
pre-commit install
|
||||
```
|
||||
To run `pre-commit` manually, run:
|
||||
|
||||
```bash
|
||||
tox -m lint
|
||||
```
|
||||
|
||||
## General Guidelines
|
||||
|
||||
- Avoid backward breaking changes if at all possible.
|
||||
- Write inline documentation for new classes and methods.
|
||||
- Add yourself to doc/contributors.rst :)
|
||||
65
CONTRIBUTING.rst
Normal file
65
CONTRIBUTING.rst
Normal file
@ -0,0 +1,65 @@
|
||||
Contributing to Motor
|
||||
=====================
|
||||
|
||||
Contributions are encouraged. Please read these guidelines before sending a
|
||||
pull request.
|
||||
|
||||
Bugfixes and New Features
|
||||
-------------------------
|
||||
|
||||
Before starting to write code, look for existing tickets or create one in `Jira
|
||||
<https://jira.mongodb.org/browse/MOTOR>`_ for your specific issue or feature
|
||||
request.
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
|
||||
Install a recent version of MongoDB and run it on the default port from a clean
|
||||
data directory. Pass "--setParameter enableTestCommands=1" to mongod to enable
|
||||
testing MotorCursor's ``max_time_ms`` method.
|
||||
|
||||
Control how the tests connect to MongoDB with these environment variables:
|
||||
|
||||
- ``DB_IP``: Defaults to "localhost", can be a domain name or IP
|
||||
- ``DB_PORT``: Defaults to 27017
|
||||
- ``DB_USER``, ``DB_PASSWORD``: To test with authentication, create an admin
|
||||
user and set these environment variables to the username and password
|
||||
- ``CERT_DIR``: Path with alternate client.pem and ca.pem for testing.
|
||||
Otherwise the suite uses those in test/certificates/.
|
||||
|
||||
Install `tox`_ and run it from the command line in the repository directory.
|
||||
You will need a variety of Python interpreters installed. For a minimal test,
|
||||
ensure you have your desired Python version on your path, and run::
|
||||
|
||||
> tox -m test
|
||||
|
||||
The doctests pass with Python 3.7+ and a MongoDB 5.0 instance running on
|
||||
port 27017:
|
||||
|
||||
> tox -m doctest
|
||||
|
||||
.. _tox: https://testrun.org/tox/
|
||||
|
||||
Running Linters
|
||||
---------------
|
||||
|
||||
Motor uses `pre-commit <https://pypi.org/project/pre-commit/>`_
|
||||
for managing linting of the codebase.
|
||||
``pre-commit`` performs various checks on all files in Motor and uses tools
|
||||
that help follow a consistent code style within the codebase.
|
||||
|
||||
To set up ``pre-commit`` locally, run::
|
||||
|
||||
pip install pre-commit
|
||||
pre-commit install
|
||||
|
||||
To run ``pre-commit`` manually, run::
|
||||
|
||||
> tox -m lint
|
||||
|
||||
General Guidelines
|
||||
------------------
|
||||
|
||||
- Avoid backward breaking changes if at all possible.
|
||||
- Write inline documentation for new classes and methods.
|
||||
- Add yourself to doc/contributors.rst :)
|
||||
31
MANIFEST.in
Normal file
31
MANIFEST.in
Normal file
@ -0,0 +1,31 @@
|
||||
include README.rst
|
||||
include LICENSE
|
||||
include tox.ini
|
||||
include pytest.ini
|
||||
include pyproject.toml
|
||||
include mypy.ini
|
||||
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
|
||||
|
||||
exclude .flake8
|
||||
exclude .readthedocs.yaml
|
||||
exclude .git-blame-ignore-revs
|
||||
exclude .pre-commit-config.yaml
|
||||
exclude release.sh
|
||||
exclude RELEASE.rst
|
||||
exclude CONTRIBUTING.rst
|
||||
exclude .evergreen/*
|
||||
209
README.md
209
README.md
@ -1,209 +0,0 @@
|
||||
# Motor
|
||||
|
||||
[](https://pypi.org/project/motor)
|
||||
[](https://pypi.org/project/motor)
|
||||
[](https://pepy.tech/project/motor)
|
||||
[](http://motor.readthedocs.io/en/stable/?badge=stable)
|
||||
|
||||

|
||||
|
||||
> [!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/)
|
||||
driver for Python [asyncio](https://docs.python.org/3/library/asyncio.html) and
|
||||
[Tornado](http://tornadoweb.org/) applications. Motor presents a coroutine-based API
|
||||
for non-blocking access to MongoDB.
|
||||
|
||||
> "We use Motor in high throughput environments, processing tens of
|
||||
> thousands of requests per second. It allows us to take full advantage
|
||||
> of modern hardware, ensuring we utilise the entire capacity of our
|
||||
> purchased CPUs. This helps us be more efficient with computing power,
|
||||
> compute spend and minimises the environmental impact of our
|
||||
> infrastructure as a result."
|
||||
>
|
||||
> --*David Mytton, Server Density*
|
||||
>
|
||||
> "We develop easy-to-use sensors and sensor systems with open source
|
||||
> software to ensure every innovator, from school child to laboratory
|
||||
> researcher, has the same opportunity to create. We integrate Motor
|
||||
> into our software to guarantee massively scalable sensor systems for
|
||||
> everyone."
|
||||
>
|
||||
> --*Ryan Smith, inXus Interactive*
|
||||
|
||||
## Support / Feedback
|
||||
|
||||
For issues with, questions about, or feedback for PyMongo, please look
|
||||
into our [support channels](https://support.mongodb.com/welcome). Please
|
||||
do not email any of the Motor developers directly with issues or
|
||||
questions - you're more likely to get an answer on the
|
||||
[StackOverflow](https://stackoverflow.com/questions/tagged/mongodb)
|
||||
(using a "mongodb" tag).
|
||||
|
||||
## Bugs / Feature Requests
|
||||
|
||||
Think you've found a bug? Want to see a new feature in Motor? Please
|
||||
open a case in our issue management tool, JIRA:
|
||||
|
||||
- [Create an account and login](https://jira.mongodb.org).
|
||||
- Navigate to [the MOTOR
|
||||
project](https://jira.mongodb.org/browse/MOTOR).
|
||||
- Click **Create Issue** - Please provide as much information as
|
||||
possible about the issue type and how to reproduce it.
|
||||
|
||||
Bug reports in JIRA for all driver projects (i.e. MOTOR, CSHARP, JAVA)
|
||||
and the Core Server (i.e. SERVER) project are **public**.
|
||||
|
||||
### How To Ask For Help
|
||||
|
||||
Please include all of the following information when opening an issue:
|
||||
|
||||
- Detailed steps to reproduce the problem, including full traceback, if
|
||||
possible.
|
||||
|
||||
- The exact python version used, with patch level:
|
||||
|
||||
```bash
|
||||
python -c "import sys; print(sys.version)"
|
||||
```
|
||||
|
||||
- The exact version of Motor used, with patch level:
|
||||
|
||||
```bash
|
||||
python -c "import motor; print(motor.version)"
|
||||
```
|
||||
|
||||
- The exact version of PyMongo used, with patch level:
|
||||
|
||||
```bash
|
||||
python -c "import pymongo; print(pymongo.version); print(pymongo.has_c())"
|
||||
```
|
||||
|
||||
- The exact Tornado version, if you are using Tornado:
|
||||
|
||||
```bash
|
||||
python -c "import tornado; print(tornado.version)"
|
||||
```
|
||||
|
||||
- The operating system and version (e.g. RedHat Enterprise Linux 6.4,
|
||||
OSX 10.9.5, ...)
|
||||
|
||||
### Security Vulnerabilities
|
||||
|
||||
If you've identified a security vulnerability in a driver or any other
|
||||
MongoDB project, please report it according to the [instructions
|
||||
here](https://mongodb.com/docs/manual/tutorial/create-a-vulnerability-report).
|
||||
|
||||
## Installation
|
||||
|
||||
Motor can be installed with [pip](http://pypi.python.org/pypi/pip):
|
||||
|
||||
```bash
|
||||
pip install motor
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
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.9,<5
|
||||
- Python 3.10+
|
||||
|
||||
Optional dependencies:
|
||||
|
||||
Motor supports same optional dependencies as PyMongo. Required
|
||||
dependencies can be installed along with Motor.
|
||||
|
||||
GSSAPI authentication requires `gssapi` extra dependency. The correct
|
||||
dependency can be installed automatically along with Motor:
|
||||
|
||||
```bash
|
||||
pip install "motor[gssapi]"
|
||||
```
|
||||
|
||||
similarly,
|
||||
|
||||
MONGODB-AWS authentication requires `aws` extra dependency:
|
||||
|
||||
```bash
|
||||
pip install "motor[aws]"
|
||||
```
|
||||
|
||||
Support for mongodb+srv:// URIs requires `srv` extra dependency:
|
||||
|
||||
```bash
|
||||
pip install "motor[srv]"
|
||||
```
|
||||
|
||||
OCSP requires `ocsp` extra dependency:
|
||||
|
||||
```bash
|
||||
pip install "motor[ocsp]"
|
||||
```
|
||||
|
||||
Wire protocol compression with snappy requires `snappy` extra
|
||||
dependency:
|
||||
|
||||
```bash
|
||||
pip install "motor[snappy]"
|
||||
```
|
||||
|
||||
Wire protocol compression with zstandard requires `zstd` extra
|
||||
dependency:
|
||||
|
||||
```bash
|
||||
pip install "motor[zstd]"
|
||||
```
|
||||
|
||||
Client-Side Field Level Encryption requires `encryption` extra
|
||||
dependency:
|
||||
|
||||
```bash
|
||||
pip install "motor[encryption]"
|
||||
```
|
||||
|
||||
You can install all dependencies automatically with the following
|
||||
command:
|
||||
|
||||
```bash
|
||||
pip install "motor[gssapi,aws,ocsp,snappy,srv,zstd,encryption]"
|
||||
```
|
||||
|
||||
See
|
||||
[requirements](https://motor.readthedocs.io/en/stable/requirements.html)
|
||||
for details about compatibility.
|
||||
|
||||
## Examples
|
||||
|
||||
See the [examples on
|
||||
ReadTheDocs](https://motor.readthedocs.io/en/stable/examples/index.html).
|
||||
|
||||
## Documentation
|
||||
|
||||
Motor's documentation is on
|
||||
[ReadTheDocs](https://motor.readthedocs.io/en/stable/).
|
||||
|
||||
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`.
|
||||
|
||||
## Learning Resources
|
||||
|
||||
- MongoDB Learn - [Python
|
||||
courses](https://learn.mongodb.com/catalog?labels=%5B%22Language%22%5D&values=%5B%22Python%22%5D).
|
||||
- [Python Articles on Developer
|
||||
Center](https://www.mongodb.com/developer/languages/python/).
|
||||
|
||||
## Testing
|
||||
|
||||
Run `python setup.py test`. Tests are located in the `test/` directory.
|
||||
184
README.rst
Normal file
184
README.rst
Normal file
@ -0,0 +1,184 @@
|
||||
=====
|
||||
Motor
|
||||
=====
|
||||
|
||||
.. image:: https://raw.github.com/mongodb/motor/master/doc/_static/motor.png
|
||||
|
||||
:Info: Motor is a full-featured, non-blocking MongoDB_ driver for Python
|
||||
Tornado_ and asyncio_ applications.
|
||||
:Documentation: Available at `motor.readthedocs.io <https://motor.readthedocs.io/en/stable/>`_
|
||||
:Author: A\. Jesse Jiryu Davis
|
||||
|
||||
About
|
||||
=====
|
||||
|
||||
Motor presents a coroutine-based API for non-blocking access
|
||||
to MongoDB. The source is `on GitHub <https://github.com/mongodb/motor>`_
|
||||
and the docs are on ReadTheDocs_.
|
||||
|
||||
"We use Motor in high throughput environments, processing tens of thousands
|
||||
of requests per second. It allows us to take full advantage of modern
|
||||
hardware, ensuring we utilise the entire capacity of our purchased CPUs.
|
||||
This helps us be more efficient with computing power, compute spend and
|
||||
minimises the environmental impact of our infrastructure as a result."
|
||||
|
||||
--*David Mytton, Server Density*
|
||||
|
||||
"We develop easy-to-use sensors and sensor systems with open source
|
||||
software to ensure every innovator, from school child to laboratory
|
||||
researcher, has the same opportunity to create. We integrate Motor into our
|
||||
software to guarantee massively scalable sensor systems for everyone."
|
||||
|
||||
--*Ryan Smith, inXus Interactive*
|
||||
|
||||
Support / Feedback
|
||||
==================
|
||||
|
||||
For issues with, questions about, or feedback for PyMongo, please look into
|
||||
our `support channels <https://support.mongodb.com/welcome>`_. Please
|
||||
do not email any of the Motor developers directly with issues or
|
||||
questions - you're more likely to get an answer on the `StackOverflow <https://stackoverflow.com/questions/tagged/mongodb>`_ (using a "mongodb" tag).
|
||||
|
||||
Bugs / Feature Requests
|
||||
=======================
|
||||
|
||||
Think you've found a bug? Want to see a new feature in Motor? Please open a
|
||||
case in our issue management tool, JIRA:
|
||||
|
||||
- `Create an account and login <https://jira.mongodb.org>`_.
|
||||
- Navigate to `the MOTOR project <https://jira.mongodb.org/browse/MOTOR>`_.
|
||||
- Click **Create Issue** - Please provide as much information as possible about the issue type and how to reproduce it.
|
||||
|
||||
Bug reports in JIRA for all driver projects (i.e. MOTOR, CSHARP, JAVA) and the
|
||||
Core Server (i.e. SERVER) project are **public**.
|
||||
|
||||
How To Ask For Help
|
||||
-------------------
|
||||
|
||||
Please include all of the following information when opening an issue:
|
||||
|
||||
- Detailed steps to reproduce the problem, including full traceback, if possible.
|
||||
- The exact python version used, with patch level::
|
||||
|
||||
$ python -c "import sys; print(sys.version)"
|
||||
|
||||
- The exact version of Motor used, with patch level::
|
||||
|
||||
$ python -c "import motor; print(motor.version)"
|
||||
|
||||
- The exact version of PyMongo used, with patch level::
|
||||
|
||||
$ python -c "import pymongo; print(pymongo.version); print(pymongo.has_c())"
|
||||
|
||||
- The exact Tornado version, if you are using Tornado::
|
||||
|
||||
$ python -c "import tornado; print(tornado.version)"
|
||||
|
||||
- The operating system and version (e.g. RedHat Enterprise Linux 6.4, OSX 10.9.5, ...)
|
||||
|
||||
Security Vulnerabilities
|
||||
------------------------
|
||||
|
||||
If you've identified a security vulnerability in a driver or any other
|
||||
MongoDB project, please report it according to the `instructions here
|
||||
<https://mongodb.com/docs/manual/tutorial/create-a-vulnerability-report>`_.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Motor can be installed with `pip <http://pypi.python.org/pypi/pip>`_::
|
||||
|
||||
$ pip install motor
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
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.7+
|
||||
|
||||
Optional dependencies:
|
||||
|
||||
Motor supports same optional dependencies as PyMongo. Required dependencies can be installed
|
||||
along with Motor.
|
||||
|
||||
GSSAPI authentication requires ``gssapi`` extra dependency. The correct
|
||||
dependency can be installed automatically along with Motor::
|
||||
|
||||
$ pip install "motor[gssapi]"
|
||||
|
||||
similarly,
|
||||
|
||||
MONGODB-AWS authentication requires ``aws`` extra dependency::
|
||||
|
||||
$ pip install "motor[aws]"
|
||||
|
||||
Support for mongodb+srv:// URIs requires ``srv`` extra dependency::
|
||||
|
||||
$ pip install "motor[srv]"
|
||||
|
||||
OCSP requires ``ocsp`` extra dependency::
|
||||
|
||||
$ pip install "motor[ocsp]"
|
||||
|
||||
Wire protocol compression with snappy requires ``snappy`` extra dependency::
|
||||
|
||||
$ pip install "motor[snappy]"
|
||||
|
||||
Wire protocol compression with zstandard requires ``zstd`` extra dependency::
|
||||
|
||||
$ pip install "motor[zstd]"
|
||||
|
||||
Client-Side Field Level Encryption requires ``encryption`` extra dependency::
|
||||
|
||||
$ pip install "motor[encryption]"
|
||||
|
||||
You can install all dependencies automatically with the following
|
||||
command::
|
||||
|
||||
$ pip install "motor[gssapi,aws,ocsp,snappy,srv,zstd,encryption]"
|
||||
|
||||
See `requirements <https://motor.readthedocs.io/en/stable/requirements.html>`_
|
||||
for details about compatibility.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
See the `examples on ReadTheDocs <https://motor.readthedocs.io/en/stable/examples/index.html>`_.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
Motor's documentation is on ReadTheDocs_.
|
||||
|
||||
Build the documentation with Python 3.7+. Install sphinx_, Tornado_, and aiohttp_,
|
||||
and do ``cd doc; make html``.
|
||||
|
||||
Learning Resources
|
||||
==================
|
||||
|
||||
MongoDB Learn - `Python courses <https://learn.mongodb.com/catalog?labels=%5B%22Language%22%5D&values=%5B%22Python%22%5D>`_.
|
||||
`Python Articles on Developer Center <https://www.mongodb.com/developer/languages/python/>`_.
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
Run ``python setup.py test``.
|
||||
Tests are located in the ``test/`` directory.
|
||||
|
||||
.. _PyMongo: http://pypi.python.org/pypi/pymongo/
|
||||
|
||||
.. _MongoDB: http://mongodb.org/
|
||||
|
||||
.. _Tornado: http://tornadoweb.org/
|
||||
|
||||
.. _asyncio: https://docs.python.org/3/library/asyncio.html
|
||||
|
||||
.. _aiohttp: https://github.com/aio-libs/aiohttp
|
||||
|
||||
.. _ReadTheDocs: https://motor.readthedocs.io/en/stable/
|
||||
|
||||
.. _sphinx: http://sphinx.pocoo.org/
|
||||
71
RELEASE.md
71
RELEASE.md
@ -1,71 +0,0 @@
|
||||
# Motor Releases
|
||||
|
||||
## Versioning
|
||||
|
||||
Motor's version numbers follow [semantic
|
||||
versioning](http://semver.org/): each version number is structured
|
||||
"major.minor.patch". Patch releases fix bugs, minor releases add
|
||||
features (and may fix bugs), and major releases include API changes that
|
||||
break backwards compatibility (and may add features and fix bugs).
|
||||
|
||||
In between releases we add .devN to the version number to denote the
|
||||
version under development. So if we just released 2.3.0, then the
|
||||
current dev version might be 2.3.1.dev0 or 2.4.0.dev0. When we make the
|
||||
next release we replace all instances of 2.x.x.devN in the docs with the
|
||||
new version number.
|
||||
|
||||
<https://www.python.org/dev/peps/pep-0440/>
|
||||
|
||||
## Release Process
|
||||
|
||||
Motor ships a [pure Python
|
||||
wheel](https://packaging.python.org/guides/distributing-packages-using-setuptools/#pure-python-wheels)
|
||||
and a [source
|
||||
distribution](https://packaging.python.org/guides/distributing-packages-using-setuptools/#source-distributions).
|
||||
|
||||
1. Motor is tested on Evergreen. Ensure that the latest commit is
|
||||
passing CI as expected:
|
||||
<https://evergreen.mongodb.com/waterfall/motor>.
|
||||
|
||||
2. Check JIRA to ensure all the tickets in this version have been
|
||||
completed.
|
||||
|
||||
3. Add release notes to `doc/changelog.rst`. Generally just
|
||||
summarize/clarify the git log, but you might add some more long form
|
||||
notes for big changes.
|
||||
|
||||
4. Replace the `devN` version number w/ the new version number (see
|
||||
note above in [Versioning](#versioning)) in `motor/_version.py`.
|
||||
Commit the change and tag the release. Immediately bump the version
|
||||
number to `dev0` in a new commit:
|
||||
|
||||
$ # Bump to release version number
|
||||
$ git commit -a -m "BUMP <release version number>"
|
||||
$ git tag -a "<release version number>" -m "BUMP <release version number>"
|
||||
$ # Bump to dev version number
|
||||
$ git commit -a -m "BUMP <dev version number>"
|
||||
$ git push
|
||||
$ git push --tags
|
||||
|
||||
5. Bump the version number to `<next version>.dev0` in
|
||||
`motor/_version.py`, commit, then push.
|
||||
|
||||
6. Authorize the deployment for the tagged version on the release
|
||||
GitHub Action and wait for it to successfully publish to PyPI.
|
||||
|
||||
7. Make sure the new version appears on
|
||||
<https://motor.readthedocs.io/>. If the new version does not show up
|
||||
automatically, trigger a rebuild of "latest":
|
||||
<https://readthedocs.org/projects/motor/builds/>
|
||||
|
||||
8. Publish the release version in Jira and add a brief description
|
||||
about the reason for the release or the main feature.
|
||||
|
||||
9. Announce the release on:
|
||||
<https://www.mongodb.com/community/forums/c/announcements/driver-releases>
|
||||
|
||||
10. Create a GitHub Release for the tag using
|
||||
<https://github.com/mongodb/motor/releases/new>. The title should be
|
||||
"Motor X.Y.Z", and the description should contain a link to the
|
||||
release notes on the the community forum, e.g. "Release notes:
|
||||
mongodb.com/community/forums/t/motor-2-5-1-released/120313."
|
||||
81
RELEASE.rst
Normal file
81
RELEASE.rst
Normal file
@ -0,0 +1,81 @@
|
||||
==============
|
||||
Motor Releases
|
||||
==============
|
||||
|
||||
Versioning
|
||||
----------
|
||||
|
||||
Motor's version numbers follow `semantic versioning <http://semver.org/>`_:
|
||||
each version number is structured "major.minor.patch". Patch releases fix
|
||||
bugs, minor releases add features (and may fix bugs), and major releases
|
||||
include API changes that break backwards compatibility (and may add features
|
||||
and fix bugs).
|
||||
|
||||
In between releases we add .devN to the version number to denote the version
|
||||
under development. So if we just released 2.3.0, then the current dev
|
||||
version might be 2.3.1.dev0 or 2.4.0.dev0. When we make the next release we
|
||||
replace all instances of 2.x.x.devN in the docs with the new version number.
|
||||
|
||||
https://www.python.org/dev/peps/pep-0440/
|
||||
|
||||
Release Process
|
||||
---------------
|
||||
|
||||
Motor ships a `pure Python wheel <https://packaging.python.org/guides/distributing-packages-using-setuptools/#pure-python-wheels>`_
|
||||
and a `source distribution <https://packaging.python.org/guides/distributing-packages-using-setuptools/#source-distributions>`_.
|
||||
|
||||
#. Motor is tested on Evergreen. Ensure that the latest commit is passing CI as
|
||||
expected: https://evergreen.mongodb.com/waterfall/motor.
|
||||
|
||||
#. Check JIRA to ensure all the tickets in this version have been completed.
|
||||
|
||||
#. Add release notes to `doc/changelog.rst`. Generally just summarize/clarify
|
||||
the git log, but you might add some more long form notes for big changes.
|
||||
|
||||
#. Replace the `devN` version number w/ the new version number (see
|
||||
note above in `Versioning`_). Make sure version number is updated in
|
||||
``motor/_version.py``. Commit the change and tag the release.
|
||||
Immediately bump the version number to `dev0` in a new commit::
|
||||
|
||||
$ # Bump to release version number
|
||||
$ git commit -a -m "BUMP <release version number>"
|
||||
$ git tag -a "<release version number>" -m "BUMP <release version number>"
|
||||
$ # Bump to dev version number
|
||||
$ git commit -a -m "BUMP <dev version number>"
|
||||
$ git push
|
||||
$ git push --tags
|
||||
|
||||
#. Build the release packages by running the `release.sh`
|
||||
script on macOS::
|
||||
|
||||
$ git clone git@github.com:mongodb/motor.git
|
||||
$ cd motor
|
||||
$ git checkout "<release version number>"
|
||||
$ ./release.sh
|
||||
|
||||
This will create the following distributions::
|
||||
|
||||
$ ls dist
|
||||
motor-<version>.tar.gz
|
||||
motor-<version>-py3-none-any.whl
|
||||
|
||||
#. Upload all the release packages to PyPI with twine::
|
||||
|
||||
$ python3 -m twine upload dist/*
|
||||
|
||||
#. Make sure the new version appears on https://motor.readthedocs.io/. If the
|
||||
new version does not show up automatically, trigger a rebuild of "latest":
|
||||
https://readthedocs.org/projects/motor/builds/
|
||||
|
||||
#. Bump the version number to <next version>.dev0 in motor/_version.py,
|
||||
commit, push.
|
||||
|
||||
#. Publish the release version in Jira.
|
||||
|
||||
#. Announce the release on:
|
||||
https://www.mongodb.com/community/forums/c/announcements/driver-releases
|
||||
|
||||
#. Create a GitHub Release for the tag using https://github.com/mongodb/motor/releases/new.
|
||||
The title should be "Motor X.Y.Z", and the description should contain a
|
||||
link to the release notes on the the community forum, e.g.
|
||||
"Release notes: mongodb.com/community/forums/t/motor-2-5-1-released/120313."
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
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>`_.
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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:
|
||||
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,11 +1,5 @@
|
||||
: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:
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
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>`_.
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
: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
|
||||
|
||||
@ -3,80 +3,6 @@ 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
|
||||
-----------
|
||||
|
||||
- Type hint bug fixes and improvements. Added typings to classes in ``motor_tornado`` and
|
||||
``motor_asyncio``.
|
||||
|
||||
Motor 3.3.2
|
||||
-----------
|
||||
- Fix incorrect type hints for the following:
|
||||
:meth:`MotorCursor.to_list`,
|
||||
:meth:`MotorCollection.name`,
|
||||
:meth:`MotorDatabase.get_collection`,
|
||||
:meth:`MotorClientSession.with_transaction`
|
||||
- Fix a bug that caused application-supplied DriverInfo to be overwritten.
|
||||
|
||||
Motor 3.3.1
|
||||
-----------
|
||||
- Fix a bug in the type hint for :meth:`MotorCursor.to_list`.
|
||||
@ -141,11 +67,11 @@ New features:
|
||||
|
||||
The new Queryable Encryption changes that are in beta are:
|
||||
|
||||
- The ``encrypted_fields`` argument to the
|
||||
- The `encrypted_fields` argument to the
|
||||
:class:`~motor.motor_tornado.MotorCollection` constructor, and the
|
||||
:meth:`~motor.motor_tornado.MotorDatabase.create_collection`
|
||||
and :meth:`~motor.motor_tornado.MotorDatabase.drop_collection` methods.
|
||||
- The ``query_type`` and ``contention_factor`` arguments to
|
||||
- The `query_type` and `contention_factor` arguments to
|
||||
:meth:`motor.motor_asyncio.AsyncIOMotorClientEncryption.encrypt` and
|
||||
:meth:`motor.motor_tornado.MotorClientEncryption.encrypt`.
|
||||
|
||||
@ -161,7 +87,7 @@ Motor 3.0
|
||||
---------
|
||||
|
||||
Motor 3.0 adds support for PyMongo 4.0+. It inherits a number
|
||||
of improvements and breaking API changes from PyMongo 4.0+.
|
||||
of improvemnts and breaking API changes from PyMongo 4.0+.
|
||||
See :doc:`migrate-to-motor-3` for more information.
|
||||
|
||||
Breaking Changes
|
||||
@ -241,8 +167,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:`~pymongo.GridFSBucket` and
|
||||
:class:`~pymongo.GridFS`. See :ref:`removed-gridfs-checksum` for details.
|
||||
- Removed the `disable_md5` parameter for :class:`~gridfs.GridFSBucket` and
|
||||
:class:`~gridfs.GridFS`. See :ref:`removed-gridfs-checksum` for details.
|
||||
- PyMongoCrypt 1.2.0 or later is now required for client side field level
|
||||
encryption support.
|
||||
|
||||
@ -254,10 +180,10 @@ Notable improvements
|
||||
- Added the ``maxConnecting`` URI and
|
||||
:class:`~motor.motor_tornado.MotorClient` keyword argument.
|
||||
- :class:`~motor.motor_tornado.MotorClient` now accepts a URI and keyword
|
||||
argument ``srvMaxHosts`` that limits the number of mongos-like hosts a client
|
||||
argument `srvMaxHosts` that limits the number of mongos-like hosts a client
|
||||
will connect to. More specifically, when a mongodb+srv:// connection string
|
||||
resolves to more than ``srvMaxHosts`` number of hosts, the client will randomly
|
||||
choose a ``srvMaxHosts`` sized subset of hosts.
|
||||
resolves to more than `srvMaxHosts` number of hosts, the client will randomly
|
||||
choose a `srvMaxHosts` sized subset of hosts.
|
||||
- Added :attr:`motor.motor_tornado.MotorClient.options` for read-only access
|
||||
to a client's configuration options.
|
||||
- Added support for the ``comment`` parameter to all helpers. For example see
|
||||
@ -709,9 +635,9 @@ Highlights include:
|
||||
|
||||
- Complete support for MongoDB 3.4:
|
||||
|
||||
- Unicode aware string comparison using collations.
|
||||
- Unicode aware string comparison using collations. See :ref:`PyMongo's examples for collation <collation-on-operation>`.
|
||||
- :class:`MotorCursor` and :class:`MotorGridOutCursor` have a new attribute :meth:`~MotorCursor.collation`.
|
||||
- Support for the new :class:`~pymongo.decimal128.Decimal128` BSON type.
|
||||
- Support for the new :class:`~bson.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.
|
||||
@ -740,9 +666,9 @@ Highlights include:
|
||||
verification.
|
||||
- TLS compression is now explicitly disabled when possible.
|
||||
- The Server Name Indication (SNI) TLS extension is used when possible.
|
||||
- PyMongo's ``bson`` module provides finer control over JSON encoding/decoding
|
||||
with :class:`~pymongo.json_util.JSONOptions`.
|
||||
- Allow :class:`~pymongo.code.Code` objects to have a scope of ``None``,
|
||||
- PyMongo's `bson` module provides finer control over JSON encoding/decoding
|
||||
with :class:`~bson.json_util.JSONOptions`.
|
||||
- Allow :class:`~bson.code.Code` objects to have a scope of ``None``,
|
||||
signifying no scope. Also allow encoding Code objects with an empty scope
|
||||
(i.e. ``{}``).
|
||||
|
||||
@ -765,7 +691,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/4.0/changelog.html#changes-in-version-3-0
|
||||
.. _the PyMongo 3 changelog: https://pymongo.readthedocs.io/en/stable/changelog.html#changes-in-version-3-0
|
||||
|
||||
:class:`MotorReplicaSetClient` is removed
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -812,8 +738,8 @@ Unix domain socket paths must be quoted with :func:`urllib.parse.quote_plus` (or
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
path = "/tmp/mongodb-27017.sock"
|
||||
MotorClient("mongodb://%s" % urllib.parse.quote_plus(path))
|
||||
path = '/tmp/mongodb-27017.sock'
|
||||
MotorClient('mongodb://%s' % urllib.parse.quote_plus(path))
|
||||
|
||||
:class:`~motor.motor_tornado.MotorCollection` changes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -862,9 +788,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:`~pymongo.codec_options.CodecOptions` instead)
|
||||
:class:`~bson.codec_options.CodecOptions` instead)
|
||||
- compile_re (BSON regular expressions are always decoded to
|
||||
:class:`~pymongo.regex.Regex`)
|
||||
:class:`~bson.regex.Regex`)
|
||||
|
||||
The following find/find_one options are deprecated:
|
||||
|
||||
@ -1089,20 +1015,20 @@ Motor 0.6
|
||||
This is a bugfix release. Fixing these bugs has introduced tiny API changes that
|
||||
may affect some programs.
|
||||
|
||||
``motor_asyncio`` and ``motor_tornado`` submodules
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
`motor_asyncio` and `motor_tornado` submodules
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These modules have been moved from:
|
||||
|
||||
- ``motor_asyncio.py``
|
||||
- ``motor_tornado.py``
|
||||
- `motor_asyncio.py`
|
||||
- `motor_tornado.py`
|
||||
|
||||
To:
|
||||
|
||||
- ``motor_asyncio/__init__.py``
|
||||
- ``motor_tornado/__init__.py``
|
||||
- `motor_asyncio/__init__.py`
|
||||
- `motor_tornado/__init__.py`
|
||||
|
||||
Motor had to make this change in order to omit the ``motor_asyncio`` submodule
|
||||
Motor had to make this change in order to omit the `motor_asyncio` submodule
|
||||
entirely and avoid a spurious :exc:`SyntaxError` being printed when installing in
|
||||
Python 2. The change should be invisible to application code.
|
||||
|
||||
@ -1156,18 +1082,18 @@ explanation.)
|
||||
|
||||
.. _commit message dc19418c: https://github.com/mongodb/motor/commit/dc19418c
|
||||
|
||||
``async`` and ``await``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
`async` and `await`
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Motor now supports Python 3.5 native coroutines, written with the ``async`` and
|
||||
``await`` syntax::
|
||||
Motor now supports Python 3.5 native coroutines, written with the `async` and
|
||||
`await` syntax::
|
||||
|
||||
async def f():
|
||||
await collection.insert({'_id': 1})
|
||||
|
||||
Cursors from :meth:`~MotorCollection.find`, :meth:`~MotorCollection.aggregate`, or
|
||||
:meth:`~MotorGridFS.find` can be iterated elegantly and very efficiently in native
|
||||
coroutines with ``async for``::
|
||||
coroutines with `async for`::
|
||||
|
||||
async def f():
|
||||
async for doc in collection.find():
|
||||
@ -1179,7 +1105,7 @@ coroutines with ``async for``::
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:meth:`MotorCollection.aggregate` now returns a cursor by default, and the cursor
|
||||
is returned immediately without a ``yield``. The old syntax is no longer
|
||||
is returned immediately without a `yield`. The old syntax is no longer
|
||||
supported::
|
||||
|
||||
# Motor 0.4 and older, no longer supported.
|
||||
@ -1217,7 +1143,7 @@ Deprecations
|
||||
|
||||
Motor 0.5 deprecates a large number of APIs that will be removed in version 1.0:
|
||||
|
||||
``MotorClient``:
|
||||
`MotorClient`:
|
||||
- `~MotorClient.host`
|
||||
- `~MotorClient.port`
|
||||
- `~MotorClient.document_class`
|
||||
@ -1228,7 +1154,7 @@ Motor 0.5 deprecates a large number of APIs that will be removed in version 1.0:
|
||||
- `~MotorClient.disconnect`
|
||||
- `~MotorClient.alive`
|
||||
|
||||
``MotorReplicaSetClient``:
|
||||
`MotorReplicaSetClient`:
|
||||
- `~MotorReplicaSetClient.document_class`
|
||||
- `~MotorReplicaSetClient.tz_aware`
|
||||
- `~MotorReplicaSetClient.secondary_acceptable_latency_ms`
|
||||
@ -1236,12 +1162,12 @@ Motor 0.5 deprecates a large number of APIs that will be removed in version 1.0:
|
||||
- `~MotorReplicaSetClient.uuid_subtype`
|
||||
- `~MotorReplicaSetClient.alive`
|
||||
|
||||
``MotorDatabase``:
|
||||
`MotorDatabase`:
|
||||
- `~MotorDatabase.secondary_acceptable_latency_ms`
|
||||
- `~MotorDatabase.tag_sets`
|
||||
- `~MotorDatabase.uuid_subtype`
|
||||
|
||||
``MotorCollection``:
|
||||
`MotorCollection`:
|
||||
- `~MotorCollection.secondary_acceptable_latency_ms`
|
||||
- `~MotorCollection.tag_sets`
|
||||
- `~MotorCollection.uuid_subtype`
|
||||
@ -1271,7 +1197,7 @@ SSL hostname validation error
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When you use Motor with Tornado and SSL hostname validation fails, Motor used
|
||||
to raise a :exc:`~pymongo.errors.ConnectionFailure` with a useful message like "hostname 'X'
|
||||
to raise a :exc:`~pymongo.errors.ConnectionFailure` with a useful messsage like "hostname 'X'
|
||||
doesn't match 'Y'". The message is now empty and Tornado logs a warning
|
||||
instead.
|
||||
|
||||
@ -1301,7 +1227,8 @@ 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.
|
||||
and `features
|
||||
<https://pymongo.readthedocs.io/en/stable/changelog.html#changes-in-version-2-8>`_.
|
||||
|
||||
Fixes `a connection-pool timeout when waitQueueMultipleMS is set
|
||||
<https://jira.mongodb.org/browse/MOTOR-62>`_ and `two bugs in replica set
|
||||
@ -1315,6 +1242,8 @@ another, if the remote host requires authentication.
|
||||
For this, use PyMongo's ``copy_database`` method, or, since PyMongo's
|
||||
``copy_database`` will be removed in a future release too, use the mongo shell.
|
||||
|
||||
.. seealso:: `The "copydb" command <https://mongodb.com/docs/manual/reference/command/copydb/>`_.
|
||||
|
||||
Motor 0.3.3
|
||||
-----------
|
||||
|
||||
|
||||
38
doc/conf.py
38
doc/conf.py
@ -1,13 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Motor documentation build configuration file
|
||||
#
|
||||
# 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 -----------------------------------------------------
|
||||
@ -73,14 +76,7 @@ pygments_style = "sphinx"
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# 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
|
||||
linkcheck_ignore = [r"http://localhost:\d+"]
|
||||
|
||||
# -- Options for extensions ----------------------------------------------------
|
||||
autoclass_content = "init"
|
||||
@ -130,20 +126,15 @@ from motor import MotorClient
|
||||
|
||||
html_copy_source = False
|
||||
|
||||
try:
|
||||
import furo # noqa: F401
|
||||
# Theme gratefully vendored from CPython source.
|
||||
html_theme = "pydoctheme"
|
||||
html_theme_path = ["."]
|
||||
html_theme_options = {"collapsiblesidebar": True}
|
||||
html_static_path = ["static"]
|
||||
|
||||
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"],
|
||||
}
|
||||
html_sidebars = {
|
||||
"index": ["globaltoc.html", "searchbox.html"],
|
||||
}
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
@ -224,10 +215,11 @@ 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),
|
||||
|
||||
@ -1,13 +1,6 @@
|
||||
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
|
||||
''''''''''''''''''''
|
||||
|
||||
@ -35,14 +28,14 @@ and executing the following command::
|
||||
|
||||
You should see "TLS 1.X" where X is >= 1.
|
||||
|
||||
You can read more about TLS versions and their security implications in this `cheat sheet`_.
|
||||
You can read more about TLS versions and their security implications here:
|
||||
|
||||
`<https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html#only-support-strong-protocols>`_
|
||||
|
||||
.. _python.org: https://www.python.org/downloads/
|
||||
.. _homebrew: https://brew.sh/
|
||||
.. _macports: https://www.macports.org/
|
||||
.. _requests: https://pypi.python.org/pypi/requests
|
||||
.. _cheat sheet: https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html#only-support-strong-protocols
|
||||
|
||||
Thread Pool Size
|
||||
''''''''''''''''
|
||||
@ -53,17 +46,3 @@ system; to override the default set the environment variable ``MOTOR_MAX_WORKERS
|
||||
|
||||
Some additional threads are used for monitoring servers and background tasks, so the total
|
||||
count of threads in your process will be greater.
|
||||
|
||||
Network Compression
|
||||
'''''''''''''''''''
|
||||
|
||||
Like PyMongo, Motor supports network compression where network traffic between
|
||||
the client and MongoDB server are compressed.
|
||||
Keyword arguments to the Motor clients match those in MongoClient, documented
|
||||
`here <https://pymongo.readthedocs.io/en/stable/examples/network_compression.html>`_.
|
||||
By default no compression is used. If you wish to use wire compression,
|
||||
you will have to install one of the optional dependencies.
|
||||
Snappy requires `python-snappy <https://pypi.org/project/python-snappy>`_
|
||||
and zstandard requires `zstandard <https://pypi.org/project/zstandard>`_::
|
||||
|
||||
$ python3 -m pip install "motor[snappy, zstd]"
|
||||
|
||||
@ -17,5 +17,3 @@ The following is a list of people who have contributed to
|
||||
- Tushar Singh
|
||||
- Steven Silvester
|
||||
- Julius Park
|
||||
- Doeke Buursma
|
||||
- Scott Luu
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
"""Gratefully adapted from aiohttp, provides coroutine support to autodoc."""
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.domains.python import PyFunction, PyMethod
|
||||
|
||||
|
||||
class PyCoroutineMixin:
|
||||
class PyCoroutineMixin(object):
|
||||
def handle_signature(self, sig, signode):
|
||||
ret = super().handle_signature(sig, signode)
|
||||
signode.insert(0, addnodes.desc_annotation("coroutine ", "coroutine "))
|
||||
|
||||
@ -2,13 +2,6 @@
|
||||
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
|
||||
@ -112,5 +105,5 @@ synchronous and Motor is async; how can Motor pass PyMongo's tests?
|
||||
Synchro is a hacky little module that re-synchronizes all Motor methods using
|
||||
the Tornado IOLoop's ``run_sync`` method. ``synchrotest.py`` overrides the Python
|
||||
interpreter's import machinery to allow Synchro to masquerade as PyMongo, and
|
||||
runs PyMongo's test suite against it. Use ``tox -e synchro`` to check out
|
||||
runs PyMongo's test suite against it. Use ``tox -e synchro37`` to check out
|
||||
PyMongo's test suite and run it with Synchro.
|
||||
|
||||
@ -4,12 +4,6 @@
|
||||
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.
|
||||
|
||||
@ -49,8 +43,8 @@ GridFS
|
||||
|
||||
- File-like
|
||||
|
||||
PyMongo's :class:`~pymongo.grid_file.GridIn` and
|
||||
:class:`~pymongo.grid_file.GridOut` strive to act like Python's built-in
|
||||
PyMongo's :class:`~gridfs.grid_file.GridIn` and
|
||||
:class:`~gridfs.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
|
||||
@ -59,7 +53,7 @@ GridFS
|
||||
- Setting properties
|
||||
|
||||
In PyMongo, you can set arbitrary attributes on
|
||||
a :class:`~pymongo.grid_file.GridIn` and they're stored as metadata on
|
||||
a :class:`~gridfs.grid_file.GridIn` and they're stored as metadata on
|
||||
the server, even after the ``GridIn`` is closed::
|
||||
|
||||
fs = gridfs.GridFSBucket(db)
|
||||
@ -96,9 +90,9 @@ system_js
|
||||
|
||||
PyMongo supports Javascript procedures stored in MongoDB with syntax like:
|
||||
|
||||
.. code-block:: pycon
|
||||
.. code-block:: python
|
||||
|
||||
>>> db.system_js.my_func = "function(x) { return x * x; }"
|
||||
>>> db.system_js.my_func = 'function(x) { return x * x; }'
|
||||
>>> db.system_js.my_func(2)
|
||||
4.0
|
||||
|
||||
@ -139,10 +133,17 @@ There are two ways to create a capped collection using PyMongo:
|
||||
.. code-block:: python
|
||||
|
||||
# Typical:
|
||||
db.create_collection("collection1", capped=True, size=1000)
|
||||
db.create_collection(
|
||||
'collection1',
|
||||
capped=True,
|
||||
size=1000)
|
||||
|
||||
# Unusual:
|
||||
collection = Collection(db, "collection2", capped=True, size=1000)
|
||||
collection = Collection(
|
||||
db,
|
||||
'collection2',
|
||||
capped=True,
|
||||
size=1000)
|
||||
|
||||
Motor can't do I/O in a constructor, so the unusual style is prohibited and
|
||||
only the typical style is allowed:
|
||||
@ -150,4 +151,7 @@ only the typical style is allowed:
|
||||
.. code-block:: python
|
||||
|
||||
async def f():
|
||||
await db.create_collection("collection1", capped=True, size=1000)
|
||||
await db.create_collection(
|
||||
'collection1',
|
||||
capped=True,
|
||||
size=1000)
|
||||
|
||||
5
doc/docs-requirements.txt
Normal file
5
doc/docs-requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
tornado
|
||||
aiohttp
|
||||
Sphinx~=4.1
|
||||
sphinx_rtd_theme~=0.5
|
||||
readthedocs-sphinx-search~=0.1
|
||||
@ -31,14 +31,13 @@ async def page_handler(request):
|
||||
document = await db.pages.find_one(page_name)
|
||||
|
||||
if not document:
|
||||
return web.HTTPNotFound(text=f"No page named {page_name!r}")
|
||||
return web.HTTPNotFound(text="No page named {!r}".format(page_name))
|
||||
|
||||
return web.Response(body=document["body"].encode(), content_type="text/html")
|
||||
|
||||
|
||||
# -- handler-end --
|
||||
|
||||
|
||||
# -- main-start --
|
||||
async def init_connection():
|
||||
db = await setup_db()
|
||||
|
||||
@ -1,13 +1,6 @@
|
||||
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`.
|
||||
|
||||
|
||||
@ -3,13 +3,6 @@
|
||||
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.
|
||||
|
||||
|
||||
@ -74,7 +74,7 @@ async def main():
|
||||
await create_json_schema_file(kms_providers, key_vault_namespace, key_vault_client)
|
||||
|
||||
# Load the JSON Schema and construct the local schema_map option.
|
||||
with open("jsonSchema.json") as file:
|
||||
with open("jsonSchema.json", "r") as file:
|
||||
json_schema_string = file.read()
|
||||
json_schema = json_util.loads(json_schema_string)
|
||||
schema_map = {encrypted_namespace: json_schema}
|
||||
@ -91,10 +91,10 @@ async def main():
|
||||
|
||||
await coll.insert_one({"encryptedField": "123456789"})
|
||||
decrypted_doc = await coll.find_one()
|
||||
print(f"Decrypted document: {decrypted_doc}")
|
||||
print("Decrypted document: %s" % (decrypted_doc,))
|
||||
unencrypted_coll = AsyncIOMotorClient()[db_name][coll_name]
|
||||
encrypted_doc = await unencrypted_coll.find_one()
|
||||
print(f"Encrypted document: {encrypted_doc}")
|
||||
print("Encrypted document: %s" % (encrypted_doc,))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -5,13 +5,6 @@
|
||||
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()
|
||||
@ -38,10 +31,9 @@ bulk insert operations.
|
||||
.. doctest::
|
||||
|
||||
>>> async def f():
|
||||
... await db.test.insert_many(({"i": i} for i in range(10000)))
|
||||
... await db.test.insert_many(({'i': i} for i in range(10000)))
|
||||
... count = await db.test.count_documents({})
|
||||
... print("Final count: %d" % count)
|
||||
...
|
||||
>>>
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
Final count: 10000
|
||||
@ -69,17 +61,14 @@ of operations performed.
|
||||
>>> from pprint import pprint
|
||||
>>> from pymongo import InsertOne, DeleteMany, ReplaceOne, UpdateOne
|
||||
>>> async def f():
|
||||
... result = await db.test.bulk_write(
|
||||
... [
|
||||
... DeleteMany({}), # Remove all documents from the previous example.
|
||||
... InsertOne({"_id": 1}),
|
||||
... InsertOne({"_id": 2}),
|
||||
... InsertOne({"_id": 3}),
|
||||
... UpdateOne({"_id": 1}, {"$set": {"foo": "bar"}}),
|
||||
... UpdateOne({"_id": 4}, {"$inc": {"j": 1}}, upsert=True),
|
||||
... ReplaceOne({"j": 1}, {"j": 2}),
|
||||
... ]
|
||||
... )
|
||||
... result = await db.test.bulk_write([
|
||||
... DeleteMany({}), # Remove all documents from the previous example.
|
||||
... InsertOne({'_id': 1}),
|
||||
... InsertOne({'_id': 2}),
|
||||
... InsertOne({'_id': 3}),
|
||||
... UpdateOne({'_id': 1}, {'$set': {'foo': 'bar'}}),
|
||||
... UpdateOne({'_id': 4}, {'$inc': {'j': 1}}, upsert=True),
|
||||
... ReplaceOne({'j': 1}, {'j': 2})])
|
||||
... pprint(result.bulk_api_result)
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
@ -106,10 +95,9 @@ the failure.
|
||||
>>> from pymongo.errors import BulkWriteError
|
||||
>>> async def f():
|
||||
... requests = [
|
||||
... ReplaceOne({"j": 2}, {"i": 5}),
|
||||
... InsertOne({"_id": 4}), # Violates the unique key constraint on _id.
|
||||
... DeleteOne({"i": 5}),
|
||||
... ]
|
||||
... ReplaceOne({'j': 2}, {'i': 5}),
|
||||
... InsertOne({'_id': 4}), # Violates the unique key constraint on _id.
|
||||
... DeleteOne({'i': 5})]
|
||||
... try:
|
||||
... await db.test.bulk_write(requests)
|
||||
... except BulkWriteError as bwe:
|
||||
@ -148,11 +136,10 @@ and fourth operations succeed.
|
||||
|
||||
>>> async def f():
|
||||
... requests = [
|
||||
... InsertOne({"_id": 1}),
|
||||
... DeleteOne({"_id": 2}),
|
||||
... InsertOne({"_id": 3}),
|
||||
... ReplaceOne({"_id": 4}, {"i": 1}),
|
||||
... ]
|
||||
... InsertOne({'_id': 1}),
|
||||
... DeleteOne({'_id': 2}),
|
||||
... InsertOne({'_id': 3}),
|
||||
... ReplaceOne({'_id': 4}, {'i': 1})]
|
||||
... try:
|
||||
... await db.test.bulk_write(requests, ordered=False)
|
||||
... except BulkWriteError as bwe:
|
||||
@ -194,9 +181,10 @@ after all operations are attempted, regardless of execution order.
|
||||
|
||||
>>> from pymongo import WriteConcern
|
||||
>>> async def f():
|
||||
... coll = db.get_collection("test", write_concern=WriteConcern(w=4, wtimeout=1))
|
||||
... coll = db.get_collection(
|
||||
... 'test', write_concern=WriteConcern(w=4, wtimeout=1))
|
||||
... try:
|
||||
... await coll.bulk_write([InsertOne({"a": i}) for i in range(4)])
|
||||
... await coll.bulk_write([InsertOne({'a': i}) for i in range(4)])
|
||||
... except BulkWriteError as bwe:
|
||||
... pprint(bwe.details)
|
||||
...
|
||||
|
||||
@ -3,13 +3,6 @@
|
||||
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
|
||||
|
||||
@ -64,9 +64,9 @@ async def main():
|
||||
await coll.insert_one({"encryptedField": encrypted_field})
|
||||
# Automatically decrypts any encrypted fields.
|
||||
doc = await coll.find_one()
|
||||
print(f"Decrypted document: {doc}")
|
||||
print("Decrypted document: %s" % (doc,))
|
||||
unencrypted_coll = AsyncIOMotorClient().test.coll
|
||||
print(f"Encrypted document: {await unencrypted_coll.find_one()}")
|
||||
print("Encrypted document: %s" % (await unencrypted_coll.find_one(),))
|
||||
|
||||
# Cleanup resources.
|
||||
await client_encryption.close()
|
||||
|
||||
@ -54,11 +54,11 @@ async def main():
|
||||
)
|
||||
await coll.insert_one({"encryptedField": encrypted_field})
|
||||
doc = await coll.find_one()
|
||||
print(f"Encrypted document: {doc}")
|
||||
print("Encrypted document: %s" % (doc,))
|
||||
|
||||
# Explicitly decrypt the field:
|
||||
doc["encryptedField"] = await client_encryption.decrypt(doc["encryptedField"])
|
||||
print(f"Decrypted document: {doc}")
|
||||
print("Decrypted document: %s" % (doc,))
|
||||
|
||||
# Cleanup resources.
|
||||
await client_encryption.close()
|
||||
|
||||
@ -1,13 +1,6 @@
|
||||
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::
|
||||
@ -19,7 +12,5 @@ Motor Examples
|
||||
authentication
|
||||
aiohttp_gridfs_example
|
||||
encryption
|
||||
timeouts
|
||||
type_hints
|
||||
|
||||
See also :ref:`example-web-application-aiohttp`.
|
||||
|
||||
@ -3,13 +3,6 @@
|
||||
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.
|
||||
|
||||
|
||||
@ -27,25 +27,25 @@ logging.basicConfig(stream=sys.stdout, level=logging.INFO)
|
||||
class CommandLogger(monitoring.CommandListener):
|
||||
def started(self, event):
|
||||
logging.info(
|
||||
f"Command {event.command_name} with request id "
|
||||
f"{event.request_id} started on server "
|
||||
f"{event.connection_id}"
|
||||
"Command {0.command_name} with request id "
|
||||
"{0.request_id} started on server "
|
||||
"{0.connection_id}".format(event)
|
||||
)
|
||||
|
||||
def succeeded(self, event):
|
||||
logging.info(
|
||||
f"Command {event.command_name} with request id "
|
||||
f"{event.request_id} on server {event.connection_id} "
|
||||
f"succeeded in {event.duration_micros} "
|
||||
"microseconds"
|
||||
"Command {0.command_name} with request id "
|
||||
"{0.request_id} on server {0.connection_id} "
|
||||
"succeeded in {0.duration_micros} "
|
||||
"microseconds".format(event)
|
||||
)
|
||||
|
||||
def failed(self, event):
|
||||
logging.info(
|
||||
f"Command {event.command_name} with request id "
|
||||
f"{event.request_id} on server {event.connection_id} "
|
||||
f"failed in {event.duration_micros} "
|
||||
"microseconds"
|
||||
"Command {0.command_name} with request id "
|
||||
"{0.request_id} on server {0.connection_id} "
|
||||
"failed in {0.duration_micros} "
|
||||
"microseconds".format(event)
|
||||
)
|
||||
|
||||
|
||||
@ -77,20 +77,22 @@ ioloop.IOLoop.current().run_sync(do_insert)
|
||||
# server logger start
|
||||
class ServerLogger(monitoring.ServerListener):
|
||||
def opened(self, event):
|
||||
logging.info(f"Server {event.server_address} added to topology {event.topology_id}")
|
||||
logging.info("Server {0.server_address} added to topology {0.topology_id}".format(event))
|
||||
|
||||
def description_changed(self, event):
|
||||
previous_server_type = event.previous_description.server_type
|
||||
new_server_type = event.new_description.server_type
|
||||
if new_server_type != previous_server_type:
|
||||
logging.info(
|
||||
f"Server {event.server_address} changed type from "
|
||||
f"{event.previous_description.server_type_name} to "
|
||||
f"{event.new_description.server_type_name}"
|
||||
"Server {0.server_address} changed type from "
|
||||
"{0.previous_description.server_type_name} to "
|
||||
"{0.new_description.server_type_name}".format(event)
|
||||
)
|
||||
|
||||
def closed(self, event):
|
||||
logging.warning(f"Server {event.server_address} removed from topology {event.topology_id}")
|
||||
logging.warning(
|
||||
"Server {0.server_address} removed from topology {0.topology_id}".format(event)
|
||||
)
|
||||
|
||||
|
||||
monitoring.register(ServerLogger())
|
||||
@ -100,21 +102,21 @@ monitoring.register(ServerLogger())
|
||||
# topology logger start
|
||||
class TopologyLogger(monitoring.TopologyListener):
|
||||
def opened(self, event):
|
||||
logging.info(f"Topology with id {event.topology_id} opened")
|
||||
logging.info("Topology with id {0.topology_id} opened".format(event))
|
||||
|
||||
def description_changed(self, event):
|
||||
logging.info(f"Topology description updated for topology id {event.topology_id}")
|
||||
logging.info("Topology description updated for topology id {0.topology_id}".format(event))
|
||||
previous_topology_type = event.previous_description.topology_type
|
||||
new_topology_type = event.new_description.topology_type
|
||||
if new_topology_type != previous_topology_type:
|
||||
logging.info(
|
||||
f"Topology {event.topology_id} changed type from "
|
||||
f"{event.previous_description.topology_type_name} to "
|
||||
f"{event.new_description.topology_type_name}"
|
||||
"Topology {0.topology_id} changed type from "
|
||||
"{0.previous_description.topology_type_name} to "
|
||||
"{0.new_description.topology_type_name}".format(event)
|
||||
)
|
||||
|
||||
def closed(self, event):
|
||||
logging.info(f"Topology with id {event.topology_id} closed")
|
||||
logging.info("Topology with id {0.topology_id} closed".format(event))
|
||||
|
||||
|
||||
monitoring.register(TopologyLogger())
|
||||
@ -124,18 +126,18 @@ monitoring.register(TopologyLogger())
|
||||
# heartbeat logger start
|
||||
class HeartbeatLogger(monitoring.ServerHeartbeatListener):
|
||||
def started(self, event):
|
||||
logging.info(f"Heartbeat sent to server {event.connection_id}")
|
||||
logging.info("Heartbeat sent to server {0.connection_id}".format(event))
|
||||
|
||||
def succeeded(self, event):
|
||||
logging.info(
|
||||
f"Heartbeat to server {event.connection_id} "
|
||||
"Heartbeat to server {0.connection_id} "
|
||||
"succeeded with reply "
|
||||
f"{event.reply.document}"
|
||||
"{0.reply.document}".format(event)
|
||||
)
|
||||
|
||||
def failed(self, event):
|
||||
logging.warning(
|
||||
f"Heartbeat to server {event.connection_id} failed with error {event.reply}"
|
||||
"Heartbeat to server {0.connection_id} failed with error {0.reply}".format(event)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -86,14 +86,14 @@ async def main():
|
||||
|
||||
await coll.insert_one({"encryptedField": "123456789"})
|
||||
decrypted_doc = await coll.find_one()
|
||||
print(f"Decrypted document: {decrypted_doc}")
|
||||
print("Decrypted document: %s" % (decrypted_doc,))
|
||||
unencrypted_coll = AsyncIOMotorClient()[db_name][coll_name]
|
||||
encrypted_doc = await unencrypted_coll.find_one()
|
||||
print(f"Encrypted document: {encrypted_doc}")
|
||||
print("Encrypted document: %s" % (encrypted_doc,))
|
||||
try:
|
||||
await unencrypted_coll.insert_one({"encryptedField": "123456789"})
|
||||
except OperationFailure as exc:
|
||||
print(f"Unencrypted insert failed: {exc.details}")
|
||||
print("Unencrypted insert failed: %s" % (exc.details,))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -3,13 +3,6 @@
|
||||
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
|
||||
@ -23,12 +16,11 @@ of a replica set member:
|
||||
from asyncio import sleep
|
||||
from pymongo.cursor import CursorType
|
||||
|
||||
|
||||
async def tail_oplog_example():
|
||||
oplog = client.local.oplog.rs
|
||||
first = await oplog.find().sort("$natural", pymongo.ASCENDING).limit(-1).next()
|
||||
first = await oplog.find().sort('$natural', pymongo.ASCENDING).limit(-1).next()
|
||||
print(first)
|
||||
ts = first["ts"]
|
||||
ts = first['ts']
|
||||
|
||||
while True:
|
||||
# For a regular capped collection CursorType.TAILABLE_AWAIT is the
|
||||
@ -38,14 +30,12 @@ of a replica set member:
|
||||
# can only be used when querying the oplog. Starting in MongoDB 4.4
|
||||
# this option is ignored by the server as queries against the oplog
|
||||
# are optimized automatically by the MongoDB query engine.
|
||||
cursor = oplog.find(
|
||||
{"ts": {"$gt": ts}},
|
||||
cursor_type=CursorType.TAILABLE_AWAIT,
|
||||
oplog_replay=True,
|
||||
)
|
||||
cursor = oplog.find({'ts': {'$gt': ts}},
|
||||
cursor_type=CursorType.TAILABLE_AWAIT,
|
||||
oplog_replay=True)
|
||||
while cursor.alive:
|
||||
async for doc in cursor:
|
||||
ts = doc["ts"]
|
||||
ts = doc['ts']
|
||||
print(doc)
|
||||
# We end up here if the find() returned no documents or if the
|
||||
# tailable cursor timed out (no new documents were added to the
|
||||
|
||||
@ -1,87 +0,0 @@
|
||||
|
||||
.. _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.
|
||||
@ -73,8 +73,11 @@ class ChangesHandler(tornado.websocket.WebSocketHandler):
|
||||
data = data.encode("utf-8")
|
||||
html_id = urlsafe_b64encode(data).decode().rstrip("=")
|
||||
change.pop("_id")
|
||||
change_pre = tornado.escape.xhtml_escape(pformat(change))
|
||||
change["html"] = f'<div id="change-{html_id}"><pre>{change_pre}</pre></div>'
|
||||
change["html"] = '<div id="change-%s"><pre>%s</pre></div>' % (
|
||||
html_id,
|
||||
tornado.escape.xhtml_escape(pformat(change)),
|
||||
)
|
||||
|
||||
change["html_id"] = html_id
|
||||
ChangesHandler.send_change(change)
|
||||
ChangesHandler.update_cache(change)
|
||||
@ -94,7 +97,7 @@ async def watch(collection):
|
||||
def main():
|
||||
tornado.options.parse_command_line()
|
||||
if "." not in options.ns:
|
||||
sys.stderr.write(f'Invalid ns "{options.ns}", must contain a "."')
|
||||
sys.stderr.write('Invalid ns "%s", must contain a "."' % (options.ns,))
|
||||
sys.exit(1)
|
||||
|
||||
db_name, collection_name = options.ns.split(".", 1)
|
||||
|
||||
@ -3,13 +3,6 @@
|
||||
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
|
||||
@ -25,7 +18,7 @@ http://localhost:8888
|
||||
Open a ``mongo`` shell in the terminal and perform some operations on the
|
||||
"test" collection in the "test" database:
|
||||
|
||||
.. code-block:: text
|
||||
.. code-block:: none
|
||||
|
||||
> use test
|
||||
switched to db test
|
||||
@ -36,7 +29,7 @@ Open a ``mongo`` shell in the terminal and perform some operations on the
|
||||
The application receives each change notification and displays it as JSON on
|
||||
the web page:
|
||||
|
||||
.. code-block:: text
|
||||
.. code-block:: none
|
||||
|
||||
Changes
|
||||
|
||||
|
||||
@ -1,393 +0,0 @@
|
||||
|
||||
.. _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
|
||||
@ -4,13 +4,6 @@ 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
|
||||
@ -27,8 +20,8 @@ Featureful
|
||||
Motor wraps almost all of PyMongo's API and makes it non-blocking. For the few
|
||||
PyMongo features not implemented in Motor, see :doc:`differences`.
|
||||
|
||||
Convenient With ``tornado.gen``
|
||||
===============================
|
||||
Convenient With `tornado.gen`
|
||||
=============================
|
||||
The :mod:`tornado.gen` module lets you use coroutines to simplify asynchronous
|
||||
code. Motor methods return Futures that are convenient to use with coroutines.
|
||||
|
||||
|
||||
@ -4,12 +4,6 @@ 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
|
||||
-----
|
||||
|
||||
@ -60,7 +54,7 @@ project.
|
||||
|
||||
Feature Requests / Feedback
|
||||
---------------------------
|
||||
Use our `feedback engine <https://feedback.mongodb.com/?category=7548141816650747033>`_
|
||||
Use our `feedback engine <https://feedback.mongodb.com/forums/924286-drivers>`_
|
||||
to send us feature requests and general feedback about PyMongo.
|
||||
|
||||
Contributing
|
||||
|
||||
@ -1,13 +1,6 @@
|
||||
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
|
||||
@ -26,8 +19,8 @@ Motor works in all the environments officially supported by Tornado or by
|
||||
asyncio. It requires:
|
||||
|
||||
* Unix (including macOS) or Windows.
|
||||
* PyMongo_ >=4.9,<5
|
||||
* Python 3.10+
|
||||
* PyMongo_ >=4.1,<5
|
||||
* Python 3.7+
|
||||
|
||||
Optional dependencies:
|
||||
|
||||
@ -41,7 +34,7 @@ dependency can be installed automatically along with Motor::
|
||||
|
||||
similarly,
|
||||
|
||||
`MONGODB-AWS <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/security/authentication/aws-iam/#std-label-pymongo-mongodb-aws>`_
|
||||
`MONGODB-AWS <https://pymongo.readthedocs.io/en/stable/examples/authentication.html#mongodb-aws>`_
|
||||
authentication requires ``aws`` extra dependency::
|
||||
|
||||
$ pip install "motor[aws]"
|
||||
|
||||
@ -1,13 +1,6 @@
|
||||
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
|
||||
@ -125,7 +118,6 @@ used callbacks:
|
||||
else:
|
||||
print(result)
|
||||
|
||||
|
||||
collection.find_one({}, callback=callback)
|
||||
|
||||
Callbacks have been largely superseded by a Futures API intended for use with
|
||||
@ -142,7 +134,6 @@ a parameter:
|
||||
except Exception as exc:
|
||||
print(exc)
|
||||
|
||||
|
||||
future = collection.find_one({})
|
||||
future.add_done_callback(callback)
|
||||
|
||||
@ -190,7 +181,7 @@ Or:
|
||||
.. code-block:: python3
|
||||
|
||||
with client.start_session() as session:
|
||||
doc = client.db.collection.find_one({}, session=session)
|
||||
doc = client.db.collection.find_one({}, session=session)
|
||||
|
||||
To support multi-document transactions, in Motor 2.0
|
||||
:meth:`MotorClient.start_session` is a coroutine, not a regular method. It must
|
||||
@ -212,4 +203,4 @@ Or:
|
||||
.. code-block:: python3
|
||||
|
||||
async with client.start_session() as session:
|
||||
doc = await client.db.collection.find_one({}, session=session)
|
||||
doc = await client.db.collection.find_one({}, session=session)
|
||||
|
||||
@ -1,13 +1,6 @@
|
||||
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
|
||||
@ -186,7 +179,7 @@ can be changed to this::
|
||||
``tz_aware`` defaults to ``False``
|
||||
..................................
|
||||
|
||||
``tz_aware``, an argument for :class:`~pymongo.json_util.JSONOptions`,
|
||||
``tz_aware``, an argument for :class:`~bson.json_util.JSONOptions`,
|
||||
now defaults to ``False`` instead of ``True``. ``json_util.loads`` now
|
||||
decodes datetime as naive by default.
|
||||
|
||||
@ -416,11 +409,10 @@ 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:`~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/
|
||||
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>`.
|
||||
|
||||
GridFS changes
|
||||
--------------
|
||||
@ -430,7 +422,7 @@ GridFS changes
|
||||
disable_md5 parameter is removed
|
||||
................................
|
||||
|
||||
Removed the ``disable_md5`` option for :class:`~motor.motor_tornado.gridfs.MotorGridFSBucket` and
|
||||
Removed the `disable_md5` option for :class:`~motor.motor_tornado.gridfs.MotorGridFSBucket` and
|
||||
:class:`~motor.motor_tornado.gridfs.MotorGridFS`. GridFS no longer generates checksums.
|
||||
Applications that desire a file digest should implement it outside GridFS
|
||||
and store it with other file metadata. For example::
|
||||
@ -454,12 +446,13 @@ Removed features with no migration path
|
||||
Encoding a UUID raises an error by default
|
||||
..........................................
|
||||
|
||||
The default uuid_representation for :class:`~pymongo.codec_options.CodecOptions`,
|
||||
:class:`~pymongo.json_util.JSONOptions`, and
|
||||
The default uuid_representation for :class:`~bson.codec_options.CodecOptions`,
|
||||
:class:`~bson.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
|
||||
|
||||
@ -47,6 +47,7 @@ def depart_mongoref_node(self, node):
|
||||
|
||||
|
||||
class MongodocDirective(rst.Directive):
|
||||
|
||||
has_content = True
|
||||
required_arguments = 0
|
||||
optional_arguments = 0
|
||||
|
||||
@ -32,7 +32,7 @@ def has_node_of_type(root, klass):
|
||||
if isinstance(root, klass):
|
||||
return True
|
||||
|
||||
for child in root.children: # noqa: SIM110
|
||||
for child in root.children:
|
||||
if has_node_of_type(child, klass):
|
||||
return True
|
||||
|
||||
@ -90,7 +90,7 @@ def process_motor_nodes(app, doctree):
|
||||
if desc_content_node.line is None and obj_motor_info["is_pymongo_docstring"]:
|
||||
maybe_warn_about_code_block(name, desc_content_node)
|
||||
|
||||
if obj_motor_info["is_async_method"]: # noqa: SIM102
|
||||
if obj_motor_info["is_async_method"]:
|
||||
# Might be a handwritten RST with "coroutine" already.
|
||||
if not has_coro_annotation(signature_node):
|
||||
coro_annotation = addnodes.desc_annotation(
|
||||
@ -125,9 +125,9 @@ def get_motor_attr(motor_class, name, *defargs):
|
||||
attr = safe_getattr(motor_class, name, *defargs)
|
||||
|
||||
# Store some info for process_motor_nodes()
|
||||
full_name = f"{motor_class.__module__}.{motor_class.__name__}.{name}"
|
||||
full_name = "%s.%s.%s" % (motor_class.__module__, motor_class.__name__, name)
|
||||
|
||||
full_name_legacy = f"motor.{motor_class.__module__}.{motor_class.__name__}.{name}"
|
||||
full_name_legacy = "motor.%s.%s.%s" % (motor_class.__module__, motor_class.__name__, name)
|
||||
|
||||
# These sub-attributes are set in motor.asynchronize()
|
||||
has_coroutine_annotation = getattr(attr, "coroutine_annotation", False)
|
||||
|
||||
@ -1,21 +1,14 @@
|
||||
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.10 and later.
|
||||
* PyMongo_ 4.9 and later.
|
||||
* CPython 3.7 and later.
|
||||
* PyMongo_ 4.5 and later.
|
||||
|
||||
Motor can integrate with either Tornado or asyncio.
|
||||
|
||||
The default authentication mechanism for MongoDB is SCRAM-SHA-1.
|
||||
The default authentication mechanism for MongoDB 3.0+ is SCRAM-SHA-1.
|
||||
|
||||
Building the docs requires `sphinx`_.
|
||||
|
||||
@ -34,6 +27,24 @@ Motor and PyMongo
|
||||
+-------------------+-----------------+
|
||||
| Motor Version | PyMongo Version |
|
||||
+===================+=================+
|
||||
| 1.0 | 3.3+ |
|
||||
+-------------------+-----------------+
|
||||
| 1.1 | 3.4+ |
|
||||
+-------------------+-----------------+
|
||||
| 1.2 | 3.6+ |
|
||||
+-------------------+-----------------+
|
||||
| 1.3 | 3.6+ |
|
||||
+-------------------+-----------------+
|
||||
| 2.0 | 3.7+ |
|
||||
+-------------------+-----------------+
|
||||
| 2.1 | 3.10+ |
|
||||
+-------------------+-----------------+
|
||||
| 2.2 | 3.11+ |
|
||||
+-------------------+-----------------+
|
||||
| 2.3 | 3.11+ |
|
||||
+-------------------+-----------------+
|
||||
| 2.4 | 3.11+ |
|
||||
+-------------------+-----------------+
|
||||
| 2.5 | 3.12+ |
|
||||
+-------------------+-----------------+
|
||||
| 3.0 | 4.1+ |
|
||||
@ -44,41 +55,43 @@ Motor and PyMongo
|
||||
+-------------------+-----------------+
|
||||
| 3.3 | 4.5+ |
|
||||
+-------------------+-----------------+
|
||||
| 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 | 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 |
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
+---------------------------------------------------------------------------------------------------+
|
||||
| MongoDB Version |
|
||||
+=====================+=====+=====+=====+=====+=====+=====+=====+=====+=====+=====+=====+=====+=====+
|
||||
| | 2.2 | 2.4 | 2.6 | 3.0 | 3.2 | 3.4 | 3.6 | 4.0 | 4.2 | 4.4 | 5.0 | 6.0 | 7.0 |
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| Motor Version | 1.0 | Y | Y | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 1.1 | Y | Y | Y | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 1.2 |**N**|**N**| Y | Y | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 1.3 |**N**|**N**| Y | Y | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 2.0 |**N**|**N**|**N**| Y | Y | Y | Y | Y | Y |**N**|**N**|**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 2.1 |**N**|**N**|**N**| Y | Y | Y | Y | Y | Y |**N**|**N**|**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 2.2 |**N**|**N**|**N**| Y | Y | Y | Y | Y | Y | Y |**N**|**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 2.3 |**N**|**N**|**N**| Y | Y | Y | Y | Y | Y | Y |**N**|**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 2.4 |**N**|**N**|**N**| Y | Y | Y | Y | Y | Y | Y |**N**|**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 2.5 |**N**|**N**|**N**| Y | Y | Y | Y | Y | Y | Y | Y |**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 3.0 |**N**|**N**|**N**|**N**|**N**|**N**| Y | Y | Y | Y | Y |**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 3.1 |**N**|**N**|**N**|**N**|**N**|**N**| Y | Y | Y | Y | Y | Y |**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 3.2 |**N**|**N**|**N**|**N**|**N**|**N**| Y | Y | Y | Y | Y | Y | Y |
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | 3.3 |**N**|**N**|**N**|**N**|**N**|**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.
|
||||
@ -86,7 +99,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://www.mongodb.com/docs/languages/python/pymongo-driver/current/reference/compatibility/
|
||||
.. _the PyMongo compatibility matrix: https://mongodb.com/docs/drivers/pymongo#mongodb-compatibility
|
||||
|
||||
Motor and Tornado
|
||||
`````````````````
|
||||
@ -97,7 +110,23 @@ known to be incompatible, or have not been tested together.
|
||||
+---------------------------------------------+
|
||||
| Tornado Version |
|
||||
+=====================+=====+=====+=====+=====+
|
||||
| Motor Version | 2.5 |**N**|**N**| Y | Y |
|
||||
| | 3.x | 4.x | 5.x | 6.x |
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
| Motor Version | 1.0 | Y | Y |**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
| | 1.1 | Y | Y |**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
| | 1.2 |**N**| Y |**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
| | 1.3 |**N**| Y |**N**|**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
| | 2.0 |**N**| Y | Y |**N**|
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
| | 2.1 |**N**| Y | Y | Y |
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
| | 2.2 |**N**|**N**| Y | Y |
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
| | 2.3 |**N**|**N**| Y | Y |
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
| | 3.0 |**N**|**N**|**N**| Y |
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
@ -107,12 +136,19 @@ known to be incompatible, or have not been tested together.
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
| | 3.3 |**N**|**N**|**N**| Y |
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
| | 3.4 |**N**|**N**|**N**| Y |
|
||||
+---------------+-----+-----+-----+-----+-----+
|
||||
|
||||
Motor and Python
|
||||
````````````````
|
||||
|
||||
Motor 1.2 dropped support for the short-lived version of
|
||||
the "async for" protocol implemented in Python 3.5.0 and 3.5.1. Motor continues
|
||||
to work with "async for" loops in Python 3.5.2 and later.
|
||||
|
||||
Motor 1.2.5 and 1.3.1 add compatibility with Python 3.7, but at the cost of
|
||||
dropping Python 3.4.3 and older.
|
||||
|
||||
Motor 2.2 dropped support for Pythons older than 3.5.2.
|
||||
|
||||
Motor 2.5 deprecated support for Python 3.5.
|
||||
|
||||
Motor 3.0 dropped support for Pythons older than 3.7.
|
||||
@ -121,55 +157,39 @@ 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 and added support for Python 3.13.
|
||||
|
||||
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 |
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
+---------------------------------------------------------------+
|
||||
| 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**|
|
||||
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
|
||||
Not Supported
|
||||
-------------
|
||||
|
||||
@ -3,13 +3,6 @@
|
||||
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.
|
||||
@ -19,7 +12,6 @@ Tutorial: Using Motor With :mod:`asyncio`
|
||||
import pymongo
|
||||
import motor.motor_asyncio
|
||||
import asyncio
|
||||
|
||||
client = motor.motor_asyncio.AsyncIOMotorClient()
|
||||
db = client.test_database
|
||||
|
||||
@ -28,17 +20,14 @@ Tutorial: Using Motor With :mod:`asyncio`
|
||||
import pymongo
|
||||
import motor.motor_asyncio
|
||||
import asyncio
|
||||
|
||||
client = motor.motor_asyncio.AsyncIOMotorClient()
|
||||
db = client.test_database
|
||||
pymongo.MongoClient().test_database.test_collection.insert_many(
|
||||
[{"i": i} for i in range(2000)]
|
||||
)
|
||||
[{'i': i} for i in range(2000)])
|
||||
|
||||
.. testcleanup:: *
|
||||
|
||||
import pymongo
|
||||
|
||||
pymongo.MongoClient().test_database.test_collection.delete_many({})
|
||||
|
||||
A guide to using MongoDB and asyncio with Motor.
|
||||
@ -85,9 +74,7 @@ Motor, like PyMongo, represents data with a 4-level object hierarchy:
|
||||
|
||||
Creating a Client
|
||||
-----------------
|
||||
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
|
||||
You typically create a single instance of :class:`~motor.motor_asyncio.AsyncIOMotorClient` at the time your
|
||||
application starts up.
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
@ -100,13 +87,13 @@ specify the host and port like:
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> client = motor.motor_asyncio.AsyncIOMotorClient("localhost", 27017)
|
||||
>>> client = motor.motor_asyncio.AsyncIOMotorClient('localhost', 27017)
|
||||
|
||||
Motor also supports `connection URIs`_:
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> client = motor.motor_asyncio.AsyncIOMotorClient("mongodb://localhost:27017")
|
||||
>>> client = motor.motor_asyncio.AsyncIOMotorClient('mongodb://localhost:27017')
|
||||
|
||||
Connect to a replica set like:
|
||||
|
||||
@ -124,7 +111,7 @@ dot-notation or bracket-notation:
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> db = client.test_database
|
||||
>>> db = client["test_database"]
|
||||
>>> db = client['test_database']
|
||||
|
||||
Creating a reference to a database does no I/O and does not require an
|
||||
``await`` expression.
|
||||
@ -139,7 +126,7 @@ collection in Motor works the same as getting a database:
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> collection = db.test_collection
|
||||
>>> collection = db["test_collection"]
|
||||
>>> collection = db['test_collection']
|
||||
|
||||
Just like getting a reference to a database, getting a reference to a
|
||||
collection does no I/O and doesn't require an ``await`` expression.
|
||||
@ -153,9 +140,9 @@ store a document in MongoDB, call :meth:`~AsyncIOMotorCollection.insert_one` in
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> async def do_insert():
|
||||
... document = {"key": "value"}
|
||||
... document = {'key': 'value'}
|
||||
... result = await db.test_collection.insert_one(document)
|
||||
... print("result %s" % repr(result.inserted_id))
|
||||
... print('result %s' % repr(result.inserted_id))
|
||||
...
|
||||
>>>
|
||||
>>> import asyncio
|
||||
@ -170,22 +157,23 @@ store a document in MongoDB, call :meth:`~AsyncIOMotorCollection.insert_one` in
|
||||
|
||||
>>> # Clean up from previous insert
|
||||
>>> pymongo.MongoClient().test_database.test_collection.delete_many({})
|
||||
DeleteResult({'n': 1, 'ok': 1.0}, acknowledged=True)
|
||||
<pymongo.results.DeleteResult ...>
|
||||
|
||||
Insert documents in large batches with :meth:`~AsyncIOMotorCollection.insert_many`:
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> async def do_insert():
|
||||
... result = await db.test_collection.insert_many([{"i": i} for i in range(2000)])
|
||||
... print("inserted %d docs" % (len(result.inserted_ids),))
|
||||
... result = await db.test_collection.insert_many(
|
||||
... [{'i': i} for i in range(2000)])
|
||||
... print('inserted %d docs' % (len(result.inserted_ids),))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_insert())
|
||||
inserted 2000 docs
|
||||
|
||||
Getting a Single Document With ``find_one``
|
||||
-------------------------------------------
|
||||
Getting a Single Document With `find_one`
|
||||
-----------------------------------------
|
||||
|
||||
Use :meth:`~motor.motor_asyncio.AsyncIOMotorCollection.find_one` to get the first document that
|
||||
matches a query. For example, to get a document where the value for key "i" is
|
||||
@ -194,7 +182,7 @@ less than 1:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find_one():
|
||||
... document = await db.test_collection.find_one({"i": {"$lt": 1}})
|
||||
... document = await db.test_collection.find_one({'i': {'$lt': 1}})
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
@ -221,7 +209,7 @@ To find all documents with "i" less than 5:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find():
|
||||
... cursor = db.test_collection.find({"i": {"$lt": 5}}).sort("i")
|
||||
... cursor = db.test_collection.find({'i': {'$lt': 5}}).sort('i')
|
||||
... for document in await cursor.to_list(length=100):
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
@ -245,7 +233,7 @@ You can handle one document at a time in an ``async for`` loop:
|
||||
|
||||
>>> async def do_find():
|
||||
... c = db.test_collection
|
||||
... async for document in c.find({"i": {"$lt": 2}}):
|
||||
... async for document in c.find({'i': {'$lt': 2}}):
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
@ -258,9 +246,9 @@ You can apply a sort, limit, or skip to a query before you begin iterating:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find():
|
||||
... cursor = db.test_collection.find({"i": {"$lt": 4}})
|
||||
... cursor = db.test_collection.find({'i': {'$lt': 4}})
|
||||
... # Modify the query before iterating
|
||||
... cursor.sort("i", -1).skip(1).limit(2)
|
||||
... cursor.sort('i', -1).skip(1).limit(2)
|
||||
... async for document in cursor:
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
@ -272,7 +260,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://www.mongodb.com/docs/manual/core/cursors/#cursor-batches
|
||||
.. _`large batches`: https://mongodb.com/docs/manual/tutorial/iterate-a-cursor/#cursor-batches
|
||||
|
||||
Counting Documents
|
||||
------------------
|
||||
@ -284,9 +272,9 @@ that match a query:
|
||||
|
||||
>>> async def do_count():
|
||||
... n = await db.test_collection.count_documents({})
|
||||
... print("%s documents in collection" % n)
|
||||
... n = await db.test_collection.count_documents({"i": {"$gt": 1000}})
|
||||
... print("%s documents where i > 1000" % n)
|
||||
... print('%s documents in collection' % n)
|
||||
... n = await db.test_collection.count_documents({'i': {'$gt': 1000}})
|
||||
... print('%s documents where i > 1000' % n)
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_count())
|
||||
@ -305,13 +293,13 @@ replacement document. The query follows the same syntax as for :meth:`find` or
|
||||
|
||||
>>> async def do_replace():
|
||||
... coll = db.test_collection
|
||||
... old_document = await coll.find_one({"i": 50})
|
||||
... print("found document: %s" % pprint.pformat(old_document))
|
||||
... _id = old_document["_id"]
|
||||
... result = await coll.replace_one({"_id": _id}, {"key": "value"})
|
||||
... print("replaced %s document" % result.modified_count)
|
||||
... new_document = await coll.find_one({"_id": _id})
|
||||
... print("document is now %s" % pprint.pformat(new_document))
|
||||
... old_document = await coll.find_one({'i': 50})
|
||||
... print('found document: %s' % pprint.pformat(old_document))
|
||||
... _id = old_document['_id']
|
||||
... result = await coll.replace_one({'_id': _id}, {'key': 'value'})
|
||||
... print('replaced %s document' % result.modified_count)
|
||||
... new_document = await coll.find_one({'_id': _id})
|
||||
... print('document is now %s' % pprint.pformat(new_document))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_replace())
|
||||
@ -331,10 +319,10 @@ operator to set "key" to "value":
|
||||
|
||||
>>> async def do_update():
|
||||
... coll = db.test_collection
|
||||
... result = await coll.update_one({"i": 51}, {"$set": {"key": "value"}})
|
||||
... print("updated %s document" % result.modified_count)
|
||||
... new_document = await coll.find_one({"i": 51})
|
||||
... print("document is now %s" % pprint.pformat(new_document))
|
||||
... result = await coll.update_one({'i': 51}, {'$set': {'key': 'value'}})
|
||||
... print('updated %s document' % result.modified_count)
|
||||
... new_document = await coll.find_one({'i': 51})
|
||||
... print('document is now %s' % pprint.pformat(new_document))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_update())
|
||||
@ -363,9 +351,9 @@ Deleting Documents
|
||||
>>> async def do_delete_one():
|
||||
... coll = db.test_collection
|
||||
... n = await coll.count_documents({})
|
||||
... print("%s documents before calling delete_one()" % n)
|
||||
... result = await db.test_collection.delete_one({"i": {"$gte": 1000}})
|
||||
... print("%s documents after" % (await coll.count_documents({})))
|
||||
... print('%s documents before calling delete_one()' % n)
|
||||
... result = await db.test_collection.delete_one({'i': {'$gte': 1000}})
|
||||
... print('%s documents after' % (await coll.count_documents({})))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_delete_one())
|
||||
@ -381,9 +369,9 @@ Deleting Documents
|
||||
>>> async def do_delete_many():
|
||||
... coll = db.test_collection
|
||||
... n = await coll.count_documents({})
|
||||
... print("%s documents before calling delete_many()" % n)
|
||||
... result = await db.test_collection.delete_many({"i": {"$gte": 1000}})
|
||||
... print("%s documents after" % (await coll.count_documents({})))
|
||||
... print('%s documents before calling delete_many()' % n)
|
||||
... result = await db.test_collection.delete_many({'i': {'$gte': 1000}})
|
||||
... print('%s documents after' % (await coll.count_documents({})))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(do_delete_many())
|
||||
@ -402,7 +390,8 @@ the :meth:`~motor.motor_asyncio.AsyncIOMotorDatabase.command` method on
|
||||
|
||||
>>> from bson import SON
|
||||
>>> async def use_distinct_command():
|
||||
... response = await db.command(SON([("distinct", "test_collection"), ("key", "i")]))
|
||||
... response = await db.command(SON([("distinct", "test_collection"),
|
||||
... ("key", "i")]))
|
||||
...
|
||||
>>> loop = client.get_io_loop()
|
||||
>>> loop.run_until_complete(use_distinct_command())
|
||||
@ -461,9 +450,11 @@ to an :class:`aiohttp.web.Application`:
|
||||
:start-after: main-start
|
||||
:end-before: main-end
|
||||
|
||||
.. 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.
|
||||
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.
|
||||
|
||||
Visit ``localhost:8080/pages/page-one`` and the server responds "Hello!".
|
||||
At ``localhost:8080/pages/page-two`` it responds "Goodbye." At other URLs it
|
||||
|
||||
@ -3,13 +3,6 @@
|
||||
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.
|
||||
@ -21,7 +14,6 @@ Tutorial: Using Motor With Tornado
|
||||
import tornado.web
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado import gen
|
||||
|
||||
db = motor.motor_tornado.MotorClient().test_database
|
||||
|
||||
.. testsetup:: after-inserting-2000-docs
|
||||
@ -31,16 +23,15 @@ Tutorial: Using Motor With Tornado
|
||||
import tornado.web
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado import gen
|
||||
|
||||
db = motor.motor_tornado.MotorClient().test_database
|
||||
sync_db = pymongo.MongoClient().test_database
|
||||
sync_db.test_collection.drop()
|
||||
sync_db.test_collection.insert_many([{"i": i} for i in range(2000)])
|
||||
sync_db.test_collection.insert_many(
|
||||
[{'i': i} for i in range(2000)])
|
||||
|
||||
.. testcleanup:: *
|
||||
|
||||
import pymongo
|
||||
|
||||
pymongo.MongoClient().test_database.test_collection.delete_many({})
|
||||
|
||||
A guide to using MongoDB and Tornado with Motor.
|
||||
@ -93,9 +84,8 @@ Motor, like PyMongo, represents data with a 4-level object hierarchy:
|
||||
|
||||
Creating a Client
|
||||
-----------------
|
||||
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.
|
||||
You typically create a single instance of :class:`MotorClient` at the time your
|
||||
application starts up.
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
@ -106,13 +96,13 @@ specify the host and port like:
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> client = motor.motor_tornado.MotorClient("localhost", 27017)
|
||||
>>> client = motor.motor_tornado.MotorClient('localhost', 27017)
|
||||
|
||||
Motor also supports `connection URIs`_:
|
||||
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> client = motor.motor_tornado.MotorClient("mongodb://localhost:27017")
|
||||
>>> client = motor.motor_tornado.MotorClient('mongodb://localhost:27017')
|
||||
|
||||
Connect to a replica set like:
|
||||
|
||||
@ -130,7 +120,7 @@ dot-notation or bracket-notation:
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> db = client.test_database
|
||||
>>> db = client["test_database"]
|
||||
>>> db = client['test_database']
|
||||
|
||||
Creating a reference to a database does no I/O and does not require an
|
||||
``await`` expression.
|
||||
@ -159,10 +149,10 @@ makes it available to request handlers::
|
||||
def get(self):
|
||||
db = self.settings['db']
|
||||
|
||||
.. 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.
|
||||
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
|
||||
@ -197,7 +187,7 @@ collection in Motor works the same as getting a database:
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> collection = db.test_collection
|
||||
>>> collection = db["test_collection"]
|
||||
>>> collection = db['test_collection']
|
||||
|
||||
Just like getting a reference to a database, getting a reference to a
|
||||
collection does no I/O and doesn't require an ``await`` expression.
|
||||
@ -211,9 +201,9 @@ store a document in MongoDB, call :meth:`~MotorCollection.insert_one` in an
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> async def do_insert():
|
||||
... document = {"key": "value"}
|
||||
... document = {'key': 'value'}
|
||||
... result = await db.test_collection.insert_one(document)
|
||||
... print("result %s" % repr(result.inserted_id))
|
||||
... print('result %s' % repr(result.inserted_id))
|
||||
...
|
||||
>>>
|
||||
>>> IOLoop.current().run_sync(do_insert)
|
||||
@ -226,7 +216,7 @@ store a document in MongoDB, call :meth:`~MotorCollection.insert_one` in an
|
||||
|
||||
>>> # Clean up from previous insert
|
||||
>>> pymongo.MongoClient().test_database.test_collection.delete_many({})
|
||||
DeleteResult({'n': 1, 'ok': 1.0}, acknowledged=True)
|
||||
<pymongo.results.DeleteResult ...>
|
||||
|
||||
A typical beginner's mistake with Motor is to insert documents in a loop,
|
||||
not waiting for each insert to complete before beginning the next::
|
||||
@ -246,7 +236,7 @@ sequence, use ``await``:
|
||||
|
||||
>>> async def do_insert():
|
||||
... for i in range(2000):
|
||||
... await db.test_collection.insert_one({"i": i})
|
||||
... await db.test_collection.insert_one({'i': i})
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_insert)
|
||||
|
||||
@ -259,7 +249,7 @@ sequence, use ``await``:
|
||||
|
||||
>>> # Clean up from previous insert
|
||||
>>> pymongo.MongoClient().test_database.test_collection.delete_many({})
|
||||
DeleteResult({'n': 2000, 'ok': 1.0}, acknowledged=True)
|
||||
<pymongo.results.DeleteResult ...>
|
||||
|
||||
For better performance, insert documents in large batches with
|
||||
:meth:`~MotorCollection.insert_many`:
|
||||
@ -267,8 +257,9 @@ For better performance, insert documents in large batches with
|
||||
.. doctest:: before-inserting-2000-docs
|
||||
|
||||
>>> async def do_insert():
|
||||
... result = await db.test_collection.insert_many([{"i": i} for i in range(2000)])
|
||||
... print("inserted %d docs" % (len(result.inserted_ids),))
|
||||
... result = await db.test_collection.insert_many(
|
||||
... [{'i': i} for i in range(2000)])
|
||||
... print('inserted %d docs' % (len(result.inserted_ids),))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_insert)
|
||||
inserted 2000 docs
|
||||
@ -282,7 +273,7 @@ less than 1:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find_one():
|
||||
... document = await db.test_collection.find_one({"i": {"$lt": 1}})
|
||||
... document = await db.test_collection.find_one({'i': {'$lt': 1}})
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_find_one)
|
||||
@ -311,7 +302,7 @@ To find all documents with "i" less than 5:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find():
|
||||
... cursor = db.test_collection.find({"i": {"$lt": 5}}).sort("i")
|
||||
... cursor = db.test_collection.find({'i': {'$lt': 5}}).sort('i')
|
||||
... for document in await cursor.to_list(length=100):
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
@ -334,7 +325,7 @@ You can handle one document at a time in an ``async for`` loop:
|
||||
|
||||
>>> async def do_find():
|
||||
... c = db.test_collection
|
||||
... async for document in c.find({"i": {"$lt": 2}}):
|
||||
... async for document in c.find({'i': {'$lt': 2}}):
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_find)
|
||||
@ -346,9 +337,9 @@ You can apply a sort, limit, or skip to a query before you begin iterating:
|
||||
.. doctest:: after-inserting-2000-docs
|
||||
|
||||
>>> async def do_find():
|
||||
... cursor = db.test_collection.find({"i": {"$lt": 4}})
|
||||
... cursor = db.test_collection.find({'i': {'$lt': 4}})
|
||||
... # Modify the query before iterating
|
||||
... cursor.sort("i", -1).skip(1).limit(2)
|
||||
... cursor.sort('i', -1).skip(1).limit(2)
|
||||
... async for document in cursor:
|
||||
... pprint.pprint(document)
|
||||
...
|
||||
@ -359,7 +350,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://www.mongodb.com/docs/manual/core/cursors/#cursor-batches
|
||||
.. _`large batches`: https://mongodb.com/docs/manual/tutorial/iterate-a-cursor/#cursor-batches
|
||||
|
||||
Counting Documents
|
||||
------------------
|
||||
@ -370,9 +361,9 @@ documents in a collection, or the number of documents that match a query:
|
||||
|
||||
>>> async def do_count():
|
||||
... n = await db.test_collection.count_documents({})
|
||||
... print("%s documents in collection" % n)
|
||||
... n = await db.test_collection.count_documents({"i": {"$gt": 1000}})
|
||||
... print("%s documents where i > 1000" % n)
|
||||
... print('%s documents in collection' % n)
|
||||
... n = await db.test_collection.count_documents({'i': {'$gt': 1000}})
|
||||
... print('%s documents where i > 1000' % n)
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_count)
|
||||
2000 documents in collection
|
||||
@ -390,13 +381,13 @@ replacement document. The query follows the same syntax as for :meth:`find` or
|
||||
|
||||
>>> async def do_replace():
|
||||
... coll = db.test_collection
|
||||
... old_document = await coll.find_one({"i": 50})
|
||||
... print("found document: %s" % pprint.pformat(old_document))
|
||||
... _id = old_document["_id"]
|
||||
... result = await coll.replace_one({"_id": _id}, {"key": "value"})
|
||||
... print("replaced %s document" % result.modified_count)
|
||||
... new_document = await coll.find_one({"_id": _id})
|
||||
... print("document is now %s" % pprint.pformat(new_document))
|
||||
... old_document = await coll.find_one({'i': 50})
|
||||
... print('found document: %s' % pprint.pformat(old_document))
|
||||
... _id = old_document['_id']
|
||||
... result = await coll.replace_one({'_id': _id}, {'key': 'value'})
|
||||
... print('replaced %s document' % result.modified_count)
|
||||
... new_document = await coll.find_one({'_id': _id})
|
||||
... print('document is now %s' % pprint.pformat(new_document))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_replace)
|
||||
found document: {'_id': ObjectId('...'), 'i': 50}
|
||||
@ -415,10 +406,10 @@ operator to set "key" to "value":
|
||||
|
||||
>>> async def do_update():
|
||||
... coll = db.test_collection
|
||||
... result = await coll.update_one({"i": 51}, {"$set": {"key": "value"}})
|
||||
... print("updated %s document" % result.modified_count)
|
||||
... new_document = await coll.find_one({"i": 51})
|
||||
... print("document is now %s" % pprint.pformat(new_document))
|
||||
... result = await coll.update_one({'i': 51}, {'$set': {'key': 'value'}})
|
||||
... print('updated %s document' % result.modified_count)
|
||||
... new_document = await coll.find_one({'i': 51})
|
||||
... print('document is now %s' % pprint.pformat(new_document))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_update)
|
||||
updated 1 document
|
||||
@ -446,9 +437,9 @@ Removing Documents
|
||||
>>> async def do_delete_one():
|
||||
... coll = db.test_collection
|
||||
... n = await coll.count_documents({})
|
||||
... print("%s documents before calling delete_one()" % n)
|
||||
... result = await db.test_collection.delete_one({"i": {"$gte": 1000}})
|
||||
... print("%s documents after" % (await coll.count_documents({})))
|
||||
... print('%s documents before calling delete_one()' % n)
|
||||
... result = await db.test_collection.delete_one({'i': {'$gte': 1000}})
|
||||
... print('%s documents after' % (await coll.count_documents({})))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_delete_one)
|
||||
2000 documents before calling delete_one()
|
||||
@ -463,9 +454,9 @@ Removing Documents
|
||||
>>> async def do_delete_many():
|
||||
... coll = db.test_collection
|
||||
... n = await coll.count_documents({})
|
||||
... print("%s documents before calling delete_many()" % n)
|
||||
... result = await db.test_collection.delete_many({"i": {"$gte": 1000}})
|
||||
... print("%s documents after" % (await coll.count_documents({})))
|
||||
... print('%s documents before calling delete_many()' % n)
|
||||
... result = await db.test_collection.delete_many({'i': {'$gte': 1000}})
|
||||
... print('%s documents after' % (await coll.count_documents({})))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(do_delete_many)
|
||||
1999 documents before calling delete_many()
|
||||
@ -483,7 +474,8 @@ the :meth:`~motor.motor_tornado.MotorDatabase.command` method on
|
||||
|
||||
>>> from bson import SON
|
||||
>>> async def use_distinct_command():
|
||||
... response = await db.command(SON([("distinct", "test_collection"), ("key", "i")]))
|
||||
... response = await db.command(SON([("distinct", "test_collection"),
|
||||
... ("key", "i")]))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(use_distinct_command)
|
||||
|
||||
|
||||
@ -13,17 +13,18 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""Motor, an asynchronous driver for MongoDB."""
|
||||
from ._version import get_version_string, version, version_tuple # noqa: F401
|
||||
|
||||
from ._version import get_version_string, version, version_tuple # noqa
|
||||
|
||||
"""Current version of Motor."""
|
||||
|
||||
|
||||
try:
|
||||
import tornado
|
||||
import tornado # type: ignore
|
||||
except ImportError:
|
||||
tornado = None # type:ignore[assignment]
|
||||
tornado = None
|
||||
else:
|
||||
# For backwards compatibility with Motor 0.4, export Motor's Tornado classes
|
||||
# at module root. This may change in the future.
|
||||
from .motor_tornado import * # noqa: F403
|
||||
from .motor_tornado import __all__ # noqa: F401
|
||||
from .motor_tornado import __all__
|
||||
|
||||
@ -13,29 +13,14 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""Version-related data for motor."""
|
||||
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__
|
||||
version_tuple = (3, 3, 2, ".dev0")
|
||||
|
||||
|
||||
def get_version_string() -> str:
|
||||
return __version__
|
||||
if isinstance(version_tuple[-1], str):
|
||||
return ".".join(map(str, version_tuple[:-1])) + version_tuple[-1]
|
||||
return ".".join(map(str, version_tuple))
|
||||
|
||||
|
||||
version = get_version_string()
|
||||
|
||||
@ -28,8 +28,6 @@ import gridfs
|
||||
from motor.motor_asyncio import AsyncIOMotorDatabase, AsyncIOMotorGridFSBucket
|
||||
from motor.motor_gridfs import _hash_gridout
|
||||
|
||||
# mypy: disable-error-code="no-untyped-def,no-untyped-call"
|
||||
|
||||
|
||||
def get_gridfs_file(bucket, filename, request):
|
||||
"""Override to choose a GridFS file to serve at a URL.
|
||||
@ -108,6 +106,7 @@ def set_extra_headers(response, gridout):
|
||||
- `gridout`: The :class:`~motor.motor_asyncio.AsyncIOMotorGridOut` we
|
||||
will serve to the client
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def _config_error(request):
|
||||
@ -135,9 +134,9 @@ class AIOHTTPGridFS:
|
||||
app = aiohttp.web.Application()
|
||||
|
||||
# The GridFS URL pattern must have a "{filename}" variable.
|
||||
resource = app.router.add_resource("/fs/{filename}")
|
||||
resource.add_route("GET", gridfs_handler)
|
||||
resource.add_route("HEAD", gridfs_handler)
|
||||
resource = app.router.add_resource('/fs/{filename}')
|
||||
resource.add_route('GET', gridfs_handler)
|
||||
resource.add_route('HEAD', gridfs_handler)
|
||||
|
||||
app_handler = app.make_handler()
|
||||
server = loop.create_server(app_handler, port=80)
|
||||
@ -189,8 +188,8 @@ class AIOHTTPGridFS:
|
||||
|
||||
try:
|
||||
gridout = await self._get_gridfs_file(self._bucket, filename, request)
|
||||
except gridfs.NoFile as e:
|
||||
raise aiohttp.web.HTTPNotFound(text=request.path) from e
|
||||
except gridfs.NoFile:
|
||||
raise aiohttp.web.HTTPNotFound(text=request.path)
|
||||
|
||||
resp = aiohttp.web.StreamResponse()
|
||||
|
||||
@ -248,8 +247,7 @@ class AIOHTTPGridFS:
|
||||
|
||||
if cache_time > 0:
|
||||
resp.headers["Expires"] = (
|
||||
datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
||||
+ datetime.timedelta(seconds=cache_time)
|
||||
datetime.datetime.utcnow() + datetime.timedelta(seconds=cache_time)
|
||||
).strftime("%a, %d %b %Y %H:%M:%S GMT")
|
||||
|
||||
resp.headers["Cache-Control"] = "max-age=" + str(cache_time)
|
||||
|
||||
248
motor/core.py
248
motor/core.py
@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""Framework-agnostic core of Motor, an asynchronous driver for MongoDB."""
|
||||
|
||||
import functools
|
||||
import time
|
||||
import warnings
|
||||
@ -27,8 +28,7 @@ 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 Cursor, RawBatchCursor
|
||||
from pymongo.cursor_shared import _QUERY_OPTIONS
|
||||
from pymongo.cursor import _QUERY_OPTIONS, Cursor, RawBatchCursor
|
||||
from pymongo.database import Database
|
||||
from pymongo.driver_info import DriverInfo
|
||||
from pymongo.encryption import ClientEncryption
|
||||
@ -73,7 +73,7 @@ def _max_time_expired_error(exc):
|
||||
return isinstance(exc, pymongo.errors.OperationFailure) and exc.code == 50
|
||||
|
||||
|
||||
class AgnosticBase:
|
||||
class AgnosticBase(object):
|
||||
def __eq__(self, other):
|
||||
if (
|
||||
isinstance(other, self.__class__)
|
||||
@ -87,15 +87,10 @@ class AgnosticBase:
|
||||
self.delegate = delegate
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}({self.delegate!r})"
|
||||
return "%s(%r)" % (self.__class__.__name__, self.delegate)
|
||||
|
||||
|
||||
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()
|
||||
@ -110,7 +105,6 @@ 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)
|
||||
@ -128,7 +122,6 @@ 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*.
|
||||
@ -148,44 +141,13 @@ class AgnosticClient(AgnosticBaseProperties):
|
||||
self._io_loop = io_loop
|
||||
|
||||
kwargs.setdefault("connect", False)
|
||||
|
||||
driver_info = DriverInfo("Motor", motor_version, self._framework.platform_info())
|
||||
|
||||
if kwargs.get("driver"):
|
||||
provided_info = kwargs.get("driver")
|
||||
if not isinstance(provided_info, DriverInfo):
|
||||
raise TypeError(
|
||||
f"Incorrect type for `driver` {type(provided_info)};"
|
||||
" expected value of type pymongo.driver_info.DriverInfo"
|
||||
)
|
||||
added_version = f"|{provided_info.version}" if provided_info.version else ""
|
||||
added_platform = f"|{provided_info.platform}" if provided_info.platform else ""
|
||||
driver_info = DriverInfo(
|
||||
f"{driver_info.name}|{provided_info.name}",
|
||||
f"{driver_info.version}{added_version}",
|
||||
f"{driver_info.platform}{added_platform}",
|
||||
)
|
||||
|
||||
kwargs["driver"] = driver_info
|
||||
kwargs.setdefault(
|
||||
"driver", DriverInfo("Motor", motor_version, self._framework.platform_info())
|
||||
)
|
||||
|
||||
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:
|
||||
@ -242,7 +204,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:`~pymongo.timestamp.Timestamp`. Requires
|
||||
the specified :class:`~bson.timestamp.Timestamp`. Requires
|
||||
MongoDB >= 4.0.
|
||||
- `session` (optional): a
|
||||
:class:`~pymongo.client_session.ClientSession`.
|
||||
@ -251,10 +213,9 @@ class AgnosticClient(AgnosticBaseProperties):
|
||||
This option and `resume_after` are mutually exclusive.
|
||||
- `comment` (optional): A user-provided comment to attach to this
|
||||
command.
|
||||
- `full_document_before_change`: Allowed values: `whenAvailable` and `required`. Change
|
||||
events may now result in a `fullDocumentBeforeChange` response field.
|
||||
- `show_expanded_events` (optional): Include expanded events such as DDL events like
|
||||
`dropIndexes`.
|
||||
- `full_document_before_change`: Allowed values: `whenAvailable` and `required`. Change events
|
||||
may now result in a `fullDocumentBeforeChange` response field.
|
||||
- `show_expanded_events` (optional): Include expanded events such as DDL events like `dropIndexes`.
|
||||
|
||||
:Returns:
|
||||
A :class:`~MotorChangeStream`.
|
||||
@ -299,8 +260,8 @@ class AgnosticClient(AgnosticBaseProperties):
|
||||
def __getattr__(self, name):
|
||||
if name.startswith("_"):
|
||||
raise AttributeError(
|
||||
f"{self.__class__.__name__} has no attribute {name!r}. To access the {name}"
|
||||
f" database, use client['{name}']."
|
||||
"%s has no attribute %r. To access the %s"
|
||||
" database, use client['%s']." % (self.__class__.__name__, name, name, name)
|
||||
)
|
||||
|
||||
return self[name]
|
||||
@ -331,7 +292,7 @@ class AgnosticClient(AgnosticBaseProperties):
|
||||
return session_class(obj, self)
|
||||
|
||||
|
||||
class _MotorTransactionContext:
|
||||
class _MotorTransactionContext(object):
|
||||
"""Internal transaction context manager for start_transaction."""
|
||||
|
||||
def __init__(self, session):
|
||||
@ -360,8 +321,8 @@ class AgnosticClientSession(AgnosticBase):
|
||||
|
||||
async with await client.start_session() as s:
|
||||
async with s.start_transaction():
|
||||
await collection.delete_one({"x": 1}, session=s)
|
||||
await collection.insert_one({"x": 2}, session=s)
|
||||
await collection.delete_one({'x': 1}, session=s)
|
||||
await collection.insert_one({'x': 2}, session=s)
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
@ -427,7 +388,7 @@ class AgnosticClientSession(AgnosticBase):
|
||||
In the event of an exception, ``with_transaction`` may retry the commit
|
||||
or the entire transaction, therefore ``coro`` may be awaited
|
||||
multiple times by a single call to ``with_transaction``. Developers
|
||||
should be mindful of this possibility when writing a ``coro`` that
|
||||
should be mindful of this possiblity when writing a ``coro`` that
|
||||
modifies application state or has any other side-effects.
|
||||
Note that even when the ``coro`` is invoked multiple times,
|
||||
``with_transaction`` ensures that the transaction will be committed
|
||||
@ -539,8 +500,8 @@ class AgnosticClientSession(AgnosticBase):
|
||||
# Use "await" for start_session, but not for start_transaction.
|
||||
async with await client.start_session() as s:
|
||||
async with s.start_transaction():
|
||||
await collection.delete_one({"x": 1}, session=s)
|
||||
await collection.insert_one({"x": 2}, session=s)
|
||||
await collection.delete_one({'x': 1}, session=s)
|
||||
await collection.insert_one({'x': 2}, session=s)
|
||||
|
||||
"""
|
||||
self.delegate.start_transaction(
|
||||
@ -708,7 +669,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:`~pymongo.timestamp.Timestamp`. Requires
|
||||
the specified :class:`~bson.timestamp.Timestamp`. Requires
|
||||
MongoDB >= 4.0.
|
||||
- `session` (optional): a
|
||||
:class:`~pymongo.client_session.ClientSession`.
|
||||
@ -717,10 +678,9 @@ class AgnosticDatabase(AgnosticBaseProperties):
|
||||
This option and `resume_after` are mutually exclusive.
|
||||
- `comment` (optional): A user-provided comment to attach to this
|
||||
command.
|
||||
- `full_document_before_change`: Allowed values: `whenAvailable` and `required`. Change
|
||||
events may now result in a `fullDocumentBeforeChange` response field.
|
||||
- `show_expanded_events` (optional): Include expanded events such as DDL events like
|
||||
`dropIndexes`.
|
||||
- `full_document_before_change`: Allowed values: `whenAvailable` and `required`. Change events
|
||||
may now result in a `fullDocumentBeforeChange` response field.
|
||||
- `show_expanded_events` (optional): Include expanded events such as DDL events like `dropIndexes`.
|
||||
|
||||
:Returns:
|
||||
A :class:`~MotorChangeStream`.
|
||||
@ -786,7 +746,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:`~pymongo.son.SON` or
|
||||
should use an instance of :class:`~bson.son.SON` or
|
||||
a string and kwargs instead of a Python `dict`.
|
||||
|
||||
- `value` (optional): value to use for the command verb when
|
||||
@ -797,14 +757,13 @@ class AgnosticDatabase(AgnosticBaseProperties):
|
||||
read preference configured for the transaction.
|
||||
Otherwise, defaults to
|
||||
:attr:`~pymongo.read_preferences.ReadPreference.PRIMARY`.
|
||||
- `codec_options`: A :class:`~pymongo.codec_options.CodecOptions`
|
||||
- `codec_options`: A :class:`~bson.codec_options.CodecOptions`
|
||||
instance.
|
||||
- `session` (optional): A
|
||||
:class:`MotorClientSession`.
|
||||
- `comment` (optional): A user-provided comment to attach to future getMores for this
|
||||
command.
|
||||
- `max_await_time_ms` (optional): The number of ms to wait for more data on future
|
||||
getMores for this command.
|
||||
- `max_await_time_ms` (optional): The number of ms to wait for more data on future getMores for this command.
|
||||
- `**kwargs` (optional): additional keyword arguments will
|
||||
be added to the command document before it is sent
|
||||
|
||||
@ -853,8 +812,8 @@ class AgnosticDatabase(AgnosticBaseProperties):
|
||||
def __getattr__(self, name):
|
||||
if name.startswith("_"):
|
||||
raise AttributeError(
|
||||
f"{self.__class__.__name__} has no attribute {name!r}. To access the {name}"
|
||||
" collection, use database['{name}']."
|
||||
"%s has no attribute %r. To access the %s"
|
||||
" collection, use database['%s']." % (self.__class__.__name__, name, name, name)
|
||||
)
|
||||
|
||||
return self[name]
|
||||
@ -871,14 +830,14 @@ class AgnosticDatabase(AgnosticBaseProperties):
|
||||
client_class_name = self._client.__class__.__name__
|
||||
if database_name == "open_sync":
|
||||
raise TypeError(
|
||||
f"{client_class_name}.open_sync() is unnecessary Motor 0.2, "
|
||||
"see changelog for details."
|
||||
"%s.open_sync() is unnecessary Motor 0.2, "
|
||||
"see changelog for details." % client_class_name
|
||||
)
|
||||
|
||||
raise TypeError(
|
||||
"MotorDatabase object is not callable. If you meant to "
|
||||
f"call the '{database_name}' method on a {client_class_name} object it is "
|
||||
"failing because no such method exists."
|
||||
"call the '%s' method on a %s object it is "
|
||||
"failing because no such method exists." % (database_name, client_class_name)
|
||||
)
|
||||
|
||||
def wrap(self, obj):
|
||||
@ -981,10 +940,11 @@ class AgnosticCollection(AgnosticBaseProperties):
|
||||
def __getattr__(self, name):
|
||||
# Dotted collection name, like "foo.bar".
|
||||
if name.startswith("_"):
|
||||
full_name = f"{self.name}.{name}"
|
||||
full_name = "%s.%s" % (self.name, name)
|
||||
raise AttributeError(
|
||||
f"{self.__class__.__name__} has no attribute {name!r}. To access the {full_name}"
|
||||
f" collection, use database['{full_name}']."
|
||||
"%s has no attribute %r. To access the %s"
|
||||
" collection, use database['%s']."
|
||||
% (self.__class__.__name__, name, full_name, full_name)
|
||||
)
|
||||
|
||||
return self[name]
|
||||
@ -1210,7 +1170,6 @@ class AgnosticCollection(AgnosticBaseProperties):
|
||||
|
||||
change_stream = None
|
||||
|
||||
|
||||
async def watch_collection():
|
||||
global change_stream
|
||||
|
||||
@ -1221,20 +1180,17 @@ class AgnosticCollection(AgnosticBaseProperties):
|
||||
async for change in change_stream:
|
||||
print(change)
|
||||
|
||||
|
||||
# Tornado
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
|
||||
def main():
|
||||
loop = IOLoop.current()
|
||||
# Start watching collection for changes.
|
||||
try:
|
||||
loop.run_sync(watch_collection)
|
||||
except KeyboardInterrupt:
|
||||
if change_stream:
|
||||
loop.run_sync(change_stream.close)
|
||||
|
||||
try:
|
||||
loop.run_sync(watch_collection)
|
||||
except KeyboardInterrupt:
|
||||
if change_stream:
|
||||
loop.run_sync(change_stream.close)
|
||||
|
||||
# asyncio
|
||||
try:
|
||||
@ -1253,14 +1209,14 @@ class AgnosticCollection(AgnosticBaseProperties):
|
||||
.. code-block:: python3
|
||||
|
||||
try:
|
||||
pipeline = [{"$match": {"operationType": "insert"}}]
|
||||
pipeline = [{'$match': {'operationType': 'insert'}}]
|
||||
async with db.collection.watch(pipeline) as stream:
|
||||
async for change in stream:
|
||||
print(change)
|
||||
except pymongo.errors.PyMongoError:
|
||||
# The ChangeStream encountered an unrecoverable error or the
|
||||
# resume attempt failed to recreate the cursor.
|
||||
logging.error("...")
|
||||
logging.error('...')
|
||||
|
||||
For a precise description of the resume process see the
|
||||
`change streams specification`_.
|
||||
@ -1294,10 +1250,9 @@ class AgnosticCollection(AgnosticBaseProperties):
|
||||
This option and `resume_after` are mutually exclusive.
|
||||
- `comment` (optional): A user-provided comment to attach to this
|
||||
command.
|
||||
- `full_document_before_change`: Allowed values: `whenAvailable` and `required`.
|
||||
Change events may now result in a `fullDocumentBeforeChange` response field.
|
||||
- `show_expanded_events` (optional): Include expanded events such as DDL events
|
||||
like `dropIndexes`.
|
||||
- `full_document_before_change`: Allowed values: `whenAvailable` and `required`. Change events
|
||||
may now result in a `fullDocumentBeforeChange` response field.
|
||||
- `show_expanded_events` (optional): Include expanded events such as DDL events like `dropIndexes`.
|
||||
|
||||
:Returns:
|
||||
A :class:`~MotorChangeStream`.
|
||||
@ -1399,11 +1354,6 @@ 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()
|
||||
@ -1483,10 +1433,10 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
|
||||
>>> async def f():
|
||||
... await collection.drop()
|
||||
... await collection.insert_many([{"_id": i} for i in range(5)])
|
||||
... await collection.insert_many([{'_id': i} for i in range(5)])
|
||||
... async for doc in collection.find():
|
||||
... sys.stdout.write(str(doc["_id"]) + ", ")
|
||||
... print("done")
|
||||
... sys.stdout.write(str(doc['_id']) + ', ')
|
||||
... print('done')
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
0, 1, 2, 3, 4, done
|
||||
@ -1499,12 +1449,12 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
|
||||
>>> async def f():
|
||||
... await collection.drop()
|
||||
... await collection.insert_many([{"_id": i} for i in range(5)])
|
||||
... cursor = collection.find().sort([("_id", 1)])
|
||||
... while await cursor.fetch_next:
|
||||
... await collection.insert_many([{'_id': i} for i in range(5)])
|
||||
... cursor = collection.find().sort([('_id', 1)])
|
||||
... while (await cursor.fetch_next):
|
||||
... doc = cursor.next_object()
|
||||
... sys.stdout.write(str(doc["_id"]) + ", ")
|
||||
... print("done")
|
||||
... sys.stdout.write(str(doc['_id']) + ', ')
|
||||
... print('done')
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
0, 1, 2, 3, 4, done
|
||||
@ -1512,7 +1462,7 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
.. versionchanged:: 2.2
|
||||
Deprecated.
|
||||
|
||||
.. _`large batches`: https://www.mongodb.com/docs/manual/core/cursors/#cursor-batches
|
||||
.. _`large batches`: https://www.mongodb.com/docs/manual/tutorial/iterate-a-cursor/#cursor-batches
|
||||
.. _`gen.coroutine`: http://tornadoweb.org/en/stable/gen.html
|
||||
"""
|
||||
warnings.warn(
|
||||
@ -1572,9 +1522,9 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
.. testsetup:: each
|
||||
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
MongoClient().test.test_collection.delete_many({})
|
||||
MongoClient().test.test_collection.insert_many([{"_id": i} for i in range(5)])
|
||||
MongoClient().test.test_collection.insert_many(
|
||||
[{'_id': i} for i in range(5)])
|
||||
|
||||
collection = MotorClient().test.test_collection
|
||||
|
||||
@ -1584,13 +1534,13 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
... if error:
|
||||
... raise error
|
||||
... elif result:
|
||||
... sys.stdout.write(str(result["_id"]) + ", ")
|
||||
... sys.stdout.write(str(result['_id']) + ', ')
|
||||
... else:
|
||||
... # Iteration complete
|
||||
... IOLoop.current().stop()
|
||||
... print("done")
|
||||
... print('done')
|
||||
...
|
||||
>>> cursor = collection.find().sort([("_id", 1)])
|
||||
>>> cursor = collection.find().sort([('_id', 1)])
|
||||
>>> cursor.each(callback=each)
|
||||
>>> IOLoop.current().start()
|
||||
0, 1, 2, 3, 4, done
|
||||
@ -1636,13 +1586,13 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
self._framework.call_soon(self.get_io_loop(), functools.partial(callback, None, None))
|
||||
|
||||
@coroutine_annotation
|
||||
def to_list(self, length=None):
|
||||
def to_list(self, length):
|
||||
"""Get a list of documents.
|
||||
|
||||
.. testsetup:: to_list
|
||||
|
||||
MongoClient().test.test_collection.delete_many({})
|
||||
MongoClient().test.test_collection.insert_many([{"_id": i} for i in range(4)])
|
||||
MongoClient().test.test_collection.insert_many([{'_id': i} for i in range(4)])
|
||||
|
||||
from tornado import ioloop
|
||||
|
||||
@ -1652,12 +1602,13 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
>>> collection = MotorClient().test.test_collection
|
||||
>>>
|
||||
>>> async def f():
|
||||
... cursor = collection.find().sort([("_id", 1)])
|
||||
... cursor = collection.find().sort([('_id', 1)])
|
||||
... docs = await cursor.to_list(length=2)
|
||||
... while docs:
|
||||
... print(docs)
|
||||
... docs = await cursor.to_list(length=2)
|
||||
... print("done")
|
||||
...
|
||||
... print('done')
|
||||
...
|
||||
>>> ioloop.IOLoop.current().run_sync(f)
|
||||
[{'_id': 0}, {'_id': 1}]
|
||||
@ -1694,12 +1645,7 @@ class AgnosticBaseCursor(AgnosticBase):
|
||||
else:
|
||||
the_list = []
|
||||
self._framework.add_future(
|
||||
self.get_io_loop(),
|
||||
self._get_more(),
|
||||
self._to_list,
|
||||
length,
|
||||
the_list,
|
||||
future,
|
||||
self.get_io_loop(), self._get_more(), self._to_list, length, the_list, future
|
||||
)
|
||||
|
||||
return future
|
||||
@ -1787,6 +1733,8 @@ 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()
|
||||
@ -1804,13 +1752,13 @@ class AgnosticCursor(AgnosticBaseCursor):
|
||||
return self.__class__(self.delegate.__deepcopy__(memo), self.collection)
|
||||
|
||||
def _query_flags(self):
|
||||
return self.delegate._query_flags
|
||||
return self.delegate._Cursor__query_flags
|
||||
|
||||
def _data(self):
|
||||
return self.delegate._data
|
||||
return self.delegate._Cursor__data
|
||||
|
||||
def _killed(self):
|
||||
return self.delegate._killed
|
||||
return self.delegate._Cursor__killed
|
||||
|
||||
|
||||
class AgnosticRawBatchCursor(AgnosticCursor):
|
||||
@ -1822,6 +1770,8 @@ class AgnosticCommandCursor(AgnosticBaseCursor):
|
||||
__motor_class_name__ = "MotorCommandCursor"
|
||||
__delegate_class__ = CommandCursor
|
||||
|
||||
_CommandCursor__die = AsyncRead()
|
||||
|
||||
async def try_next(self):
|
||||
"""Advance the cursor without blocking indefinitely.
|
||||
|
||||
@ -1848,10 +1798,10 @@ class AgnosticCommandCursor(AgnosticBaseCursor):
|
||||
return 0
|
||||
|
||||
def _data(self):
|
||||
return self.delegate._data
|
||||
return self.delegate._CommandCursor__data
|
||||
|
||||
def _killed(self):
|
||||
return self.delegate._killed
|
||||
return self.delegate._CommandCursor__killed
|
||||
|
||||
|
||||
class AgnosticRawBatchCommandCursor(AgnosticCommandCursor):
|
||||
@ -1859,29 +1809,29 @@ class AgnosticRawBatchCommandCursor(AgnosticCommandCursor):
|
||||
__delegate_class__ = RawBatchCommandCursor
|
||||
|
||||
|
||||
class _LatentCursor:
|
||||
class _LatentCursor(object):
|
||||
"""Take the place of a PyMongo CommandCursor until aggregate() begins."""
|
||||
|
||||
alive = True
|
||||
_data = []
|
||||
_id = None
|
||||
_killed = False
|
||||
_sock_mgr = None
|
||||
_session = None
|
||||
_explicit_session = None
|
||||
_CommandCursor__data = []
|
||||
_CommandCursor__id = None
|
||||
_CommandCursor__killed = False
|
||||
_CommandCursor__sock_mgr = None
|
||||
_CommandCursor__session = None
|
||||
_CommandCursor__explicit_session = None
|
||||
cursor_id = None
|
||||
|
||||
def __init__(self, collection):
|
||||
self._collection = collection.delegate
|
||||
self._CommandCursor__collection = collection.delegate
|
||||
|
||||
def _end_session(self, *args, **kwargs):
|
||||
def _CommandCursor__end_session(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def _die_lock(self, *args, **kwargs):
|
||||
def _CommandCursor__die(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def clone(self):
|
||||
return _LatentCursor(self._collection)
|
||||
return _LatentCursor(self._CommandCursor__collection)
|
||||
|
||||
def rewind(self):
|
||||
pass
|
||||
@ -1938,9 +1888,9 @@ class AgnosticLatentCommandCursor(AgnosticCommandCursor):
|
||||
# Return early if the task was cancelled.
|
||||
if original_future.done():
|
||||
return
|
||||
if self.delegate._data or not self.delegate.alive:
|
||||
if self.delegate._CommandCursor__data or not self.delegate.alive:
|
||||
# _get_more is complete.
|
||||
original_future.set_result(len(self.delegate._data))
|
||||
original_future.set_result(len(self.delegate._CommandCursor__data))
|
||||
else:
|
||||
# Send a getMore.
|
||||
future = super()._get_more()
|
||||
@ -1964,11 +1914,6 @@ 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,
|
||||
@ -2160,11 +2105,6 @@ 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,
|
||||
@ -2211,7 +2151,7 @@ class AgnosticClientEncryption(AgnosticBase):
|
||||
await self.close()
|
||||
|
||||
def __enter__(self):
|
||||
raise RuntimeError(f'Use {self.__class__.__name__} in "async with", not "with"')
|
||||
raise RuntimeError('Use {} in "async with", not "with"'.format(self.__class__.__name__))
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
pass
|
||||
@ -2233,8 +2173,8 @@ class AgnosticClientEncryption(AgnosticBase):
|
||||
|
||||
.. warning::
|
||||
This function does not update the encryptedFieldsMap in the client's
|
||||
AutoEncryptionOpts, thus the user must create a new client after calling
|
||||
this function with the encryptedFields returned.
|
||||
AutoEncryptionOpts, thus the user must create a new client after calling this function with
|
||||
the encryptedFields returned.
|
||||
|
||||
Normally collection creation is automatic. This method should
|
||||
only be used to specify options on
|
||||
@ -2274,12 +2214,10 @@ class AgnosticClientEncryption(AgnosticBase):
|
||||
|
||||
All optional `create collection command`_ parameters should be passed
|
||||
as keyword arguments to this method.
|
||||
See the documentation for :meth:`~pymongo.database.Database.create_collection`
|
||||
for all valid options.
|
||||
See the documentation for :meth:`~pymongo.database.Database.create_collection` for all valid options.
|
||||
|
||||
:Raises:
|
||||
- :class:`~pymongo.errors.EncryptedCollectionError`: When either data-key creation or
|
||||
creating the collection fails.
|
||||
- :class:`~pymongo.errors.EncryptedCollectionError`: When either data-key creation or creating the collection fails.
|
||||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
|
||||
343
motor/core.pyi
343
motor/core.pyi
@ -12,13 +12,29 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Framework-agnostic type stubs for Motor, an asynchronous driver for MongoDB."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from asyncio import Future
|
||||
from collections.abc import Callable, Coroutine, Iterable, Mapping, MutableMapping, Sequence
|
||||
from typing import (
|
||||
Any,
|
||||
Generic,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Collection,
|
||||
Coroutine,
|
||||
Dict,
|
||||
FrozenSet,
|
||||
Iterable,
|
||||
List,
|
||||
Mapping,
|
||||
MutableMapping,
|
||||
NoReturn,
|
||||
Optional,
|
||||
Sequence,
|
||||
Set,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
overload,
|
||||
@ -28,34 +44,27 @@ import pymongo.common
|
||||
import pymongo.database
|
||||
import pymongo.errors
|
||||
import pymongo.mongo_client
|
||||
import typing_extensions
|
||||
from bson import Binary, Code, CodecOptions, DBRef, Timestamp
|
||||
from bson.codec_options import TypeRegistry
|
||||
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 ClientSession, SessionOptions, TransactionOptions
|
||||
from pymongo.collection import Collection, ReturnDocument # noqa: F401
|
||||
from pymongo.client_session import _T, ClientSession, SessionOptions, TransactionOptions
|
||||
from pymongo.collection import ReturnDocument, _IndexKeyHint, _IndexList, _WriteOp
|
||||
from pymongo.command_cursor import CommandCursor, RawBatchCommandCursor
|
||||
from pymongo.cursor import Cursor, RawBatchCursor
|
||||
from pymongo.cursor_shared import _Hint, _Sort
|
||||
from pymongo.cursor import Cursor, RawBatchCursor, _Hint, _Sort
|
||||
from pymongo.database import Database
|
||||
from pymongo.encryption import ClientEncryption, RewrapManyDataKeyResult
|
||||
from pymongo.encryption_options import RangeOpts
|
||||
from pymongo.operations import _IndexKeyHint, _IndexList
|
||||
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,
|
||||
@ -66,9 +75,9 @@ from pymongo.typings import (
|
||||
)
|
||||
|
||||
try:
|
||||
from pymongo.operations import SearchIndexModel
|
||||
from pymongo import SearchIndexModel
|
||||
except ImportError:
|
||||
SearchIndexModel: typing_extensions.TypeAlias = Any # type:ignore[no-redef]
|
||||
SearchIndexModel = Any
|
||||
|
||||
_WITH_TRANSACTION_RETRY_TIME_LIMIT: int
|
||||
|
||||
@ -77,29 +86,30 @@ _CodecDocumentType = TypeVar("_CodecDocumentType", bound=Mapping[str, Any])
|
||||
def _within_time_limit(start_time: float) -> bool: ...
|
||||
def _max_time_expired_error(exc: Exception) -> bool: ...
|
||||
|
||||
class AgnosticBase:
|
||||
class AgnosticBase(object):
|
||||
delegate: Any
|
||||
|
||||
def __eq__(self, other: object) -> bool: ...
|
||||
def __eq__(self, other: Any) -> bool: ...
|
||||
def __init__(self, delegate: Any) -> None: ...
|
||||
def __repr__(self) -> str: ...
|
||||
|
||||
class AgnosticBaseProperties(AgnosticBase, Generic[_DocumentType]):
|
||||
codec_options: CodecOptions[_DocumentType]
|
||||
class AgnosticBaseProperties(AgnosticBase):
|
||||
codec_options: CodecOptions
|
||||
read_preference: _ServerMode
|
||||
read_concern: ReadConcern
|
||||
write_concern: WriteConcern
|
||||
|
||||
class AgnosticClient(AgnosticBaseProperties[_DocumentType]):
|
||||
class AgnosticClient(AgnosticBaseProperties):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[pymongo.MongoClient[_DocumentType]]
|
||||
__delegate_class__: Type[pymongo.MongoClient]
|
||||
|
||||
def address(self) -> Optional[tuple[str, int]]: ...
|
||||
def arbiters(self) -> set[tuple[str, int]]: ...
|
||||
def address(self) -> Optional[Tuple[str, int]]: ...
|
||||
def arbiters(self) -> Set[Tuple[str, int]]: ...
|
||||
def close(self) -> None: ...
|
||||
def __hash__(self) -> int: ...
|
||||
async def drop_database(
|
||||
self,
|
||||
name_or_database: Union[str, AgnosticDatabase[_DocumentTypeArg]],
|
||||
name_or_database: Union[str, AgnosticDatabase],
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
) -> None: ...
|
||||
@ -111,7 +121,7 @@ class AgnosticClient(AgnosticBaseProperties[_DocumentType]):
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> AgnosticDatabase[_DocumentType]: ...
|
||||
) -> Database[_DocumentType]: ...
|
||||
def get_default_database(
|
||||
self,
|
||||
default: Optional[str] = None,
|
||||
@ -119,18 +129,7 @@ class AgnosticClient(AgnosticBaseProperties[_DocumentType]):
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
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: ...
|
||||
) -> Database[_DocumentType]: ...
|
||||
|
||||
HOST: str
|
||||
|
||||
@ -141,20 +140,20 @@ class AgnosticClient(AgnosticBaseProperties[_DocumentType]):
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> AgnosticCommandCursor[dict[str, Any]]: ...
|
||||
) -> AgnosticCommandCursor: ...
|
||||
async def list_database_names(
|
||||
self,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
) -> list[str]: ...
|
||||
def nodes(self) -> frozenset[_Address]: ...
|
||||
) -> List[str]: ...
|
||||
def nodes(self) -> FrozenSet[_Address]: ...
|
||||
PORT: int
|
||||
def primary(self) -> Optional[tuple[str, int]]: ...
|
||||
def primary(self) -> Optional[Tuple[str, int]]: ...
|
||||
read_concern: ReadConcern
|
||||
def secondaries(self) -> set[tuple[str, int]]: ...
|
||||
def secondaries(self) -> Set[Tuple[str, int]]: ...
|
||||
async def server_info(
|
||||
self, session: Optional[AgnosticClientSession] = None
|
||||
) -> dict[str, Any]: ...
|
||||
) -> Dict[str, Any]: ...
|
||||
def topology_description(self) -> TopologyDescription: ...
|
||||
async def start_session(
|
||||
self,
|
||||
@ -166,16 +165,7 @@ class AgnosticClient(AgnosticBaseProperties[_DocumentType]):
|
||||
_io_loop: Optional[Any]
|
||||
_framework: Any
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
host: Optional[Union[str, Sequence[str]]] = None,
|
||||
port: Optional[int] = None,
|
||||
document_class: Optional[type[_DocumentType]] = None,
|
||||
tz_aware: Optional[bool] = None,
|
||||
connect: Optional[bool] = None,
|
||||
type_registry: Optional[TypeRegistry] = None,
|
||||
**kwargs: Any,
|
||||
) -> None: ...
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
|
||||
@property
|
||||
def io_loop(self) -> Any: ...
|
||||
def get_io_loop(self) -> Any: ...
|
||||
@ -193,9 +183,9 @@ class AgnosticClient(AgnosticBaseProperties[_DocumentType]):
|
||||
comment: Optional[str] = None,
|
||||
full_document_before_change: Optional[str] = None,
|
||||
show_expanded_events: Optional[bool] = None,
|
||||
) -> AgnosticChangeStream[_DocumentType]: ...
|
||||
def __getattr__(self, name: str) -> AgnosticDatabase[_DocumentType]: ...
|
||||
def __getitem__(self, name: str) -> AgnosticDatabase[_DocumentType]: ...
|
||||
) -> AgnosticChangeStream: ...
|
||||
def __getattr__(self, name: str) -> AgnosticDatabase: ...
|
||||
def __getitem__(self, name: str) -> AgnosticDatabase: ...
|
||||
def wrap(self, obj: Any) -> Any: ...
|
||||
|
||||
class _MotorTransactionContext:
|
||||
@ -203,11 +193,11 @@ class _MotorTransactionContext:
|
||||
|
||||
def __init__(self, session: AgnosticClientSession): ...
|
||||
async def __aenter__(self) -> _MotorTransactionContext: ...
|
||||
async def __aexit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None: ...
|
||||
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: ...
|
||||
|
||||
class AgnosticClientSession(AgnosticBase):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[ClientSession]
|
||||
__delegate_class__: Type[ClientSession]
|
||||
|
||||
async def commit_transaction(self) -> None: ...
|
||||
async def abort_transaction(self) -> None: ...
|
||||
@ -240,13 +230,13 @@ class AgnosticClientSession(AgnosticBase):
|
||||
@property
|
||||
def client(self) -> AgnosticClient: ...
|
||||
async def __aenter__(self) -> AgnosticClientSession: ...
|
||||
async def __aexit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None: ...
|
||||
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: ...
|
||||
def __enter__(self) -> None: ...
|
||||
def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None: ...
|
||||
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: ...
|
||||
|
||||
class AgnosticDatabase(AgnosticBaseProperties[_DocumentType]):
|
||||
class AgnosticDatabase(AgnosticBaseProperties):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[Database[_DocumentType]]
|
||||
__delegate_class__: Type[Database]
|
||||
|
||||
def __hash__(self) -> int: ...
|
||||
def __bool__(self) -> int: ...
|
||||
@ -260,34 +250,7 @@ class AgnosticDatabase(AgnosticBaseProperties[_DocumentType]):
|
||||
comment: Optional[Any] = None,
|
||||
max_await_time_ms: Optional[int] = None,
|
||||
**kwargs: Any,
|
||||
) -> AgnosticCommandCursor[_DocumentType]: ...
|
||||
@overload
|
||||
async def command(
|
||||
self,
|
||||
command: Union[str, MutableMapping[str, Any]],
|
||||
value: Any = ...,
|
||||
check: bool = ...,
|
||||
allowable_errors: Optional[Sequence[Union[str, int]]] = ...,
|
||||
read_preference: Optional[_ServerMode] = ...,
|
||||
codec_options: None = ...,
|
||||
session: Optional[AgnosticClientSession] = ...,
|
||||
comment: Optional[Any] = ...,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, Any]: ...
|
||||
@overload
|
||||
async def command(
|
||||
self,
|
||||
command: Union[str, MutableMapping[str, Any]],
|
||||
value: Any = 1,
|
||||
check: bool = True,
|
||||
allowable_errors: Optional[Sequence[Union[str, int]]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
codec_options: CodecOptions[_CodecDocumentType] = ...,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> _CodecDocumentType: ...
|
||||
@overload
|
||||
) -> AgnosticCommandCursor: ...
|
||||
async def command(
|
||||
self,
|
||||
command: Union[str, MutableMapping[str, Any]],
|
||||
@ -299,7 +262,7 @@ class AgnosticDatabase(AgnosticBaseProperties[_DocumentType]):
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> Union[dict[str, Any], _CodecDocumentType]: ...
|
||||
) -> Union[Dict[str, Any], _CodecDocumentType]: ...
|
||||
async def create_collection(
|
||||
self,
|
||||
name: str,
|
||||
@ -310,7 +273,7 @@ class AgnosticDatabase(AgnosticBaseProperties[_DocumentType]):
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
check_exists: Optional[bool] = True,
|
||||
**kwargs: Any,
|
||||
) -> AgnosticCollection[_DocumentType]: ...
|
||||
) -> AgnosticCollection: ...
|
||||
async def dereference(
|
||||
self,
|
||||
dbref: DBRef,
|
||||
@ -320,58 +283,57 @@ class AgnosticDatabase(AgnosticBaseProperties[_DocumentType]):
|
||||
) -> Optional[_DocumentType]: ...
|
||||
async def drop_collection(
|
||||
self,
|
||||
name_or_collection: Union[str, AgnosticCollection[_DocumentTypeArg]],
|
||||
name_or_collection: Union[str, AgnosticCollection],
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
encrypted_fields: Optional[Mapping[str, Any]] = None,
|
||||
) -> dict[str, Any]: ...
|
||||
def get_collection(
|
||||
) -> Dict[str, Any]: ...
|
||||
async def get_collection(
|
||||
self,
|
||||
name: str,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> AgnosticCollection[_DocumentType]: ...
|
||||
) -> AgnosticCollection: ...
|
||||
async def list_collection_names(
|
||||
self,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
filter: Optional[Mapping[str, Any]] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> list[str]: ...
|
||||
) -> List[str]: ...
|
||||
async def list_collections(
|
||||
self,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
filter: Optional[Mapping[str, Any]] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> AgnosticCommandCursor[MutableMapping[str, Any]]: ...
|
||||
@property
|
||||
) -> AgnosticCommandCursor: ...
|
||||
def name(self) -> str: ...
|
||||
async def validate_collection(
|
||||
self,
|
||||
name_or_collection: Union[str, AgnosticCollection[_DocumentTypeArg]],
|
||||
name_or_collection: Union[str, AgnosticCollection],
|
||||
scandata: bool = False,
|
||||
full: bool = False,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
background: Optional[bool] = None,
|
||||
comment: Optional[Any] = None,
|
||||
) -> dict[str, Any]: ...
|
||||
) -> Dict[str, Any]: ...
|
||||
def with_options(
|
||||
self,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> AgnosticDatabase[_DocumentType]: ...
|
||||
) -> AgnosticDatabase: ...
|
||||
async def _async_aggregate(
|
||||
self, pipeline: _Pipeline, session: Optional[AgnosticClientSession] = None, **kwargs: Any
|
||||
) -> AgnosticCommandCursor[_DocumentType]: ...
|
||||
def __init__(self, client: AgnosticClient[_DocumentType], name: str, **kwargs: Any) -> None: ...
|
||||
) -> AgnosticCommandCursor: ...
|
||||
def __init__(self, client: AgnosticClient, name: str, **kwargs: Any) -> None: ...
|
||||
def aggregate(
|
||||
self, pipeline: _Pipeline, *args: Any, **kwargs: Any
|
||||
) -> AgnosticLatentCommandCursor[_DocumentType]: ...
|
||||
) -> AgnosticLatentCommandCursor: ...
|
||||
def watch(
|
||||
self,
|
||||
pipeline: Optional[_Pipeline] = None,
|
||||
@ -386,18 +348,18 @@ class AgnosticDatabase(AgnosticBaseProperties[_DocumentType]):
|
||||
comment: Optional[Any] = None,
|
||||
full_document_before_change: Optional[str] = None,
|
||||
show_expanded_events: Optional[bool] = None,
|
||||
) -> AgnosticChangeStream[_DocumentType]: ...
|
||||
) -> AgnosticChangeStream: ...
|
||||
@property
|
||||
def client(self) -> AgnosticClient[_DocumentType]: ...
|
||||
def __getattr__(self, name: str) -> AgnosticCollection[_DocumentType]: ...
|
||||
def __getitem__(self, name: str) -> AgnosticCollection[_DocumentType]: ...
|
||||
def client(self) -> AgnosticClient: ...
|
||||
def __getattr__(self, name: str) -> AgnosticCollection: ...
|
||||
def __getitem__(self, name: str) -> AgnosticCollection: ...
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> None: ...
|
||||
def wrap(self, obj: Any) -> Any: ...
|
||||
def get_io_loop(self) -> Any: ...
|
||||
|
||||
class AgnosticCollection(AgnosticBaseProperties[_DocumentType]):
|
||||
class AgnosticCollection(AgnosticBaseProperties):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[Collection[_DocumentType]]
|
||||
__delegate_class__: Type[Collection]
|
||||
|
||||
def __hash__(self) -> int: ...
|
||||
def __bool__(self) -> bool: ...
|
||||
@ -430,7 +392,7 @@ class AgnosticCollection(AgnosticBaseProperties[_DocumentType]):
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> list[str]: ...
|
||||
) -> List[str]: ...
|
||||
async def delete_many(
|
||||
self,
|
||||
filter: Mapping[str, Any],
|
||||
@ -456,7 +418,7 @@ class AgnosticCollection(AgnosticBaseProperties[_DocumentType]):
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> list[Any]: ...
|
||||
) -> List[Any]: ...
|
||||
async def drop(
|
||||
self,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
@ -500,7 +462,7 @@ class AgnosticCollection(AgnosticBaseProperties[_DocumentType]):
|
||||
projection: Optional[Union[Mapping[str, Any], Iterable[str]]] = None,
|
||||
sort: Optional[_IndexList] = None,
|
||||
upsert: bool = False,
|
||||
return_document: bool = ...,
|
||||
return_document: bool = ReturnDocument.BEFORE,
|
||||
hint: Optional[_IndexKeyHint] = None,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
let: Optional[Mapping[str, Any]] = None,
|
||||
@ -514,7 +476,7 @@ class AgnosticCollection(AgnosticBaseProperties[_DocumentType]):
|
||||
projection: Optional[Union[Mapping[str, Any], Iterable[str]]] = None,
|
||||
sort: Optional[_IndexList] = None,
|
||||
upsert: bool = False,
|
||||
return_document: bool = ...,
|
||||
return_document: bool = ReturnDocument.BEFORE,
|
||||
array_filters: Optional[Sequence[Mapping[str, Any]]] = None,
|
||||
hint: Optional[_IndexKeyHint] = None,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
@ -541,7 +503,6 @@ class AgnosticCollection(AgnosticBaseProperties[_DocumentType]):
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
) -> InsertOneResult: ...
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
async def options(
|
||||
self, session: Optional[AgnosticClientSession] = None, comment: Optional[Any] = None
|
||||
@ -587,7 +548,7 @@ class AgnosticCollection(AgnosticBaseProperties[_DocumentType]):
|
||||
collation: Optional[_CollationIn] = None,
|
||||
array_filters: Optional[Sequence[Mapping[str, Any]]] = None,
|
||||
hint: Optional[_IndexKeyHint] = None,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
session: Union[Optional[AgnosticClientSession], Optional[AgnosticClientSession]] = None,
|
||||
let: Optional[Mapping[str, Any]] = None,
|
||||
comment: Optional[Any] = None,
|
||||
) -> UpdateResult: ...
|
||||
@ -597,14 +558,14 @@ class AgnosticCollection(AgnosticBaseProperties[_DocumentType]):
|
||||
read_preference: Optional[ReadPreference] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> AgnosticCollection[_DocumentType]: ...
|
||||
def list_search_indexes(
|
||||
) -> Collection[Mapping[str, Any]]: ...
|
||||
async def list_search_indexes(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> AgnosticLatentCommandCursor[Mapping[str, Any]]: ...
|
||||
) -> AgnosticCommandCursor: ...
|
||||
async def create_search_index(
|
||||
self,
|
||||
model: Union[Mapping[str, SearchIndexModel], Any],
|
||||
@ -614,11 +575,11 @@ class AgnosticCollection(AgnosticBaseProperties[_DocumentType]):
|
||||
) -> str: ...
|
||||
async def create_search_indexes(
|
||||
self,
|
||||
models: list[SearchIndexModel],
|
||||
models: List[SearchIndexModel],
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> list[str]: ...
|
||||
) -> List[str]: ...
|
||||
async def drop_search_index(
|
||||
self,
|
||||
name: str,
|
||||
@ -645,19 +606,17 @@ class AgnosticCollection(AgnosticBaseProperties[_DocumentType]):
|
||||
_delegate: Any = None,
|
||||
**kwargs: Any,
|
||||
) -> None: ...
|
||||
def __getattr__(self, name: str) -> AgnosticCollection[_DocumentType]: ...
|
||||
def __getitem__(self, name: str) -> AgnosticCollection[_DocumentType]: ...
|
||||
def __getattr__(self, name: str) -> AgnosticCollection: ...
|
||||
def __getitem__(self, name: str) -> AgnosticCollection: ...
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||
def find(self, *args: Any, **kwargs: Any) -> AgnosticCursor[_DocumentType]: ...
|
||||
def find_raw_batches(
|
||||
self, *args: Any, **kwargs: Any
|
||||
) -> AgnosticRawBatchCursor[_DocumentType]: ...
|
||||
def find(self, *args: Any, **kwargs: Any) -> AgnosticCursor: ...
|
||||
def find_raw_batches(self, *args: Any, **kwargs: Any) -> AgnosticCursor: ...
|
||||
def aggregate(
|
||||
self, pipeline: _Pipeline, *args: Any, **kwargs: Any
|
||||
) -> AgnosticCommandCursor[_DocumentType]: ...
|
||||
) -> AgnosticCommandCursor: ...
|
||||
def aggregate_raw_batches(
|
||||
self, pipeline: _Pipeline, **kwargs: Any
|
||||
) -> AgnosticRawBatchCursor[_DocumentType]: ...
|
||||
) -> AgnosticCommandCursor: ...
|
||||
def watch(
|
||||
self,
|
||||
pipeline: Optional[_Pipeline] = None,
|
||||
@ -675,17 +634,13 @@ class AgnosticCollection(AgnosticBaseProperties[_DocumentType]):
|
||||
) -> Any: ...
|
||||
def list_indexes(
|
||||
self, session: Optional[AgnosticClientSession] = None, **kwargs: Any
|
||||
) -> AgnosticLatentCommandCursor[MutableMapping[str, Any]]: ...
|
||||
) -> AgnosticCommandCursor: ...
|
||||
def wrap(self, obj: Any) -> Any: ...
|
||||
def get_io_loop(self) -> Any: ...
|
||||
|
||||
class AgnosticBaseCursor(AgnosticBase, Generic[_DocumentType]):
|
||||
class AgnosticBaseCursor(AgnosticBase):
|
||||
def __init__(
|
||||
self,
|
||||
cursor: Union[
|
||||
Cursor[_DocumentType], CommandCursor[_DocumentType], _LatentCursor[_DocumentType]
|
||||
],
|
||||
collection: AgnosticCollection[_DocumentType],
|
||||
self, cursor: Union[Cursor, CommandCursor, _LatentCursor], collection: AgnosticCollection
|
||||
) -> None: ...
|
||||
def address(self) -> Optional[_Address]: ...
|
||||
def cursor_id(self) -> Optional[int]: ...
|
||||
@ -697,92 +652,86 @@ class AgnosticBaseCursor(AgnosticBase, Generic[_DocumentType]):
|
||||
async def next(self) -> _DocumentType: ...
|
||||
__anext__ = next
|
||||
async def __aenter__(self) -> Any: ...
|
||||
async def __aexit__(self, exc_type: object, exc_val: object, exc_tb: object) -> Any: ...
|
||||
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> Any: ...
|
||||
def _get_more(self) -> int: ...
|
||||
@property
|
||||
def fetch_next(self) -> Future[Any]: ...
|
||||
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: Optional[int] = ...) -> Future[list[_DocumentType]]: ...
|
||||
def _to_list(
|
||||
self, length: Union[int, None], the_list: list, future: Any, get_more_result: Any
|
||||
) -> None: ...
|
||||
def to_list(self, length: int) -> Future[List]: ...
|
||||
def _to_list(self, length: int, the_list: List, future: Any, get_more_result: Any) -> None: ...
|
||||
def get_io_loop(self) -> Any: ...
|
||||
def batch_size(self, batch_size: int) -> AgnosticBaseCursor[_DocumentType]: ...
|
||||
def batch_size(self, batch_size: int) -> AgnosticBaseCursor: ...
|
||||
def _buffer_size(self) -> int: ...
|
||||
def _query_flags(self) -> Optional[int]: ...
|
||||
def _data(self) -> None: ...
|
||||
def _killed(self) -> None: ...
|
||||
async def close(self) -> None: ...
|
||||
|
||||
class AgnosticCursor(AgnosticBaseCursor[_DocumentType]):
|
||||
class AgnosticCursor(AgnosticBaseCursor):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[Cursor]
|
||||
def collation(self, collation: Optional[_CollationIn]) -> AgnosticCursor[_DocumentType]: ...
|
||||
async def distinct(self, key: str) -> list: ...
|
||||
__delegate_class__: Type[Cursor]
|
||||
def collation(self, collation: Optional[_CollationIn]) -> AgnosticCursor: ...
|
||||
async def distinct(self, key: str) -> List: ...
|
||||
async def explain(self) -> _DocumentType: ...
|
||||
def add_option(self, mask: int) -> AgnosticCursor[_DocumentType]: ...
|
||||
def remove_option(self, mask: int) -> AgnosticCursor[_DocumentType]: ...
|
||||
def limit(self, limit: int) -> AgnosticCursor[_DocumentType]: ...
|
||||
def skip(self, skip: int) -> AgnosticCursor[_DocumentType]: ...
|
||||
def max_scan(self, max_scan: Optional[int]) -> AgnosticCursor[_DocumentType]: ...
|
||||
def add_option(self, mask: int) -> AgnosticCursor: ...
|
||||
def remove_option(self, mask: int) -> AgnosticCursor: ...
|
||||
def limit(self, limit: int) -> AgnosticCursor: ...
|
||||
def skip(self, skip: int) -> AgnosticCursor: ...
|
||||
def max_scan(self, max_scan: Optional[int]) -> AgnosticCursor: ...
|
||||
def sort(
|
||||
self, key_or_list: _Hint, direction: Optional[Union[int, str]] = None
|
||||
) -> AgnosticCursor[_DocumentType]: ...
|
||||
def hint(self, index: Optional[_Hint]) -> AgnosticCursor[_DocumentType]: ...
|
||||
def where(self, code: Union[str, Code]) -> AgnosticCursor[_DocumentType]: ...
|
||||
def max_await_time_ms(
|
||||
self, max_await_time_ms: Optional[int]
|
||||
) -> AgnosticCursor[_DocumentType]: ...
|
||||
def max_time_ms(self, max_time_ms: Optional[int]) -> AgnosticCursor[_DocumentType]: ...
|
||||
def min(self, spec: _Sort) -> AgnosticCursor[_DocumentType]: ...
|
||||
def max(self, spec: _Sort) -> AgnosticCursor[_DocumentType]: ...
|
||||
def comment(self, comment: Any) -> AgnosticCursor[_DocumentType]: ...
|
||||
def allow_disk_use(self, allow_disk_use: bool) -> AgnosticCursor[_DocumentType]: ...
|
||||
def rewind(self) -> AgnosticCursor[_DocumentType]: ...
|
||||
def clone(self) -> AgnosticCursor[_DocumentType]: ...
|
||||
def __copy__(self) -> AgnosticCursor[_DocumentType]: ...
|
||||
def __deepcopy__(self, memo: Any) -> AgnosticCursor[_DocumentType]: ...
|
||||
) -> AgnosticCursor: ...
|
||||
def hint(self, index: Optional[_Hint]) -> AgnosticCursor: ...
|
||||
def where(self, code: Union[str, Code]) -> AgnosticCursor: ...
|
||||
def max_await_time_ms(self, max_await_time_ms: Optional[int]) -> AgnosticCursor: ...
|
||||
def max_time_ms(self, max_time_ms: Optional[int]) -> AgnosticCursor: ...
|
||||
def min(self, spec: _Sort) -> AgnosticCursor: ...
|
||||
def max(self, spec: _Sort) -> AgnosticCursor: ...
|
||||
def comment(self, comment: Any) -> AgnosticCursor: ...
|
||||
def allow_disk_use(self, allow_disk_use: bool) -> AgnosticCursor: ...
|
||||
def rewind(self) -> AgnosticCursor: ...
|
||||
def clone(self) -> AgnosticCursor: ...
|
||||
def __copy__(self) -> AgnosticCursor: ...
|
||||
def __deepcopy__(self, memo: Any) -> AgnosticCursor: ...
|
||||
def _query_flags(self) -> int: ...
|
||||
def _data(self) -> Any: ...
|
||||
def _killed(self) -> Any: ...
|
||||
|
||||
class AgnosticRawBatchCursor(AgnosticCursor[_DocumentType]):
|
||||
class AgnosticRawBatchCursor(AgnosticCursor):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[RawBatchCursor]
|
||||
__delegate_class__: Type[RawBatchCursor]
|
||||
|
||||
class AgnosticCommandCursor(AgnosticBaseCursor[_DocumentType]):
|
||||
class AgnosticCommandCursor(AgnosticBaseCursor):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[CommandCursor]
|
||||
__delegate_class__: Type[CommandCursor]
|
||||
|
||||
def _query_flags(self) -> int: ...
|
||||
def _data(self) -> Any: ...
|
||||
def _killed(self) -> Any: ...
|
||||
|
||||
class AgnosticRawBatchCommandCursor(AgnosticCommandCursor[_DocumentType]):
|
||||
class AgnosticRawBatchCommandCursor(AgnosticCommandCursor):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[RawBatchCommandCursor]
|
||||
__delegate_class__: Type[RawBatchCommandCursor]
|
||||
|
||||
class _LatentCursor(Generic[_DocumentType]):
|
||||
def __init__(self, collection: AgnosticCollection[_DocumentType]): ...
|
||||
def _end_session(self, *args: Any, **kwargs: Any) -> None: ...
|
||||
def clone(self) -> _LatentCursor[_DocumentType]: ...
|
||||
def rewind(self) -> _LatentCursor[_DocumentType]: ...
|
||||
class _LatentCursor:
|
||||
def __init__(self, collection: AgnosticCollection): ...
|
||||
def _CommandCursor__end_session(self, *args: Any, **kwargs: Any) -> None: ...
|
||||
def _CommandCursor__die(self, *args: Any, **kwargs: Any) -> None: ...
|
||||
def clone(self) -> _LatentCursor: ...
|
||||
def rewind(self) -> _LatentCursor: ...
|
||||
|
||||
class AgnosticLatentCommandCursor(AgnosticCommandCursor[_DocumentType]):
|
||||
class AgnosticLatentCommandCursor(AgnosticCommandCursor):
|
||||
__motor_class_name__: str
|
||||
def __init__(
|
||||
self, collection: AgnosticCollection[_DocumentType], start: Any, *args: Any, **kwargs: Any
|
||||
): ...
|
||||
def __init__(self, collection: AgnosticCollection, start: Any, *args: Any, **kwargs: Any): ...
|
||||
def _on_started(self, original_future: Any, future: Any) -> None: ...
|
||||
|
||||
class AgnosticChangeStream(AgnosticBase, Generic[_DocumentType]):
|
||||
class AgnosticChangeStream(AgnosticBase):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[ChangeStream]
|
||||
__delegate_class__: Type[ChangeStream]
|
||||
|
||||
async def _close(self) -> None: ...
|
||||
@property
|
||||
def resume_token(self) -> Optional[Mapping[str, Any]]: ...
|
||||
def __init__(
|
||||
self,
|
||||
@ -808,22 +757,22 @@ class AgnosticChangeStream(AgnosticBase, Generic[_DocumentType]):
|
||||
async def next(self) -> _DocumentType: ...
|
||||
async def try_next(self) -> Optional[_DocumentType]: ...
|
||||
async def close(self) -> None: ...
|
||||
def __aiter__(self) -> AgnosticChangeStream[_DocumentType]: ...
|
||||
def __aiter__(self) -> AgnosticChangeStream: ...
|
||||
__anext__ = next
|
||||
async def __aenter__(self) -> AgnosticChangeStream[_DocumentType]: ...
|
||||
async def __aexit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None: ...
|
||||
async def __aenter__(self) -> AgnosticChangeStream: ...
|
||||
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: ...
|
||||
def get_io_loop(self) -> Any: ...
|
||||
def __enter__(self) -> None: ...
|
||||
def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None: ...
|
||||
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: ...
|
||||
|
||||
class AgnosticClientEncryption(AgnosticBase, Generic[_DocumentType]):
|
||||
class AgnosticClientEncryption(AgnosticBase):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[ClientEncryption]
|
||||
__delegate_class__: Type[ClientEncryption]
|
||||
def __init__(
|
||||
self,
|
||||
kms_providers: Mapping[str, Any],
|
||||
key_vault_namespace: str,
|
||||
key_vault_client: AgnosticClient[_DocumentTypeArg],
|
||||
key_vault_client: AgnosticClient,
|
||||
codec_options: CodecOptions,
|
||||
io_loop: Optional[Any] = None,
|
||||
kms_tls_options: Optional[Mapping[str, Any]] = None,
|
||||
@ -873,17 +822,17 @@ class AgnosticClientEncryption(AgnosticBase, Generic[_DocumentType]):
|
||||
@property
|
||||
def io_loop(self) -> Any: ...
|
||||
def get_io_loop(self) -> Any: ...
|
||||
async def __aenter__(self) -> AgnosticClientEncryption[_DocumentType]: ...
|
||||
async def __aexit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None: ...
|
||||
async def __aenter__(self) -> AgnosticClientEncryption: ...
|
||||
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: ...
|
||||
def __enter__(self) -> NoReturn: ...
|
||||
def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None: ...
|
||||
async def get_keys(self) -> AgnosticCursor[RawBSONDocument]: ...
|
||||
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: ...
|
||||
async def get_keys(self) -> AgnosticCursor: ...
|
||||
async def create_encrypted_collection(
|
||||
self,
|
||||
database: AgnosticDatabase[_DocumentTypeArg],
|
||||
database: AgnosticDatabase,
|
||||
name: str,
|
||||
encrypted_fields: Mapping[str, Any],
|
||||
kms_provider: Optional[str] = None,
|
||||
master_key: Optional[Mapping[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
) -> tuple[AgnosticCollection[_DocumentTypeArg], Mapping[str, Any]]: ...
|
||||
) -> Tuple[AgnosticCollection, Mapping[str, Any]]: ...
|
||||
|
||||
@ -11,6 +11,8 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
get_database_doc = """
|
||||
Get a :class:`MotorDatabase` with the given name and options.
|
||||
|
||||
@ -31,7 +33,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:`~pymongo.codec_options.CodecOptions`. If ``None`` (the
|
||||
:class:`~bson.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 +68,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:`~pymongo.codec_options.CodecOptions`. If ``None`` (the
|
||||
:class:`~bson.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
|
||||
@ -111,7 +113,7 @@ For example, to list all non-system collections::
|
||||
- `comment` (optional): A user-provided comment to attach to this command.
|
||||
- `**kwargs` (optional): Optional parameters of the
|
||||
`listCollections
|
||||
<https://www.mongodb.com/docs/manual/reference/command/listCollections/>`_ command.
|
||||
<https://www.mongodb.com/docs/manual/reference/command/listCollections/>`_ comand.
|
||||
can be passed as keyword arguments to this method. The supported
|
||||
options differ by server version.
|
||||
|
||||
@ -187,7 +189,7 @@ This will print something like::
|
||||
:Returns:
|
||||
An instance of :class:`~pymongo.results.BulkWriteResult`.
|
||||
|
||||
.. seealso:: `Writes and ids <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/crud/insert/#overview>`_
|
||||
.. seealso:: :ref:`writes-and-ids`
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
@ -333,9 +335,9 @@ collection_name}`` we can do::
|
||||
result = await db.command("count", collection_name)
|
||||
|
||||
For commands that take additional arguments we can use
|
||||
kwargs. So ``{count: collection_name, query: query}`` becomes::
|
||||
kwargs. So ``{filemd5: object_id, root: file_root}`` becomes::
|
||||
|
||||
result = await db.command("count", collection_name, query=query)
|
||||
result = await db.command("filemd5", object_id, root=file_root)
|
||||
|
||||
:Parameters:
|
||||
- `command`: document representing the command to be issued,
|
||||
@ -344,7 +346,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:`~pymongo.son.SON` or
|
||||
should use an instance of :class:`~bson.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 +831,7 @@ This prints something like::
|
||||
:Returns:
|
||||
An instance of :class:`~pymongo.results.InsertManyResult`.
|
||||
|
||||
.. seealso:: `Writes and ids <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/crud/insert/#overview>`_
|
||||
.. seealso:: :ref:`writes-and-ids`
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
@ -866,7 +868,7 @@ This code outputs the new document's ``_id``::
|
||||
:Returns:
|
||||
- An instance of :class:`~pymongo.results.InsertOneResult`.
|
||||
|
||||
.. seealso:: `Writes and ids <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/crud/insert/#overview>`_
|
||||
.. seealso:: :ref:`writes-and-ids`
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
@ -890,7 +892,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:`~pymongo.son.SON`
|
||||
Note: `out` options are order sensitive. :class:`~bson.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
|
||||
@ -1140,17 +1142,17 @@ Pass a field name and a direction, either
|
||||
.. testsetup:: sort
|
||||
|
||||
MongoClient().test.test_collection.drop()
|
||||
MongoClient().test.test_collection.insert_many(
|
||||
[{"_id": i, "field1": i % 2, "field2": i} for i in range(5)]
|
||||
)
|
||||
MongoClient().test.test_collection.insert_many([
|
||||
{'_id': i, 'field1': i % 2, 'field2': i}
|
||||
for i in range(5)])
|
||||
collection = MotorClient().test.test_collection
|
||||
|
||||
.. doctest:: sort
|
||||
|
||||
>>> async def f():
|
||||
... cursor = collection.find().sort("_id", pymongo.DESCENDING)
|
||||
... docs = await cursor.to_list()
|
||||
... print([d["_id"] for d in docs])
|
||||
... cursor = collection.find().sort('_id', pymongo.DESCENDING)
|
||||
... docs = await cursor.to_list(None)
|
||||
... print([d['_id'] for d in docs])
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
[4, 3, 2, 1, 0]
|
||||
@ -1160,11 +1162,12 @@ To sort by multiple fields, pass a list of (key, direction) pairs:
|
||||
.. doctest:: sort
|
||||
|
||||
>>> async def f():
|
||||
... cursor = collection.find().sort(
|
||||
... [("field1", pymongo.ASCENDING), ("field2", pymongo.DESCENDING)]
|
||||
... )
|
||||
... docs = await cursor.to_list()
|
||||
... print([(d["field1"], d["field2"]) for d in docs])
|
||||
... cursor = collection.find().sort([
|
||||
... ('field1', pymongo.ASCENDING),
|
||||
... ('field2', pymongo.DESCENDING)])
|
||||
...
|
||||
... docs = await cursor.to_list(None)
|
||||
... print([(d['field1'], d['field2']) for d in docs])
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
[(0, 4), (0, 2), (0, 0), (1, 3), (1, 1)]
|
||||
@ -1174,23 +1177,24 @@ Text search results can be sorted by relevance:
|
||||
.. testsetup:: sort_text
|
||||
|
||||
MongoClient().test.test_collection.drop()
|
||||
MongoClient().test.test_collection.insert_many(
|
||||
[{"field": "words"}, {"field": "words about some words"}]
|
||||
)
|
||||
MongoClient().test.test_collection.insert_many([
|
||||
{'field': 'words'},
|
||||
{'field': 'words about some words'}])
|
||||
|
||||
MongoClient().test.test_collection.create_index([("field", "text")])
|
||||
MongoClient().test.test_collection.create_index([('field', 'text')])
|
||||
collection = MotorClient().test.test_collection
|
||||
|
||||
.. doctest:: sort_text
|
||||
|
||||
>>> async def f():
|
||||
... cursor = collection.find(
|
||||
... {"$text": {"$search": "some words"}}, {"score": {"$meta": "textScore"}}
|
||||
... )
|
||||
... cursor = collection.find({
|
||||
... '$text': {'$search': 'some words'}},
|
||||
... {'score': {'$meta': 'textScore'}})
|
||||
...
|
||||
... # Sort by 'score' field.
|
||||
... cursor.sort([("score", {"$meta": "textScore"})])
|
||||
... cursor.sort([('score', {'$meta': 'textScore'})])
|
||||
... async for doc in cursor:
|
||||
... print("%.1f %s" % (doc["score"], doc["field"]))
|
||||
... print('%.1f %s' % (doc['score'], doc['field']))
|
||||
...
|
||||
>>> IOLoop.current().run_sync(f)
|
||||
1.5 words about some words
|
||||
@ -1228,10 +1232,11 @@ to initialize it, or an ``async with`` statement.
|
||||
# Or, use an "async with" statement to end the session
|
||||
# automatically.
|
||||
async with await client.start_session() as s:
|
||||
doc = {"_id": ObjectId(), "x": 1}
|
||||
doc = {'_id': ObjectId(), 'x': 1}
|
||||
await collection.insert_one(doc, session=s)
|
||||
|
||||
secondary = collection.with_options(read_preference=ReadPreference.SECONDARY)
|
||||
secondary = collection.with_options(
|
||||
read_preference=ReadPreference.SECONDARY)
|
||||
|
||||
# Sessions are causally consistent by default, so we can read
|
||||
# the doc we just inserted, even reading from a secondary.
|
||||
@ -1242,8 +1247,8 @@ to initialize it, or an ``async with`` statement.
|
||||
async with await client.start_session() as s:
|
||||
# Note, start_transaction doesn't require "await".
|
||||
async with s.start_transaction():
|
||||
await collection.delete_one({"x": 1}, session=s)
|
||||
await collection.insert_one({"x": 2}, session=s)
|
||||
await collection.delete_one({'x': 1}, session=s)
|
||||
await collection.insert_one({'x': 2}, session=s)
|
||||
|
||||
# Exiting the "with s.start_transaction()" block while throwing an
|
||||
# exception automatically aborts the transaction, exiting the block
|
||||
@ -1252,10 +1257,10 @@ to initialize it, or an ``async with`` statement.
|
||||
# You can run additional transactions in the same session, so long as
|
||||
# you run them one at a time.
|
||||
async with s.start_transaction():
|
||||
await collection.insert_one({"x": 3}, session=s)
|
||||
await collection.insert_many(
|
||||
{"x": {"$gte": 2}}, {"$inc": {"x": 1}}, session=s
|
||||
)
|
||||
await collection.insert_one({'x': 3}, session=s)
|
||||
await collection.insert_many({'x': {'$gte': 2}},
|
||||
{'$inc': {'x': 1}},
|
||||
session=s)
|
||||
|
||||
|
||||
Requires MongoDB 3.6.
|
||||
@ -1277,7 +1282,7 @@ started it.
|
||||
where_doc = """Adds a `$where`_ clause to this query.
|
||||
|
||||
The `code` argument must be an instance of :class:`str`
|
||||
:class:`~pymongo.code.Code` containing a JavaScript expression.
|
||||
:class:`~bson.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 +1298,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:`~pymongo.code.Code`
|
||||
.. note:: MongoDB 4.4 drops support for :class:`~bson.code.Code`
|
||||
with scope variables. Consider using `$expr`_ instead.
|
||||
|
||||
:Parameters:
|
||||
@ -1337,7 +1342,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:`~pymongo.errors.NoFile` if no file with file_id exists.
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists.
|
||||
|
||||
:Parameters:
|
||||
- `file_id`: The _id of the file to be deleted.
|
||||
@ -1361,7 +1366,7 @@ gridfs_download_to_stream_doc = """Downloads the contents of the stored file spe
|
||||
file.seek(0)
|
||||
contents = file.read()
|
||||
|
||||
Raises :exc:`~pymongo.errors.NoFile` if no file with file_id exists.
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists.
|
||||
|
||||
:Parameters:
|
||||
- `file_id`: The _id of the file to be downloaded.
|
||||
@ -1383,7 +1388,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:`~pymongo.errors.NoFile` if no such version of
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no such version of
|
||||
that file exists.
|
||||
|
||||
Raises :exc:`~ValueError` if `filename` is not a string.
|
||||
@ -1419,7 +1424,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:`~pymongo.errors.NoFile` if no file with file_id exists.
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists.
|
||||
|
||||
:Parameters:
|
||||
- `file_id`: The _id of the file to be downloaded.
|
||||
@ -1441,7 +1446,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:`~pymongo.errors.NoFile` if no such version of
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no such version of
|
||||
that file exists.
|
||||
|
||||
Raises :exc:`~ValueError` filename is not a string.
|
||||
@ -1484,7 +1489,7 @@ gridfs_open_upload_stream_doc = """Opens a stream for writing.
|
||||
|
||||
Returns an instance of :class:`AsyncIOMotorGridIn`.
|
||||
|
||||
Raises :exc:`~pymongo.errors.NoFile` if no such version of
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no such version of
|
||||
that file exists.
|
||||
Raises :exc:`~ValueError` if `filename` is not a string.
|
||||
|
||||
@ -1528,7 +1533,7 @@ gridfs_open_upload_stream_with_id_doc = """Opens a stream for writing.
|
||||
|
||||
Returns an instance of :class:`AsyncIOMotorGridIn`.
|
||||
|
||||
Raises :exc:`~pymongo.errors.NoFile` if no such version of
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no such version of
|
||||
that file exists.
|
||||
Raises :exc:`~ValueError` if `filename` is not a string.
|
||||
|
||||
@ -1560,7 +1565,7 @@ gridfs_rename_doc = """Renames the stored file with the specified file_id.
|
||||
|
||||
await fs.rename(file_id, "new_test_name")
|
||||
|
||||
Raises :exc:`~pymongo.errors.NoFile` if no file with file_id exists.
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists.
|
||||
|
||||
:Parameters:
|
||||
- `file_id`: The _id of the file to be renamed.
|
||||
@ -1581,7 +1586,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:`~pymongo.errors.NoFile` if no such version of
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no such version of
|
||||
that file exists.
|
||||
Raises :exc:`~ValueError` if `filename` is not a string.
|
||||
|
||||
@ -1616,7 +1621,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:`~pymongo.errors.NoFile` if no such version of
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no such version of
|
||||
that file exists.
|
||||
Raises :exc:`~ValueError` if `filename` is not a string.
|
||||
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
|
||||
See "Frameworks" in the Developer Guide.
|
||||
"""
|
||||
|
||||
|
||||
import asyncio
|
||||
import asyncio.tasks
|
||||
import functools
|
||||
@ -25,8 +27,6 @@ import warnings
|
||||
from asyncio import get_event_loop # noqa: F401 - For framework interface.
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
# mypy: ignore-errors
|
||||
|
||||
try:
|
||||
import contextvars
|
||||
except ImportError:
|
||||
@ -68,7 +68,7 @@ _EXECUTOR = ThreadPoolExecutor(max_workers=max_workers)
|
||||
|
||||
def _reset_global_executor():
|
||||
"""Re-initialize the global ThreadPoolExecutor"""
|
||||
global _EXECUTOR # noqa: PLW0603
|
||||
global _EXECUTOR
|
||||
_EXECUTOR = ThreadPoolExecutor(max_workers=max_workers)
|
||||
|
||||
|
||||
|
||||
@ -32,7 +32,6 @@ try:
|
||||
except ImportError:
|
||||
contextvars = None
|
||||
|
||||
# mypy: ignore-errors
|
||||
|
||||
CLASS_PREFIX = ""
|
||||
|
||||
@ -64,7 +63,7 @@ _EXECUTOR = ThreadPoolExecutor(max_workers=max_workers)
|
||||
|
||||
def _reset_global_executor():
|
||||
"""Re-initialize the global ThreadPoolExecutor"""
|
||||
global _EXECUTOR # noqa: PLW0603
|
||||
global _EXECUTOR
|
||||
_EXECUTOR = ThreadPoolExecutor(max_workers=max_workers)
|
||||
|
||||
|
||||
@ -149,4 +148,4 @@ def yieldable(future):
|
||||
|
||||
|
||||
def platform_info():
|
||||
return f"Tornado {tornado_version}"
|
||||
return "Tornado %s" % (tornado_version,)
|
||||
|
||||
@ -13,14 +13,12 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""Dynamic class-creation for Motor."""
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
from collections.abc import Callable
|
||||
from typing import Any, TypeVar
|
||||
from typing import Any, Callable, Dict
|
||||
|
||||
_class_cache: dict[Any, Any] = {}
|
||||
|
||||
# mypy: ignore-errors
|
||||
_class_cache: Dict[Any, Any] = {}
|
||||
|
||||
|
||||
def asynchronize(framework, sync_method: Callable, doc=None, wrap_class=None, unwrap_class=None):
|
||||
@ -123,7 +121,7 @@ def coroutine_annotation(f):
|
||||
return f
|
||||
|
||||
|
||||
class MotorAttributeFactory:
|
||||
class MotorAttributeFactory(object):
|
||||
"""Used by Motor classes to mark attributes that delegate in some way to
|
||||
PyMongo. At module import time, create_class_with_framework calls
|
||||
create_attribute() for each attr to create the final class attribute.
|
||||
@ -267,10 +265,7 @@ class MotorCursorChainingMethod(MotorAttributeFactory):
|
||||
return return_clone
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def create_class_with_framework(cls: T, framework: Any, module_name: str) -> T:
|
||||
def create_class_with_framework(cls, framework, module_name):
|
||||
motor_class_name = framework.CLASS_PREFIX + cls.__motor_class_name__
|
||||
cache_key = (cls, motor_class_name, framework)
|
||||
cached_class = _class_cache.get(cache_key)
|
||||
|
||||
@ -13,13 +13,15 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""Asyncio support for Motor, an asynchronous driver for MongoDB."""
|
||||
from typing import TypeVar
|
||||
|
||||
from . import core, motor_gridfs
|
||||
from .frameworks import asyncio as asyncio_framework
|
||||
from .metaprogramming import T, create_class_with_framework
|
||||
from .metaprogramming import create_class_with_framework
|
||||
|
||||
__all__ = [
|
||||
"AsyncIOMotorClient",
|
||||
"AsyncIOMotorClientSession",
|
||||
"AsyncIOMotorClientEncryption",
|
||||
"AsyncIOMotorDatabase",
|
||||
"AsyncIOMotorCollection",
|
||||
"AsyncIOMotorCursor",
|
||||
@ -32,6 +34,8 @@ __all__ = [
|
||||
"AsyncIOMotorClientEncryption",
|
||||
]
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def create_asyncio_class(cls: T) -> T:
|
||||
return create_class_with_framework(cls, asyncio_framework, "motor.motor_asyncio")
|
||||
|
||||
@ -1,264 +0,0 @@
|
||||
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_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
|
||||
from pymongo.write_concern import WriteConcern
|
||||
|
||||
from motor import core, motor_gridfs
|
||||
|
||||
__all__: list[str] = [
|
||||
"AsyncIOMotorClient",
|
||||
"AsyncIOMotorClientSession",
|
||||
"AsyncIOMotorDatabase",
|
||||
"AsyncIOMotorCollection",
|
||||
"AsyncIOMotorCursor",
|
||||
"AsyncIOMotorCommandCursor",
|
||||
"AsyncIOMotorChangeStream",
|
||||
"AsyncIOMotorGridFSBucket",
|
||||
"AsyncIOMotorGridIn",
|
||||
"AsyncIOMotorGridOut",
|
||||
"AsyncIOMotorGridOutCursor",
|
||||
"AsyncIOMotorClientEncryption",
|
||||
"AsyncIOMotorLatentCommandCursor",
|
||||
]
|
||||
|
||||
class AsyncIOMotorClient(core.AgnosticClient[_DocumentType]):
|
||||
def get_database(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> AsyncIOMotorDatabase[_DocumentType]: ...
|
||||
def get_default_database(
|
||||
self,
|
||||
default: Optional[str] = None,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> AsyncIOMotorDatabase[_DocumentType]: ...
|
||||
async def list_databases(
|
||||
self,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIOMotorCommandCursor[dict[str, Any]]: ...
|
||||
async def start_session(
|
||||
self,
|
||||
causal_consistency: Optional[bool] = None,
|
||||
default_transaction_options: Optional[TransactionOptions] = None,
|
||||
snapshot: Optional[bool] = False,
|
||||
) -> AsyncIOMotorClientSession: ...
|
||||
def watch(
|
||||
self,
|
||||
pipeline: Optional[_Pipeline] = None,
|
||||
full_document: Optional[str] = None,
|
||||
resume_after: Optional[Mapping[str, Any]] = None,
|
||||
max_await_time_ms: Optional[int] = None,
|
||||
batch_size: Optional[int] = None,
|
||||
collation: Optional[_CollationIn] = None,
|
||||
start_at_operation_time: Optional[Timestamp] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
start_after: Optional[Mapping[str, Any]] = None,
|
||||
comment: Optional[str] = None,
|
||||
full_document_before_change: Optional[str] = None,
|
||||
show_expanded_events: Optional[bool] = None,
|
||||
) -> AsyncIOMotorChangeStream[_DocumentType]: ...
|
||||
def __getattr__(self, name: str) -> AsyncIOMotorDatabase[_DocumentType]: ...
|
||||
def __getitem__(self, name: str) -> AsyncIOMotorDatabase[_DocumentType]: ...
|
||||
|
||||
class AsyncIOMotorClientSession(core.AgnosticClientSession):
|
||||
@property
|
||||
def client(self) -> AsyncIOMotorClient: ...
|
||||
async def __aenter__(self) -> AsyncIOMotorClientSession: ...
|
||||
|
||||
class AsyncIOMotorDatabase(core.AgnosticDatabase[_DocumentType]):
|
||||
async def cursor_command(
|
||||
self,
|
||||
command: Union[str, MutableMapping[str, Any]],
|
||||
value: Any = 1,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
codec_options: Optional[CodecOptions[core._CodecDocumentType]] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
max_await_time_ms: Optional[int] = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIOMotorCommandCursor[_DocumentType]: ...
|
||||
async def create_collection(
|
||||
self,
|
||||
name: str,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
check_exists: Optional[bool] = True,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIOMotorCollection[_DocumentType]: ...
|
||||
def get_collection(
|
||||
self,
|
||||
name: str,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> AsyncIOMotorCollection[_DocumentType]: ...
|
||||
async def list_collections(
|
||||
self,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
filter: Optional[Mapping[str, Any]] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIOMotorCommandCursor[MutableMapping[str, Any]]: ...
|
||||
def with_options(
|
||||
self,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> AsyncIOMotorDatabase[_DocumentType]: ...
|
||||
def aggregate(
|
||||
self, pipeline: _Pipeline, *args: Any, **kwargs: Any
|
||||
) -> AsyncIOMotorLatentCommandCursor[_DocumentType]: ...
|
||||
def watch(
|
||||
self,
|
||||
pipeline: Optional[_Pipeline] = None,
|
||||
full_document: Optional[str] = None,
|
||||
resume_after: Optional[Mapping[str, Any]] = None,
|
||||
max_await_time_ms: Optional[int] = None,
|
||||
batch_size: Optional[int] = None,
|
||||
collation: Optional[_CollationIn] = None,
|
||||
start_at_operation_time: Optional[Timestamp] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
start_after: Optional[Mapping[str, Any]] = None,
|
||||
comment: Optional[Any] = None,
|
||||
full_document_before_change: Optional[str] = None,
|
||||
show_expanded_events: Optional[bool] = None,
|
||||
) -> AsyncIOMotorChangeStream[_DocumentType]: ...
|
||||
@property
|
||||
def client(self) -> AsyncIOMotorClient[_DocumentType]: ...
|
||||
def __getattr__(self, name: str) -> AsyncIOMotorCollection[_DocumentType]: ...
|
||||
def __getitem__(self, name: str) -> AsyncIOMotorCollection[_DocumentType]: ...
|
||||
|
||||
class AsyncIOMotorCollection(core.AgnosticCollection[_DocumentType]):
|
||||
def with_options(
|
||||
self,
|
||||
codec_options: Optional[CodecOptions] = None,
|
||||
read_preference: Optional[ReadPreference] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> AsyncIOMotorCollection[_DocumentType]: ...
|
||||
def list_search_indexes(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIOMotorLatentCommandCursor[Mapping[str, Any]]: ...
|
||||
def __getattr__(self, name: str) -> AsyncIOMotorCollection[_DocumentType]: ...
|
||||
def __getitem__(self, name: str) -> AsyncIOMotorCollection[_DocumentType]: ...
|
||||
def find(self, *args: Any, **kwargs: Any) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def find_raw_batches(
|
||||
self, *args: Any, **kwargs: Any
|
||||
) -> AsyncIOMotorRawBatchCursor[_DocumentType]: ...
|
||||
def aggregate(
|
||||
self, pipeline: _Pipeline, *args: Any, **kwargs: Any
|
||||
) -> AsyncIOMotorCommandCursor[_DocumentType]: ...
|
||||
def aggregate_raw_batches(
|
||||
self, pipeline: _Pipeline, **kwargs: Any
|
||||
) -> AsyncIOMotorRawBatchCursor[_DocumentType]: ...
|
||||
def list_indexes(
|
||||
self, session: Optional[core.AgnosticClientSession] = None, **kwargs: Any
|
||||
) -> AsyncIOMotorLatentCommandCursor[MutableMapping[str, Any]]: ...
|
||||
|
||||
class AsyncIOMotorLatentCommandCursor(core.AgnosticLatentCommandCursor[_DocumentType]): ...
|
||||
|
||||
class AsyncIOMotorCursor(core.AgnosticCursor[_DocumentType]):
|
||||
def collation(self, collation: Optional[_CollationIn]) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def add_option(self, mask: int) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def remove_option(self, mask: int) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def limit(self, limit: int) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def skip(self, skip: int) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def max_scan(self, max_scan: Optional[int]) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def sort(
|
||||
self, key_or_list: _Hint, direction: Optional[Union[int, str]] = None
|
||||
) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def hint(self, index: Optional[_Hint]) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def where(self, code: Union[str, Code]) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def max_await_time_ms(
|
||||
self, max_await_time_ms: Optional[int]
|
||||
) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def max_time_ms(self, max_time_ms: Optional[int]) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def min(self, spec: _Sort) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def max(self, spec: _Sort) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def comment(self, comment: Any) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def allow_disk_use(self, allow_disk_use: bool) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def rewind(self) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def clone(self) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def __copy__(self) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
def __deepcopy__(self, memo: Any) -> AsyncIOMotorCursor[_DocumentType]: ...
|
||||
|
||||
class AsyncIOMotorRawBatchCursor(core.AgnosticRawBatchCursor[_DocumentType]): ...
|
||||
class AsyncIOMotorCommandCursor(core.AgnosticCommandCursor[_DocumentType]): ...
|
||||
class AsyncIOMotorRawBatchCommandCursor(core.AgnosticRawBatchCommandCursor[_DocumentType]): ...
|
||||
|
||||
class AsyncIOMotorChangeStream(core.AgnosticChangeStream[_DocumentType]):
|
||||
def __aiter__(self) -> AsyncIOMotorChangeStream[_DocumentType]: ...
|
||||
async def __aenter__(self) -> AsyncIOMotorChangeStream[_DocumentType]: ...
|
||||
|
||||
class AsyncIOMotorClientEncryption(core.AgnosticClientEncryption[_DocumentType]):
|
||||
async def __aenter__(self) -> AsyncIOMotorClientEncryption[_DocumentType]: ...
|
||||
async def get_keys(self) -> AsyncIOMotorCursor[RawBSONDocument]: ...
|
||||
async def create_encrypted_collection(
|
||||
self,
|
||||
database: core.AgnosticDatabase[_DocumentTypeArg],
|
||||
name: str,
|
||||
encrypted_fields: Mapping[str, Any],
|
||||
kms_provider: Optional[str] = None,
|
||||
master_key: Optional[Mapping[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
) -> tuple[AsyncIOMotorCollection[_DocumentTypeArg], Mapping[str, Any]]: ...
|
||||
|
||||
class AsyncIOMotorGridOutCursor(motor_gridfs.AgnosticGridOutCursor):
|
||||
def next_object(self) -> AsyncIOMotorGridOutCursor: ...
|
||||
|
||||
class AsyncIOMotorGridOut(motor_gridfs.AgnosticGridOut):
|
||||
def __aiter__(self) -> AsyncIOMotorGridOut: ...
|
||||
|
||||
class AsyncIOMotorGridIn(motor_gridfs.AgnosticGridIn):
|
||||
async def __aenter__(self) -> AsyncIOMotorGridIn: ...
|
||||
|
||||
class AsyncIOMotorGridFSBucket(motor_gridfs.AgnosticGridFSBucket):
|
||||
async def open_download_stream_by_name(
|
||||
self,
|
||||
filename: str,
|
||||
revision: int = -1,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
) -> AsyncIOMotorGridOut: ...
|
||||
async def open_download_stream(
|
||||
self, file_id: Any, session: Optional[core.AgnosticClientSession] = None
|
||||
) -> AsyncIOMotorGridOut: ...
|
||||
def open_upload_stream(
|
||||
self,
|
||||
filename: str,
|
||||
chunk_size_bytes: Optional[int] = None,
|
||||
metadata: Optional[Mapping[str, Any]] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
) -> AsyncIOMotorGridIn: ...
|
||||
def open_upload_stream_with_id(
|
||||
self,
|
||||
file_id: Any,
|
||||
filename: str,
|
||||
chunk_size_bytes: Optional[int] = None,
|
||||
metadata: Optional[Mapping[str, Any]] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
) -> AsyncIOMotorGridIn: ...
|
||||
def find(self, *args: Any, **kwargs: Any) -> AsyncIOMotorGridOutCursor: ...
|
||||
@ -13,4 +13,5 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""Common code to support all async frameworks."""
|
||||
|
||||
callback_type_error = TypeError("callback must be a callable")
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""GridFS implementation for Motor, an asynchronous driver for MongoDB."""
|
||||
|
||||
import hashlib
|
||||
import warnings
|
||||
|
||||
@ -37,6 +38,9 @@ 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.
|
||||
@ -69,11 +73,11 @@ class MotorGridOutProperty(ReadOnlyProperty):
|
||||
return property(fget=fget, doc=doc)
|
||||
|
||||
|
||||
class AgnosticGridOut:
|
||||
class AgnosticGridOut(object):
|
||||
"""Class to read data out of GridFS.
|
||||
|
||||
MotorGridOut supports the same attributes as PyMongo's
|
||||
:class:`~pymongo.grid_file.GridOut`, such as ``_id``, ``content_type``,
|
||||
:class:`~gridfs.grid_file.GridOut`, such as ``_id``, ``content_type``,
|
||||
etc.
|
||||
|
||||
You don't need to instantiate this class directly - use the
|
||||
@ -85,6 +89,7 @@ class AgnosticGridOut:
|
||||
__motor_class_name__ = "MotorGridOut"
|
||||
__delegate_class__ = gridfs.GridOut
|
||||
|
||||
_ensure_file = AsyncCommand()
|
||||
_id = MotorGridOutProperty()
|
||||
aliases = MotorGridOutProperty()
|
||||
chunk_size = MotorGridOutProperty()
|
||||
@ -94,7 +99,6 @@ class AgnosticGridOut:
|
||||
length = MotorGridOutProperty()
|
||||
metadata = MotorGridOutProperty()
|
||||
name = MotorGridOutProperty()
|
||||
_open = AsyncCommand(attr_name="open")
|
||||
read = AsyncRead()
|
||||
readable = DelegateMethod()
|
||||
readchunk = AsyncRead()
|
||||
@ -157,7 +161,7 @@ class AgnosticGridOut:
|
||||
:class:`~motor.MotorGridOut` now opens itself on demand, calling
|
||||
``open`` explicitly is rarely needed.
|
||||
"""
|
||||
return self._framework.chain_return_value(self._open(), self.get_io_loop(), self)
|
||||
return self._framework.chain_return_value(self._ensure_file(), self.get_io_loop(), self)
|
||||
|
||||
def get_io_loop(self):
|
||||
return self.io_loop
|
||||
@ -176,7 +180,7 @@ class AgnosticGridOut:
|
||||
@tornado.web.asynchronous
|
||||
@gen.coroutine
|
||||
def get(self, filename):
|
||||
db = self.settings["db"]
|
||||
db = self.settings['db']
|
||||
fs = await motor.MotorGridFSBucket(db())
|
||||
try:
|
||||
gridout = await fs.open_download_stream_by_name(filename)
|
||||
@ -202,7 +206,7 @@ class AgnosticGridOut:
|
||||
written += len(chunk)
|
||||
|
||||
|
||||
class AgnosticGridIn:
|
||||
class AgnosticGridIn(object):
|
||||
__motor_class_name__ = "MotorGridIn"
|
||||
__delegate_class__ = gridfs.GridIn
|
||||
|
||||
@ -255,7 +259,7 @@ Metadata set on the file appears as attributes on a
|
||||
arguments include:
|
||||
|
||||
- ``"_id"``: unique ID for this file (default:
|
||||
:class:`~pymongo.objectid.ObjectId`) - this ``"_id"`` must
|
||||
:class:`~bson.objectid.ObjectId`) - this ``"_id"`` must
|
||||
not have already been used for another file
|
||||
|
||||
- ``"filename"``: human name for the file
|
||||
@ -311,7 +315,7 @@ Metadata set on the file appears as attributes on a
|
||||
return self.io_loop
|
||||
|
||||
|
||||
class AgnosticGridFSBucket:
|
||||
class AgnosticGridFSBucket(object):
|
||||
__motor_class_name__ = "MotorGridFSBucket"
|
||||
__delegate_class__ = gridfs.GridFSBucket
|
||||
|
||||
@ -390,7 +394,7 @@ class AgnosticGridFSBucket:
|
||||
|
||||
if not isinstance(database, db_class):
|
||||
raise TypeError(
|
||||
f"First argument to {self.__class__} must be MotorDatabase, not {database!r}"
|
||||
"First argument to %s must be MotorDatabase, not %r" % (self.__class__, database)
|
||||
)
|
||||
|
||||
self.io_loop = database.get_io_loop()
|
||||
|
||||
@ -12,13 +12,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""GridFS type stubs for Motor, an asynchronous driver for MongoDB."""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
from collections.abc import Iterable, Mapping
|
||||
from typing import Any, NoReturn, Optional
|
||||
from typing import Any, Iterable, List, Mapping, NoReturn, Optional, Type
|
||||
|
||||
from bson import ObjectId
|
||||
from gridfs import DEFAULT_CHUNK_SIZE, GridFSBucket, GridIn, GridOut, GridOutCursor # noqa: F401
|
||||
from gridfs import DEFAULT_CHUNK_SIZE, GridFSBucket, GridIn, GridOut, GridOutCursor
|
||||
from pymongo import WriteConcern
|
||||
from pymongo.read_preferences import _ServerMode
|
||||
|
||||
@ -35,14 +36,15 @@ _SEEK_END = os.SEEK_END
|
||||
|
||||
class AgnosticGridOutCursor(AgnosticCursor):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[GridOutCursor]
|
||||
__delegate_class__ = type[GridOutCursor]
|
||||
async def _Cursor__die(self, synchronous: bool = False) -> None: ...
|
||||
def next_object(self) -> AgnosticGridOutCursor: ...
|
||||
|
||||
class AgnosticGridOut:
|
||||
class AgnosticGridOut(object):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[GridOut]
|
||||
__delegate_class__: Type[GridOut]
|
||||
_id: Any
|
||||
aliases: Optional[list[str]]
|
||||
aliases: Optional[List[str]]
|
||||
chunk_size: int
|
||||
filename: Optional[str]
|
||||
name: Optional[str]
|
||||
@ -50,13 +52,13 @@ class AgnosticGridOut:
|
||||
length: int
|
||||
upload_date: datetime.datetime
|
||||
metadata: Optional[Mapping[str, Any]]
|
||||
async def _open(self) -> None: ...
|
||||
async def _ensure_file(self) -> None: ...
|
||||
def close(self) -> None: ...
|
||||
async def read(self, size: int = -1) -> NoReturn: ...
|
||||
def readable(self) -> bool: ...
|
||||
async def readchunk(self) -> bytes: ...
|
||||
async def readline(self, size: int = -1) -> bytes: ...
|
||||
def seek(self, pos: int, whence: int = ...) -> int: ...
|
||||
def seek(self, pos: int, whence: int = _SEEK_SET) -> int: ...
|
||||
def seekable(self) -> bool: ...
|
||||
def tell(self) -> int: ...
|
||||
def write(self, data: Any) -> None: ...
|
||||
@ -75,9 +77,9 @@ class AgnosticGridOut:
|
||||
def get_io_loop(self) -> Any: ...
|
||||
async def stream_to_handler(self, request_handler: Any) -> None: ...
|
||||
|
||||
class AgnosticGridIn:
|
||||
class AgnosticGridIn(object):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[GridIn]
|
||||
__delegate_class__: Type[GridIn]
|
||||
__getattr__: Any
|
||||
_id: Any
|
||||
filename: str
|
||||
@ -96,22 +98,22 @@ class AgnosticGridIn:
|
||||
async def write(self, data: Any) -> None: ...
|
||||
def writeable(self) -> bool: ...
|
||||
async def writelines(self, sequence: Iterable[Any]) -> None: ...
|
||||
async def _exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> Any: ...
|
||||
async def _exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> Any: ...
|
||||
async def set(self, name: str, value: Any) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
root_collection: AgnosticCollection,
|
||||
delegate: Any = None,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
**kwargs: Any,
|
||||
**kwargs: Any
|
||||
) -> None: ...
|
||||
async def __aenter__(self) -> AgnosticGridIn: ...
|
||||
async def __aexit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None: ...
|
||||
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: ...
|
||||
def get_io_loop(self) -> Any: ...
|
||||
|
||||
class AgnosticGridFSBucket:
|
||||
class AgnosticGridFSBucket(object):
|
||||
__motor_class_name__: str
|
||||
__delegate_class__: type[GridFSBucket]
|
||||
__delegate_class__: Type[GridFSBucket]
|
||||
async def delete(
|
||||
self, file_id: Any, session: Optional[AgnosticClientSession] = None
|
||||
) -> None: ...
|
||||
@ -127,17 +129,17 @@ class AgnosticGridFSBucket:
|
||||
) -> None: ...
|
||||
async def open_download_stream_by_name(
|
||||
self, filename: str, revision: int = -1, session: Optional[AgnosticClientSession] = None
|
||||
) -> AgnosticGridOut: ...
|
||||
) -> GridOut: ...
|
||||
async def open_download_stream(
|
||||
self, file_id: Any, session: Optional[AgnosticClientSession] = None
|
||||
) -> AgnosticGridOut: ...
|
||||
) -> GridOut: ...
|
||||
def open_upload_stream(
|
||||
self,
|
||||
filename: str,
|
||||
chunk_size_bytes: Optional[int] = None,
|
||||
metadata: Optional[Mapping[str, Any]] = None,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
) -> AgnosticGridIn: ...
|
||||
) -> GridIn: ...
|
||||
def open_upload_stream_with_id(
|
||||
self,
|
||||
file_id: Any,
|
||||
@ -145,7 +147,7 @@ class AgnosticGridFSBucket:
|
||||
chunk_size_bytes: Optional[int] = None,
|
||||
metadata: Optional[Mapping[str, Any]] = None,
|
||||
session: Optional[AgnosticClientSession] = None,
|
||||
) -> AgnosticGridIn: ...
|
||||
) -> GridIn: ...
|
||||
async def rename(
|
||||
self, file_id: Any, new_filename: str, session: Optional[AgnosticClientSession] = None
|
||||
) -> None: ...
|
||||
@ -170,10 +172,10 @@ class AgnosticGridFSBucket:
|
||||
self,
|
||||
database: AgnosticDatabase,
|
||||
bucket_name: str = "fs",
|
||||
chunk_size_bytes: int = ...,
|
||||
chunk_size_bytes: int = DEFAULT_CHUNK_SIZE,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
collection: Optional[str] = None,
|
||||
collection: Optional[AgnosticCollection] = None,
|
||||
) -> None: ...
|
||||
def get_io_loop(self) -> Any: ...
|
||||
def wrap(self, obj: Any) -> Any: ...
|
||||
|
||||
@ -13,14 +13,15 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""Tornado support for Motor, an asynchronous driver for MongoDB."""
|
||||
from typing import TypeVar
|
||||
|
||||
from . import core, motor_gridfs
|
||||
from .frameworks import tornado as tornado_framework
|
||||
from .metaprogramming import T, create_class_with_framework
|
||||
from .metaprogramming import create_class_with_framework
|
||||
|
||||
__all__ = [
|
||||
"MotorClient",
|
||||
"MotorClientSession",
|
||||
"MotorClientEncryption",
|
||||
"MotorDatabase",
|
||||
"MotorCollection",
|
||||
"MotorCursor",
|
||||
@ -33,6 +34,8 @@ __all__ = [
|
||||
"MotorClientEncryption",
|
||||
]
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def create_motor_class(cls: T) -> T:
|
||||
return create_class_with_framework(cls, tornado_framework, "motor.motor_tornado")
|
||||
|
||||
@ -1,259 +0,0 @@
|
||||
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_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
|
||||
from pymongo.write_concern import WriteConcern
|
||||
|
||||
from motor import core, motor_gridfs
|
||||
|
||||
__all__: list[str] = [
|
||||
"MotorClient",
|
||||
"MotorClientSession",
|
||||
"MotorDatabase",
|
||||
"MotorCollection",
|
||||
"MotorCursor",
|
||||
"MotorCommandCursor",
|
||||
"MotorChangeStream",
|
||||
"MotorGridFSBucket",
|
||||
"MotorGridIn",
|
||||
"MotorGridOut",
|
||||
"MotorGridOutCursor",
|
||||
"MotorClientEncryption",
|
||||
]
|
||||
|
||||
class MotorClient(core.AgnosticClient[_DocumentType]):
|
||||
def get_database(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> MotorDatabase[_DocumentType]: ...
|
||||
def get_default_database(
|
||||
self,
|
||||
default: Optional[str] = None,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> MotorDatabase[_DocumentType]: ...
|
||||
async def list_databases(
|
||||
self,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> MotorCommandCursor[dict[str, Any]]: ...
|
||||
async def start_session(
|
||||
self,
|
||||
causal_consistency: Optional[bool] = None,
|
||||
default_transaction_options: Optional[TransactionOptions] = None,
|
||||
snapshot: Optional[bool] = False,
|
||||
) -> MotorClientSession: ...
|
||||
def watch(
|
||||
self,
|
||||
pipeline: Optional[_Pipeline] = None,
|
||||
full_document: Optional[str] = None,
|
||||
resume_after: Optional[Mapping[str, Any]] = None,
|
||||
max_await_time_ms: Optional[int] = None,
|
||||
batch_size: Optional[int] = None,
|
||||
collation: Optional[_CollationIn] = None,
|
||||
start_at_operation_time: Optional[Timestamp] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
start_after: Optional[Mapping[str, Any]] = None,
|
||||
comment: Optional[str] = None,
|
||||
full_document_before_change: Optional[str] = None,
|
||||
show_expanded_events: Optional[bool] = None,
|
||||
) -> MotorChangeStream[_DocumentType]: ...
|
||||
def __getattr__(self, name: str) -> MotorDatabase[_DocumentType]: ...
|
||||
def __getitem__(self, name: str) -> MotorDatabase[_DocumentType]: ...
|
||||
|
||||
class MotorClientSession(core.AgnosticClientSession):
|
||||
@property
|
||||
def client(self) -> MotorClient: ...
|
||||
async def __aenter__(self) -> MotorClientSession: ...
|
||||
|
||||
class MotorDatabase(core.AgnosticDatabase[_DocumentType]):
|
||||
async def cursor_command(
|
||||
self,
|
||||
command: Union[str, MutableMapping[str, Any]],
|
||||
value: Any = 1,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
codec_options: Optional[CodecOptions[core._CodecDocumentType]] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
max_await_time_ms: Optional[int] = None,
|
||||
**kwargs: Any,
|
||||
) -> MotorCommandCursor[_DocumentType]: ...
|
||||
async def create_collection(
|
||||
self,
|
||||
name: str,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
check_exists: Optional[bool] = True,
|
||||
**kwargs: Any,
|
||||
) -> MotorCollection[_DocumentType]: ...
|
||||
def get_collection(
|
||||
self,
|
||||
name: str,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> MotorCollection[_DocumentType]: ...
|
||||
async def list_collections(
|
||||
self,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
filter: Optional[Mapping[str, Any]] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> MotorCommandCursor[MutableMapping[str, Any]]: ...
|
||||
def with_options(
|
||||
self,
|
||||
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
|
||||
read_preference: Optional[_ServerMode] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> MotorDatabase[_DocumentType]: ...
|
||||
def aggregate(
|
||||
self, pipeline: _Pipeline, *args: Any, **kwargs: Any
|
||||
) -> MotorLatentCommandCursor[_DocumentType]: ...
|
||||
def watch(
|
||||
self,
|
||||
pipeline: Optional[_Pipeline] = None,
|
||||
full_document: Optional[str] = None,
|
||||
resume_after: Optional[Mapping[str, Any]] = None,
|
||||
max_await_time_ms: Optional[int] = None,
|
||||
batch_size: Optional[int] = None,
|
||||
collation: Optional[_CollationIn] = None,
|
||||
start_at_operation_time: Optional[Timestamp] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
start_after: Optional[Mapping[str, Any]] = None,
|
||||
comment: Optional[Any] = None,
|
||||
full_document_before_change: Optional[str] = None,
|
||||
show_expanded_events: Optional[bool] = None,
|
||||
) -> MotorChangeStream[_DocumentType]: ...
|
||||
@property
|
||||
def client(self) -> MotorClient[_DocumentType]: ...
|
||||
def __getattr__(self, name: str) -> MotorCollection[_DocumentType]: ...
|
||||
def __getitem__(self, name: str) -> MotorCollection[_DocumentType]: ...
|
||||
|
||||
class MotorCollection(core.AgnosticCollection[_DocumentType]):
|
||||
def with_options(
|
||||
self,
|
||||
codec_options: Optional[CodecOptions] = None,
|
||||
read_preference: Optional[ReadPreference] = None,
|
||||
write_concern: Optional[WriteConcern] = None,
|
||||
read_concern: Optional[ReadConcern] = None,
|
||||
) -> MotorCollection[_DocumentType]: ...
|
||||
def list_search_indexes(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
comment: Optional[Any] = None,
|
||||
**kwargs: Any,
|
||||
) -> MotorLatentCommandCursor[Mapping[str, Any]]: ...
|
||||
def __getattr__(self, name: str) -> MotorCollection[_DocumentType]: ...
|
||||
def __getitem__(self, name: str) -> MotorCollection[_DocumentType]: ...
|
||||
def find(self, *args: Any, **kwargs: Any) -> MotorCursor[_DocumentType]: ...
|
||||
def find_raw_batches(self, *args: Any, **kwargs: Any) -> MotorRawBatchCursor[_DocumentType]: ...
|
||||
def aggregate(
|
||||
self, pipeline: _Pipeline, *args: Any, **kwargs: Any
|
||||
) -> MotorCommandCursor[_DocumentType]: ...
|
||||
def aggregate_raw_batches(
|
||||
self, pipeline: _Pipeline, **kwargs: Any
|
||||
) -> MotorRawBatchCursor[_DocumentType]: ...
|
||||
def list_indexes(
|
||||
self, session: Optional[core.AgnosticClientSession] = None, **kwargs: Any
|
||||
) -> MotorLatentCommandCursor[MutableMapping[str, Any]]: ...
|
||||
|
||||
class MotorLatentCommandCursor(core.AgnosticLatentCommandCursor[_DocumentType]): ...
|
||||
|
||||
class MotorCursor(core.AgnosticCursor[_DocumentType]):
|
||||
def collation(self, collation: Optional[_CollationIn]) -> MotorCursor[_DocumentType]: ...
|
||||
def add_option(self, mask: int) -> MotorCursor[_DocumentType]: ...
|
||||
def remove_option(self, mask: int) -> MotorCursor[_DocumentType]: ...
|
||||
def limit(self, limit: int) -> MotorCursor[_DocumentType]: ...
|
||||
def skip(self, skip: int) -> MotorCursor[_DocumentType]: ...
|
||||
def max_scan(self, max_scan: Optional[int]) -> MotorCursor[_DocumentType]: ...
|
||||
def sort(
|
||||
self, key_or_list: _Hint, direction: Optional[Union[int, str]] = None
|
||||
) -> MotorCursor[_DocumentType]: ...
|
||||
def hint(self, index: Optional[_Hint]) -> MotorCursor[_DocumentType]: ...
|
||||
def where(self, code: Union[str, Code]) -> MotorCursor[_DocumentType]: ...
|
||||
def max_await_time_ms(self, max_await_time_ms: Optional[int]) -> MotorCursor[_DocumentType]: ...
|
||||
def max_time_ms(self, max_time_ms: Optional[int]) -> MotorCursor[_DocumentType]: ...
|
||||
def min(self, spec: _Sort) -> MotorCursor[_DocumentType]: ...
|
||||
def max(self, spec: _Sort) -> MotorCursor[_DocumentType]: ...
|
||||
def comment(self, comment: Any) -> MotorCursor[_DocumentType]: ...
|
||||
def allow_disk_use(self, allow_disk_use: bool) -> MotorCursor[_DocumentType]: ...
|
||||
def rewind(self) -> MotorCursor[_DocumentType]: ...
|
||||
def clone(self) -> MotorCursor[_DocumentType]: ...
|
||||
def __copy__(self) -> MotorCursor[_DocumentType]: ...
|
||||
def __deepcopy__(self, memo: Any) -> MotorCursor[_DocumentType]: ...
|
||||
|
||||
class MotorRawBatchCursor(core.AgnosticRawBatchCursor[_DocumentType]): ...
|
||||
class MotorCommandCursor(core.AgnosticCommandCursor[_DocumentType]): ...
|
||||
class MotorRawBatchCommandCursor(core.AgnosticRawBatchCommandCursor[_DocumentType]): ...
|
||||
|
||||
class MotorChangeStream(core.AgnosticChangeStream[_DocumentType]):
|
||||
def __aiter__(self) -> MotorChangeStream[_DocumentType]: ...
|
||||
async def __aenter__(self) -> MotorChangeStream[_DocumentType]: ...
|
||||
|
||||
class MotorClientEncryption(core.AgnosticClientEncryption[_DocumentType]):
|
||||
async def __aenter__(self) -> MotorClientEncryption[_DocumentType]: ...
|
||||
async def get_keys(self) -> MotorCursor[RawBSONDocument]: ...
|
||||
async def create_encrypted_collection(
|
||||
self,
|
||||
database: core.AgnosticDatabase[_DocumentTypeArg],
|
||||
name: str,
|
||||
encrypted_fields: Mapping[str, Any],
|
||||
kms_provider: Optional[str] = None,
|
||||
master_key: Optional[Mapping[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
) -> tuple[MotorCollection[_DocumentTypeArg], Mapping[str, Any]]: ...
|
||||
|
||||
class MotorGridOutCursor(motor_gridfs.AgnosticGridOutCursor):
|
||||
def next_object(self) -> MotorGridOutCursor: ...
|
||||
|
||||
class MotorGridOut(motor_gridfs.AgnosticGridOut):
|
||||
def __aiter__(self) -> MotorGridOut: ...
|
||||
|
||||
class MotorGridIn(motor_gridfs.AgnosticGridIn):
|
||||
async def __aenter__(self) -> MotorGridIn: ...
|
||||
|
||||
class MotorGridFSBucket(motor_gridfs.AgnosticGridFSBucket):
|
||||
async def open_download_stream_by_name(
|
||||
self,
|
||||
filename: str,
|
||||
revision: int = -1,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
) -> MotorGridOut: ...
|
||||
async def open_download_stream(
|
||||
self, file_id: Any, session: Optional[core.AgnosticClientSession] = None
|
||||
) -> MotorGridOut: ...
|
||||
def open_upload_stream(
|
||||
self,
|
||||
filename: str,
|
||||
chunk_size_bytes: Optional[int] = None,
|
||||
metadata: Optional[Mapping[str, Any]] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
) -> MotorGridIn: ...
|
||||
def open_upload_stream_with_id(
|
||||
self,
|
||||
file_id: Any,
|
||||
filename: str,
|
||||
chunk_size_bytes: Optional[int] = None,
|
||||
metadata: Optional[Mapping[str, Any]] = None,
|
||||
session: Optional[core.AgnosticClientSession] = None,
|
||||
) -> MotorGridIn: ...
|
||||
def find(self, *args: Any, **kwargs: Any) -> MotorGridOutCursor: ...
|
||||
19
motor/web.py
19
motor/web.py
@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""Utilities for using Motor with Tornado web applications."""
|
||||
|
||||
import datetime
|
||||
import email.utils
|
||||
import mimetypes
|
||||
@ -24,8 +25,6 @@ import tornado.web
|
||||
import motor
|
||||
from motor.motor_gridfs import _hash_gridout
|
||||
|
||||
# mypy: disable-error-code="no-untyped-def,no-untyped-call"
|
||||
|
||||
# TODO: this class is not a drop-in replacement for StaticFileHandler.
|
||||
# StaticFileHandler provides class method make_static_url, which appends
|
||||
# an checksum of the static file's contents. Templates thus can do
|
||||
@ -42,11 +41,9 @@ class GridFSHandler(tornado.web.RequestHandler):
|
||||
.. code-block:: python
|
||||
|
||||
db = motor.MotorClient().my_database
|
||||
application = web.Application(
|
||||
[
|
||||
(r"/static/(.*)", web.GridFSHandler, {"database": db}),
|
||||
]
|
||||
)
|
||||
application = web.Application([
|
||||
(r"/static/(.*)", web.GridFSHandler, {"database": db}),
|
||||
])
|
||||
|
||||
By default, requests' If-Modified-Since headers are honored, but no
|
||||
specific cache-control timeout is sent to clients. Thus each request for
|
||||
@ -100,7 +97,7 @@ class GridFSHandler(tornado.web.RequestHandler):
|
||||
try:
|
||||
gridout = await self.get_gridfs_file(fs, path, self.request)
|
||||
except gridfs.NoFile:
|
||||
raise tornado.web.HTTPError(404) from None
|
||||
raise tornado.web.HTTPError(404)
|
||||
|
||||
# If-Modified-Since header is only good to the second.
|
||||
modified = gridout.upload_date.replace(microsecond=0)
|
||||
@ -125,9 +122,7 @@ class GridFSHandler(tornado.web.RequestHandler):
|
||||
|
||||
if cache_time > 0:
|
||||
self.set_header(
|
||||
"Expires",
|
||||
datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
||||
+ datetime.timedelta(seconds=cache_time),
|
||||
"Expires", datetime.datetime.utcnow() + datetime.timedelta(seconds=cache_time)
|
||||
)
|
||||
self.set_header("Cache-Control", "max-age=" + str(cache_time))
|
||||
else:
|
||||
@ -140,7 +135,6 @@ 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.
|
||||
@ -181,3 +175,4 @@ class GridFSHandler(tornado.web.RequestHandler):
|
||||
|
||||
def set_extra_headers(self, path, gridout):
|
||||
"""For subclass to add extra headers to the response"""
|
||||
pass
|
||||
|
||||
11
mypy.ini
Normal file
11
mypy.ini
Normal file
@ -0,0 +1,11 @@
|
||||
[mypy]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
no_implicit_optional = true
|
||||
pretty = true
|
||||
show_error_context = true
|
||||
show_error_codes = true
|
||||
strict_equality = true
|
||||
warn_unused_configs = true
|
||||
warn_unused_ignores = true
|
||||
warn_redundant_casts = true
|
||||
171
pyproject.toml
171
pyproject.toml
@ -1,14 +1,14 @@
|
||||
[build-system]
|
||||
requires = ["hatchling>1.24","hatch-requirements-txt>=0.4.1"]
|
||||
build-backend = "hatchling.build"
|
||||
requires = ["setuptools>61.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "motor"
|
||||
dynamic = ["version", "dependencies", "optional-dependencies"]
|
||||
dynamic = ["version"]
|
||||
description = "Non-blocking MongoDB driver for Tornado or asyncio"
|
||||
readme = "README.md"
|
||||
readme = "README.rst"
|
||||
license = { file = "LICENSE" }
|
||||
requires-python = ">=3.10"
|
||||
requires-python = ">=3.7"
|
||||
authors = [
|
||||
{ name = "A. Jesse Jiryu Davis", email = "jesse@mongodb.com" },
|
||||
]
|
||||
@ -33,137 +33,50 @@ classifiers = [
|
||||
"Typing :: Typed",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"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",
|
||||
]
|
||||
dependencies = [
|
||||
"pymongo>=4.5,<5",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
aws = [
|
||||
"pymongo[aws]>=4.5,<5",
|
||||
]
|
||||
encryption = [
|
||||
"pymongo[encryption]>=4.5,<5",
|
||||
]
|
||||
gssapi = [
|
||||
"pymongo[gssapi]>=4.5,<5",
|
||||
]
|
||||
ocsp = [
|
||||
"pymongo[ocsp]>=4.5,<5",
|
||||
]
|
||||
snappy = [
|
||||
"pymongo[snappy]>=4.5,<5",
|
||||
]
|
||||
srv = [
|
||||
"pymongo[srv]>=4.5,<5",
|
||||
]
|
||||
test = [
|
||||
"pytest>=7", "mockupdb", "tornado>=5", "aiohttp", "motor[encryption]"
|
||||
]
|
||||
zstd = [
|
||||
"pymongo[zstd]>=4.5,<5",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://www.mongodb.org"
|
||||
Documentation = "https://motor.readthedocs.io"
|
||||
Source = "https://github.com/mongodb/motor"
|
||||
Tracker = "https://jira.mongodb.org/projects/MOTOR/issues"
|
||||
Homepage = "https://github.com/mongodb/motor/"
|
||||
|
||||
[tool.hatch.version]
|
||||
path = "motor/_version.py"
|
||||
validate-bump = false
|
||||
[tool.setuptools.dynamic]
|
||||
version = {attr = "motor._version.version"}
|
||||
|
||||
[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.10"
|
||||
strict = true
|
||||
pretty = true
|
||||
show_error_context = true
|
||||
show_error_codes = true
|
||||
warn_redundant_casts = true
|
||||
warn_unreachable = true
|
||||
disable_error_code = ["type-arg"]
|
||||
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
minversion = "7"
|
||||
testpaths = ["test"]
|
||||
xfail_strict = true
|
||||
log_cli_level = "INFO"
|
||||
addopts = ["-ra", "--strict-config", "--strict-markers",
|
||||
"--maxfail=10", "--durations=5", "--junitxml=xunit-results/TEST-results.xml"]
|
||||
faulthandler_timeout = 1500
|
||||
filterwarnings = [
|
||||
"error",
|
||||
"ignore:Bare functions are deprecated, use async ones:DeprecationWarning",
|
||||
"ignore:Application.make_handler:DeprecationWarning",
|
||||
"ignore:unclosed <socket.socket:ResourceWarning",
|
||||
"ignore:unclosed event loop:ResourceWarning",
|
||||
"ignore: The fetch_next property is deprecated:DeprecationWarning",
|
||||
"ignore:The next_object method is deprecated:DeprecationWarning",
|
||||
"ignore:unclosed <ssl.SSLSocket:ResourceWarning",
|
||||
"ignore:datetime.datetime.utc:DeprecationWarning",
|
||||
"ignore:GridOut property 'contentType' is deprecated and will be removed in PyMongo 5.0:DeprecationWarning",
|
||||
"ignore:GridIn property 'contentType' is deprecated and will be removed in PyMongo 5.0:DeprecationWarning",
|
||||
"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",
|
||||
# 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]
|
||||
line-length = 100
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"E", "F", "W", # flake8
|
||||
"B", # flake8-bugbear
|
||||
"I", # isort
|
||||
"ARG", # flake8-unused-arguments
|
||||
"C4", # flake8-comprehensions
|
||||
"EM", # flake8-errmsg
|
||||
"ICN", # flake8-import-conventions
|
||||
"G", # flake8-logging-format
|
||||
"PGH", # pygrep-hooks
|
||||
"PIE", # flake8-pie
|
||||
"PYI", # flake8-pyi
|
||||
"PL", # pylint
|
||||
"PT", # flake8-pytest-style
|
||||
"PTH", # flake8-use-pathlib
|
||||
"RUF", # Ruff-specific
|
||||
"S", # flake8-bandit
|
||||
"SIM", # flake8-simplify
|
||||
"T20", # flake8-print
|
||||
"UP", # pyupgrade
|
||||
"YTT", # flake8-2020
|
||||
"EXE", # flake8-executable
|
||||
]
|
||||
ignore = [
|
||||
"ARG001", # Unused function argument
|
||||
"UP007", # Use `X | Y` for type annotation
|
||||
"EM101", # Exception must not use a string literal, assign to variable first
|
||||
"EM102", # Exception must not use an f-string literal, assign to variable first
|
||||
"G004", # Logging statement uses f-string"
|
||||
"PLR", # Too many arguments to function call, etc.
|
||||
"SIM108", # Use ternary operator"
|
||||
"SIM105", # Use `contextlib.suppress(OSError)` instead of `try`-`except`-`pass`
|
||||
"ARG002", # Unused method argument:
|
||||
"S101", # Use of `assert` detected
|
||||
"RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
"PYI034", # `__aenter__` methods in classes like `AgnosticGridIn` usually return `self` at runtime
|
||||
]
|
||||
unfixable = [
|
||||
"RUF100", # Unused noqa
|
||||
"T20", # Removes print statements
|
||||
"F841", # Removes unused variables
|
||||
"F401"
|
||||
]
|
||||
exclude = []
|
||||
flake8-unused-arguments.ignore-variadic-names = true
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?)|dummy.*)$"
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"test/*.py" = ["PT009", "ARG", "E402", "PT027", "UP031",
|
||||
"B904", "C405", "SIM", "PLR", "PTH", "B018", "C4",
|
||||
"S", "E501", "T201", "E731", "F841", "F811",
|
||||
"B011", "PT015", "E721", "PYI"]
|
||||
"synchro/__init__.py" = ["F403", "B904", "F401"]
|
||||
"doc/*.py" = [ "T201", "PTH"]
|
||||
"motor/docstrings.py" = [ "E501", ]
|
||||
"doc/examples/*.py" = [ "E402", ]
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["motor"]
|
||||
|
||||
15
pytest.ini
Normal file
15
pytest.ini
Normal file
@ -0,0 +1,15 @@
|
||||
[pytest]
|
||||
testpaths =
|
||||
test
|
||||
addopts = -ra --junitxml=xunit-results/TEST-results.xml
|
||||
filterwarnings =
|
||||
error
|
||||
ignore:Bare functions are deprecated, use async ones:DeprecationWarning
|
||||
ignore:Application.make_handler:DeprecationWarning
|
||||
ignore:unclosed <socket.socket:ResourceWarning
|
||||
ignore:unclosed event loop:ResourceWarning
|
||||
ignore: The fetch_next property is deprecated:DeprecationWarning
|
||||
ignore:The next_object method is deprecated:DeprecationWarning
|
||||
ignore:unclosed <ssl.SSLSocket:ResourceWarning
|
||||
ignore:datetime.utcfromtimestamp:DeprecationWarning
|
||||
ignore:datetime.utcnow:DeprecationWarning
|
||||
@ -1 +0,0 @@
|
||||
pymongo>=4.9,<5.0
|
||||
@ -1 +0,0 @@
|
||||
pymongo[aws]>=4.5,<5
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user