diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index df8d6b25b..5a6eec7b0 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -13,27 +13,29 @@ on: tags: - 'v[0-9]+\.[0-9]+\.[0-9]+*' env: - MONGODB_3_6: 3.6.23 - MONGODB_4_0: 4.0.28 - MONGODB_4_4: 4.4 - MONGODB_5_0: "5.0" - MONGODB_6_0: "6.0" - MONGODB_7_0: "7.0" + MONGODB_4_0: "4.0.28" + MONGODB_4_2: "4.2.25" + MONGODB_4_4: "4.4.29" + MONGODB_5_0: "5.0.31" + MONGODB_6_0: "6.0.20" + MONGODB_7_0: "7.0.17" + MONGODB_8_0: "8.0.5" - PYMONGO_3_11: 3.11 - PYMONGO_3_12: 3.12 - PYMONGO_3_13: 3.13 - PYMONGO_4_0: 4.0 - PYMONGO_4_3: 4.3.3 - PYMONGO_4_4: 4.4.1 - PYMONGO_4_6: 4.6.2 - PYMONGO_4_7: 4.7.3 - PYMONGO_4_8: 4.8.0 - PYMONGO_4_9: 4.9 + PYMONGO_3_12: "3.12.3" + PYMONGO_3_13: "3.13.0" + PYMONGO_4_0: "4.0.2" + PYMONGO_4_3: "4.3.3" + PYMONGO_4_4: "4.4.1" + PYMONGO_4_6: "4.6.2" + PYMONGO_4_7: "4.7.3" + PYMONGO_4_8: "4.8.0" + PYMONGO_4_9: "4.9.2" + PYMONGO_4_10: "4.10.1" + PYMONGO_4_11: "4.11.2" - MAIN_PYTHON_VERSION: 3.9 + MAIN_PYTHON_VERSION: "3.9" - MONGOSH: 2.2.15 # Needed for MongoDB 6.0+ + MONGOSH: "2.4.2" # Needed for MongoDB 6.0+ jobs: linting: @@ -44,7 +46,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: ${{ env.MAIN_PYTHON_VERSION }} check-latest: true - run: bash .github/workflows/install_ci_python_dep.sh - run: pre-commit run -a @@ -56,14 +58,11 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.7, 3.8, 3.9, "3.10", 3.11, pypy3.9, pypy3.10] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.9", "pypy3.10"] MONGODB: [$MONGODB_4_0] - PYMONGO: [$PYMONGO_3_11] + PYMONGO: [$PYMONGO_3_12] include: - - python-version: 3.8 - MONGODB: $MONGODB_3_6 - PYMONGO: $PYMONGO_3_12 - - python-version: 3.9 + - python-version: "3.9" MONGODB: $MONGODB_4_4 PYMONGO: $PYMONGO_3_13 - python-version: "3.10" @@ -87,6 +86,21 @@ jobs: - python-version: "3.11" MONGODB: $MONGODB_7_0 PYMONGO: $PYMONGO_4_9 + - python-version: "3.12" + MONGODB: $MONGODB_7_0 + PYMONGO: $PYMONGO_4_9 + - python-version: "3.12" + MONGODB: $MONGODB_8_0 + PYMONGO: $PYMONGO_4_9 + - python-version: "3.13" + MONGODB: $MONGODB_8_0 + PYMONGO: $PYMONGO_4_9 + - python-version: "3.13" + MONGODB: $MONGODB_8_0 + PYMONGO: $PYMONGO_4_10 + - python-version: "3.13" + MONGODB: $MONGODB_8_0 + PYMONGO: $PYMONGO_4_11 steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} @@ -122,7 +136,7 @@ jobs: - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: ${{ env.MAIN_PYTHON_VERSION }} check-latest: true - name: install python dep run: | @@ -140,7 +154,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: ${{ env.MAIN_PYTHON_VERSION }} check-latest: true - name: build dummy wheel for test-pypi run: | @@ -153,10 +167,10 @@ jobs: if: github.event_name == 'create' && startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 - - name: Set up Python 3.9 + - name: Set up Python ${{ env.MAIN_PYTHON_VERSION }} uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: ${{ env.MAIN_PYTHON_VERSION }} check-latest: true # todo separate build from publish # https://stackoverflow.com/questions/59349905/which-properties-does-github-event-in-a-github-workflow-have diff --git a/.github/workflows/install_mongo.sh b/.github/workflows/install_mongo.sh index aece5f1e2..f07606bec 100644 --- a/.github/workflows/install_mongo.sh +++ b/.github/workflows/install_mongo.sh @@ -1,24 +1,50 @@ #!/bin/bash -MONGODB=$1 - -# Mongo > 4.0 follows different name convention for download links -mongo_build=mongodb-linux-x86_64-${MONGODB} - -if [[ "$MONGODB" == *"4.2"* ]]; then - mongo_build=mongodb-linux-x86_64-ubuntu1804-v${MONGODB}-latest -elif [[ "$MONGODB" == *"4.4"* ]]; then - mongo_build=mongodb-linux-x86_64-ubuntu1804-v${MONGODB}-latest -elif [[ "$MONGODB" == *"5.0"* ]]; then - mongo_build=mongodb-linux-x86_64-ubuntu1804-v${MONGODB}-latest -elif [[ "$MONGODB" == *"6.0"* ]]; then - mongo_build=mongodb-linux-x86_64-ubuntu1804-v${MONGODB}-latest -elif [[ "$MONGODB" == *"7.0"* ]]; then - mongo_build=mongodb-linux-x86_64-ubuntu2004-v${MONGODB}-latest +set -e # Exit immediately if a command exits with a non-zero status +set -u # Treat unset variables as an error + +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + echo "Example: $0 8.0.5" + exit 1 +fi + +MONGODB="$1" + +# Determine build name based on version +if [[ "${MONGODB}" =~ ^(7.0|8.0) ]]; then + mongo_build="mongodb-linux-x86_64-ubuntu2004-${MONGODB}" +elif [[ "${MONGODB}" =~ ^(4.0|4.2|4.4|5.0|6.0) ]]; then + mongo_build="mongodb-linux-x86_64-ubuntu1804-${MONGODB}" +else + echo "Error: Unsupported MongoDB version: ${MONGODB}" + usage +fi + +download_url="http://fastdl.mongodb.org/linux/${mongo_build}.tgz" +tarball="${mongo_build}.tgz" + +echo "Downloading MongoDB from ${download_url}..." +if ! wget --quiet "${download_url}"; then + echo "Error: Failed to download MongoDB." + exit 1 +fi + +echo "Extracting ${tarball}..." +if ! tar xzf "${tarball}"; then + echo "Error: Failed to extract ${tarball}" + exit 1 +fi + +mongodb_dir=$(find "${PWD}/" -type d -name "mongodb-linux-x86_64*" | head -n 1) +if [ -z "${mongodb_dir}" ]; then + echo "Error: Could not find MongoDB directory after extraction." + exit 1 fi -wget http://fastdl.mongodb.org/linux/$mongo_build.tgz -tar xzf $mongo_build.tgz +echo "MongoDB installed at: ${mongodb_dir}" +"${mongodb_dir}/bin/mongod" --version -mongodb_dir=$(find ${PWD}/ -type d -name "mongodb-linux-x86_64*") -$mongodb_dir/bin/mongod --version +# Cleanup +echo "Cleaning up..." +rm -f "${tarball}" diff --git a/.github/workflows/install_mongosh.sh b/.github/workflows/install_mongosh.sh index aded1826f..5f2258dc6 100644 --- a/.github/workflows/install_mongosh.sh +++ b/.github/workflows/install_mongosh.sh @@ -1,15 +1,56 @@ #!/bin/bash +set -e # Exit immediately if a command exits with a non-zero status +set -u # Treat unset variables as an error + +if [ $# -lt 2 ]; then + echo "Usage: $0 " + echo "Example: $0 8.0.5 2.4.2" + exit 1 +fi + MONGODB=$1 MONGOSH=$2 -if (( $(echo "$MONGODB < 6.0" | bc -l) )); then - echo "mongosh is not needed for MongoDB versions less than 6.0" - exit 0 +PLATFORM="linux-x64" + +# Extract major version explicitly +MONGODB_MAJOR_VERSION=$(echo "$MONGODB" | cut -d'.' -f1) + +# Check if MongoDB major version is greater than or equal to 6 +if [ "$MONGODB_MAJOR_VERSION" -lt 6 ]; then + echo "mongosh is not needed for MongoDB versions less than 6.0" + exit 0 fi -wget https://downloads.mongodb.com/compass/mongosh-${MONGOSH}-linux-x64.tgz -tar xzf mongosh-${MONGOSH}-linux-x64.tgz +DOWNLOAD_URL="https://downloads.mongodb.com/compass/mongosh-${MONGOSH}-${PLATFORM}.tgz" +TARBALL="mongosh-${MONGOSH}-${PLATFORM}.tgz" + +echo "Downloading mongosh ${MONGOSH} for ${PLATFORM}..." +if ! wget --quiet "$DOWNLOAD_URL"; then + echo "Failed to download mongosh. Please check the version and your internet connection." + exit 1 +fi + +echo "Extracting mongosh..." +if ! tar xzf "$TARBALL"; then + echo "Failed to extract mongosh." + rm -f "$TARBALL" + exit 1 +fi + +mongosh_dir=$(find "${PWD}/" -type d -name "mongosh-${MONGOSH}-${PLATFORM}" -print -quit) +if [ ! -d "$mongosh_dir" ]; then + echo "Failed to find extracted mongosh directory." + rm -f "$TARBALL" + exit 1 +fi + +echo "Testing mongosh installation..." +if ! "$mongosh_dir/bin/mongosh" --version; then + echo "Failed to run mongosh." + exit 1 +fi -mongosh_dir=$(find ${PWD}/ -type d -name "mongosh-${MONGOSH}-linux-x64") -$mongosh_dir/bin/mongosh --version +echo "Cleaning up..." +rm -f "$TARBALL" diff --git a/.github/workflows/start_mongo.sh b/.github/workflows/start_mongo.sh index 6e488292e..30c6525df 100644 --- a/.github/workflows/start_mongo.sh +++ b/.github/workflows/start_mongo.sh @@ -7,13 +7,17 @@ mongodb_dir=$(find ${PWD}/ -type d -name "mongodb-linux-x86_64*") mkdir $mongodb_dir/data args=(--dbpath $mongodb_dir/data --logpath $mongodb_dir/mongodb.log --fork --replSet mongoengine) -if (( $(echo "$MONGODB > 3.8" | bc -l) )); then + +# Parse version components +MAJOR=$(echo "$MONGODB" | cut -d'.' -f1) +MINOR=$(echo "$MONGODB" | cut -d'.' -f2) +if [ "$MAJOR" -gt 3 ] || ([ "$MAJOR" -eq 3 ] && [ "$MINOR" -ge 8 ]); then args+=(--setParameter maxTransactionLockRequestTimeoutMillis=1000) fi $mongodb_dir/bin/mongod "${args[@]}" -if (( $(echo "$MONGODB < 6.0" | bc -l) )); then +if [ "$MAJOR" -lt 6 ]; then mongo --verbose --eval "rs.initiate()" mongo --quiet --eval "rs.status().ok" else diff --git a/README.rst b/README.rst index d7cf6d3d6..56beb154b 100644 --- a/README.rst +++ b/README.rst @@ -35,7 +35,7 @@ an `API reference `_. Supported MongoDB Versions ========================== -MongoEngine is currently tested against MongoDB v3.6, v4.0, v4.4, v5.0, v6.0 and v7.0. Future versions +MongoEngine is currently tested against MongoDB v4.0, v4.4, v5.0, v6.0, v7.0 and v8.0. Future versions should be supported as well, but aren't actively tested at the moment. Make sure to open an issue or submit a pull request if you experience any problems with a more recent MongoDB versions. @@ -58,7 +58,7 @@ Dependencies All of the dependencies can easily be installed via `python -m pip `_. At the very least, you'll need these two packages to use MongoEngine: -- pymongo>=3.4 +- pymongo>=3.12 If you utilize a ``DateTimeField``, you might also use a more flexible date parser: diff --git a/mongoengine/mongodb_support.py b/mongoengine/mongodb_support.py index f15a72c96..ad65984f0 100644 --- a/mongoengine/mongodb_support.py +++ b/mongoengine/mongodb_support.py @@ -13,6 +13,7 @@ MONGODB_50 = (5, 0) MONGODB_60 = (6, 0) MONGODB_70 = (7, 0) +MONGODB_80 = (8, 0) def get_mongodb_version(): diff --git a/setup.py b/setup.py index a629d5b12..a19e8cab4 100644 --- a/setup.py +++ b/setup.py @@ -33,16 +33,18 @@ def get_version(version_tuple): "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Database", "Topic :: Software Development :: Libraries :: Python Modules", ] -install_require = ["pymongo>=3.4,<5.0"] +install_require = ["pymongo>=3.12,<5.0"] tests_require = [ "pytest", "pytest-cov", diff --git a/tests/document/test_indexes.py b/tests/document/test_indexes.py index 02da590cb..927bc7af6 100644 --- a/tests/document/test_indexes.py +++ b/tests/document/test_indexes.py @@ -9,6 +9,7 @@ from mongoengine.connection import get_db from mongoengine.mongodb_support import ( MONGODB_42, + MONGODB_80, get_mongodb_version, ) from mongoengine.pymongo_support import PYMONGO_VERSION @@ -450,31 +451,72 @@ class Test(Document): # Need to be explicit about covered indexes as mongoDB doesn't know if # the documents returned might have more keys in that here. - query_plan = Test.objects(id=obj.id).exclude("a").explain() - assert ( - query_plan["queryPlanner"]["winningPlan"]["inputStage"]["stage"] == "IDHACK" - ) - - query_plan = Test.objects(id=obj.id).only("id").explain() - assert ( - query_plan["queryPlanner"]["winningPlan"]["inputStage"]["stage"] == "IDHACK" - ) - mongo_db = get_mongodb_version() - query_plan = Test.objects(a=1).only("a").exclude("id").explain() - assert ( - query_plan["queryPlanner"]["winningPlan"]["inputStage"]["stage"] == "IXSCAN" - ) - - PROJECTION_STR = "PROJECTION" if mongo_db < MONGODB_42 else "PROJECTION_COVERED" - assert query_plan["queryPlanner"]["winningPlan"]["stage"] == PROJECTION_STR - - query_plan = Test.objects(a=1).explain() - assert ( - query_plan["queryPlanner"]["winningPlan"]["inputStage"]["stage"] == "IXSCAN" - ) - - assert query_plan.get("queryPlanner").get("winningPlan").get("stage") == "FETCH" + if mongo_db >= MONGODB_80: + query_plan = Test.objects(id=obj.id).exclude("a").explain() + assert ( + query_plan["queryPlanner"]["winningPlan"]["stage"] == "EXPRESS_IXSCAN" + ) + + query_plan = Test.objects(id=obj.id).only("id").explain() + assert ( + query_plan["queryPlanner"]["winningPlan"]["stage"] == "EXPRESS_IXSCAN" + ) + + query_plan = Test.objects(a=1).only("a").exclude("id").explain() + assert ( + query_plan["queryPlanner"]["winningPlan"]["inputStage"]["stage"] + == "IXSCAN" + ) + assert ( + query_plan["queryPlanner"]["winningPlan"]["stage"] + == "PROJECTION_COVERED" + ) + + query_plan = Test.objects(a=1).explain() + assert ( + query_plan["queryPlanner"]["winningPlan"]["inputStage"]["stage"] + == "IXSCAN" + ) + + assert ( + query_plan.get("queryPlanner").get("winningPlan").get("stage") + == "FETCH" + ) + elif mongo_db < MONGODB_80: + query_plan = Test.objects(id=obj.id).exclude("a").explain() + assert ( + query_plan["queryPlanner"]["winningPlan"]["inputStage"]["stage"] + == "IDHACK" + ) + + query_plan = Test.objects(id=obj.id).only("id").explain() + assert ( + query_plan["queryPlanner"]["winningPlan"]["inputStage"]["stage"] + == "IDHACK" + ) + + query_plan = Test.objects(a=1).only("a").exclude("id").explain() + assert ( + query_plan["queryPlanner"]["winningPlan"]["inputStage"]["stage"] + == "IXSCAN" + ) + + PROJECTION_STR = ( + "PROJECTION" if mongo_db < MONGODB_42 else "PROJECTION_COVERED" + ) + assert query_plan["queryPlanner"]["winningPlan"]["stage"] == PROJECTION_STR + + query_plan = Test.objects(a=1).explain() + assert ( + query_plan["queryPlanner"]["winningPlan"]["inputStage"]["stage"] + == "IXSCAN" + ) + + assert ( + query_plan.get("queryPlanner").get("winningPlan").get("stage") + == "FETCH" + ) def test_index_on_id(self): class BlogPost(Document): diff --git a/tox.ini b/tox.ini index 0db203642..2d0c63945 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,7 @@ [tox] -envlist = pypy3-{mg34,mg36,mg39,mg311,mg312,mg4,mg432,mg441,mg462,mg473,mg480,mg49} +envlist = + pypy3-{mg3123,mg3130,mg402,mg433,mg441,mg462,mg473,mg480,mg492,mg4101,mg4112} + py{39,310,311,312,313}-{mg3123,mg3130,mg402,mg433,mg441,mg462,mg473,mg480,mg492,mg4101,mg4112} skipsdist = True [testenv] @@ -7,15 +9,16 @@ commands = pytest tests/ {posargs} deps = -rrequirements-dev.txt - mg311: pymongo>=3.11,<3.12 - mg312: pymongo>=3.12,<3.13 - mg313: pymongo>=3.13,<3.14 - mg4: pymongo>=4.0,<4.1 + mg3123: pymongo>=3.12,<3.13 + mg3130: pymongo>=3.13,<3.14 + mg402: pymongo>=4.0,<4.1 mg433: pymongo>=4.3,<4.4 mg441: pymongo>=4.4,<4.5 mg462: pymongo>=4.6,<4.7 mg473: pymongo>=4.7,<4.8 mg480: pymongo>=4.8,<4.9 - mg49: pymongo>=4.9,<5.0 + mg492: pymongo>=4.9,<4.10 + mg4101: pymongo>=4.10,<4.11 + mg4112: pymongo>=4.11,<4.12 setenv = PYTHON_EGG_CACHE = {envdir}/python-eggs