diff --git a/.evergreen/generated_configs/variants.yml b/.evergreen/generated_configs/variants.yml index 9ee51899f4..928347f567 100644 --- a/.evergreen/generated_configs/variants.yml +++ b/.evergreen/generated_configs/variants.yml @@ -1,9 +1,9 @@ buildvariants: # Alternative hosts tests - - name: openssl-1.0.2-rhel7-py3.9 + - name: openssl-1.0.2-rhel7-python3.9 tasks: - name: .5.0 .standalone !.sync_async - display_name: OpenSSL 1.0.2 RHEL7 py3.9 + display_name: OpenSSL 1.0.2 RHEL7 Python3.9 run_on: - rhel79-small batchtime: 10080 @@ -48,57 +48,57 @@ buildvariants: SKIP_HATCH: "true" # Atlas connect tests - - name: atlas-connect-rhel8-py3.9 + - name: atlas-connect-rhel8-python3.9 tasks: - name: atlas-connect - display_name: Atlas connect RHEL8 py3.9 + display_name: Atlas connect RHEL8 Python3.9 run_on: - rhel87-small expansions: PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: atlas-connect-rhel8-py3.13 + - name: atlas-connect-rhel8-python3.13 tasks: - name: atlas-connect - display_name: Atlas connect RHEL8 py3.13 + display_name: Atlas connect RHEL8 Python3.13 run_on: - rhel87-small expansions: PYTHON_BINARY: /opt/python/3.13/bin/python3 # Atlas data lake tests - - name: atlas-data-lake-ubuntu-22-py3.9-auth-no-c + - name: atlas-data-lake-ubuntu-22-python3.9-auth-no-c tasks: - name: atlas-data-lake-tests - display_name: Atlas Data Lake Ubuntu-22 py3.9 Auth No C + display_name: Atlas Data Lake Ubuntu-22 Python3.9 Auth No C run_on: - ubuntu2204-small expansions: AUTH: auth NO_EXT: "1" PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: atlas-data-lake-ubuntu-22-py3.9-auth + - name: atlas-data-lake-ubuntu-22-python3.9-auth tasks: - name: atlas-data-lake-tests - display_name: Atlas Data Lake Ubuntu-22 py3.9 Auth + display_name: Atlas Data Lake Ubuntu-22 Python3.9 Auth run_on: - ubuntu2204-small expansions: AUTH: auth PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: atlas-data-lake-ubuntu-22-py3.13-auth-no-c + - name: atlas-data-lake-ubuntu-22-python3.13-auth-no-c tasks: - name: atlas-data-lake-tests - display_name: Atlas Data Lake Ubuntu-22 py3.13 Auth No C + display_name: Atlas Data Lake Ubuntu-22 Python3.13 Auth No C run_on: - ubuntu2204-small expansions: AUTH: auth NO_EXT: "1" PYTHON_BINARY: /opt/python/3.13/bin/python3 - - name: atlas-data-lake-ubuntu-22-py3.13-auth + - name: atlas-data-lake-ubuntu-22-python3.13-auth tasks: - name: atlas-data-lake-tests - display_name: Atlas Data Lake Ubuntu-22 py3.13 Auth + display_name: Atlas Data Lake Ubuntu-22 Python3.13 Auth run_on: - ubuntu2204-small expansions: @@ -106,7 +106,7 @@ buildvariants: PYTHON_BINARY: /opt/python/3.13/bin/python3 # Aws auth tests - - name: auth-aws-ubuntu-20-py3.9 + - name: auth-aws-ubuntu-20-python3.9 tasks: - name: aws-auth-test-4.4 - name: aws-auth-test-5.0 @@ -115,12 +115,12 @@ buildvariants: - name: aws-auth-test-8.0 - name: aws-auth-test-rapid - name: aws-auth-test-latest - display_name: Auth AWS Ubuntu-20 py3.9 + display_name: Auth AWS Ubuntu-20 Python3.9 run_on: - ubuntu2004-small expansions: PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: auth-aws-ubuntu-20-py3.13 + - name: auth-aws-ubuntu-20-python3.13 tasks: - name: aws-auth-test-4.4 - name: aws-auth-test-5.0 @@ -129,12 +129,12 @@ buildvariants: - name: aws-auth-test-8.0 - name: aws-auth-test-rapid - name: aws-auth-test-latest - display_name: Auth AWS Ubuntu-20 py3.13 + display_name: Auth AWS Ubuntu-20 Python3.13 run_on: - ubuntu2004-small expansions: PYTHON_BINARY: /opt/python/3.13/bin/python3 - - name: auth-aws-win64-py3.9 + - name: auth-aws-win64-python3.9 tasks: - name: aws-auth-test-4.4 - name: aws-auth-test-5.0 @@ -143,13 +143,13 @@ buildvariants: - name: aws-auth-test-8.0 - name: aws-auth-test-rapid - name: aws-auth-test-latest - display_name: Auth AWS Win64 py3.9 + display_name: Auth AWS Win64 Python3.9 run_on: - windows-64-vsMulti-small expansions: skip_ECS_auth_test: "true" PYTHON_BINARY: C:/python/Python39/python.exe - - name: auth-aws-win64-py3.13 + - name: auth-aws-win64-python3.13 tasks: - name: aws-auth-test-4.4 - name: aws-auth-test-5.0 @@ -158,13 +158,13 @@ buildvariants: - name: aws-auth-test-8.0 - name: aws-auth-test-rapid - name: aws-auth-test-latest - display_name: Auth AWS Win64 py3.13 + display_name: Auth AWS Win64 Python3.13 run_on: - windows-64-vsMulti-small expansions: skip_ECS_auth_test: "true" PYTHON_BINARY: C:/python/Python313/python.exe - - name: auth-aws-macos-py3.9 + - name: auth-aws-macos-python3.9 tasks: - name: aws-auth-test-4.4 - name: aws-auth-test-5.0 @@ -173,7 +173,7 @@ buildvariants: - name: aws-auth-test-8.0 - name: aws-auth-test-rapid - name: aws-auth-test-latest - display_name: Auth AWS macOS py3.9 + display_name: Auth AWS macOS Python3.9 run_on: - macos-14 expansions: @@ -181,7 +181,7 @@ buildvariants: skip_EC2_auth_test: "true" skip_web_identity_auth_test: "true" PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.9/bin/python3 - - name: auth-aws-macos-py3.13 + - name: auth-aws-macos-python3.13 tasks: - name: aws-auth-test-4.4 - name: aws-auth-test-5.0 @@ -190,7 +190,7 @@ buildvariants: - name: aws-auth-test-8.0 - name: aws-auth-test-rapid - name: aws-auth-test-latest - display_name: Auth AWS macOS py3.13 + display_name: Auth AWS macOS Python3.13 run_on: - macos-14 expansions: @@ -200,58 +200,58 @@ buildvariants: PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3 # Compression tests - - name: compression-snappy-rhel8-py3.9-no-c + - name: compression-snappy-rhel8-python3.9-no-c tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Compression snappy RHEL8 py3.9 No C + display_name: Compression snappy RHEL8 Python3.9 No C run_on: - rhel87-small expansions: COMPRESSORS: snappy NO_EXT: "1" PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: compression-snappy-rhel8-py3.10 + - name: compression-snappy-rhel8-python3.10 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Compression snappy RHEL8 py3.10 + display_name: Compression snappy RHEL8 Python3.10 run_on: - rhel87-small expansions: COMPRESSORS: snappy PYTHON_BINARY: /opt/python/3.10/bin/python3 - - name: compression-zlib-rhel8-py3.11-no-c + - name: compression-zlib-rhel8-python3.11-no-c tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Compression zlib RHEL8 py3.11 No C + display_name: Compression zlib RHEL8 Python3.11 No C run_on: - rhel87-small expansions: COMPRESSORS: zlib NO_EXT: "1" PYTHON_BINARY: /opt/python/3.11/bin/python3 - - name: compression-zlib-rhel8-py3.12 + - name: compression-zlib-rhel8-python3.12 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Compression zlib RHEL8 py3.12 + display_name: Compression zlib RHEL8 Python3.12 run_on: - rhel87-small expansions: COMPRESSORS: zlib PYTHON_BINARY: /opt/python/3.12/bin/python3 - - name: compression-zstd-rhel8-py3.13-no-c + - name: compression-zstd-rhel8-python3.13-no-c tasks: - name: .standalone .noauth .nossl .sync_async !.4.0 - display_name: Compression zstd RHEL8 py3.13 No C + display_name: Compression zstd RHEL8 Python3.13 No C run_on: - rhel87-small expansions: COMPRESSORS: zstd NO_EXT: "1" PYTHON_BINARY: /opt/python/3.13/bin/python3 - - name: compression-zstd-rhel8-py3.9 + - name: compression-zstd-rhel8-python3.9 tasks: - name: .standalone .noauth .nossl .sync_async !.4.0 - display_name: Compression zstd RHEL8 py3.9 + display_name: Compression zstd RHEL8 Python3.9 run_on: - rhel87-small expansions: @@ -260,7 +260,7 @@ buildvariants: - name: compression-snappy-rhel8-pypy3.9 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Compression snappy RHEL8 pypy3.9 + display_name: Compression snappy RHEL8 PyPy3.9 run_on: - rhel87-small expansions: @@ -269,7 +269,7 @@ buildvariants: - name: compression-zlib-rhel8-pypy3.10 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Compression zlib RHEL8 pypy3.10 + display_name: Compression zlib RHEL8 PyPy3.10 run_on: - rhel87-small expansions: @@ -278,7 +278,7 @@ buildvariants: - name: compression-zstd-rhel8-pypy3.9 tasks: - name: .standalone .noauth .nossl .sync_async !.4.0 - display_name: Compression zstd RHEL8 pypy3.9 + display_name: Compression zstd RHEL8 PyPy3.9 run_on: - rhel87-small expansions: @@ -286,10 +286,10 @@ buildvariants: PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 # Disable test commands tests - - name: disable-test-commands-rhel8-py3.9 + - name: disable-test-commands-rhel8-python3.9 tasks: - name: .latest .sync_async - display_name: Disable test commands RHEL8 py3.9 + display_name: Disable test commands RHEL8 Python3.9 run_on: - rhel87-small expansions: @@ -299,22 +299,22 @@ buildvariants: PYTHON_BINARY: /opt/python/3.9/bin/python3 # Doctests tests - - name: doctests-rhel8-py3.9 + - name: doctests-rhel8-python3.9 tasks: - name: doctests - display_name: Doctests RHEL8 py3.9 + display_name: Doctests RHEL8 Python3.9 run_on: - rhel87-small expansions: PYTHON_BINARY: /opt/python/3.9/bin/python3 # Encryption tests - - name: encryption-rhel8-py3.9 + - name: encryption-rhel8-python3.9 tasks: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: Encryption RHEL8 py3.9 + display_name: Encryption RHEL8 Python3.9 run_on: - rhel87-small batchtime: 10080 @@ -322,12 +322,12 @@ buildvariants: test_encryption: "true" PYTHON_BINARY: /opt/python/3.9/bin/python3 tags: [encryption_tag] - - name: encryption-rhel8-py3.13 + - name: encryption-rhel8-python3.13 tasks: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: Encryption RHEL8 py3.13 + display_name: Encryption RHEL8 Python3.13 run_on: - rhel87-small batchtime: 10080 @@ -340,7 +340,7 @@ buildvariants: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: Encryption RHEL8 pypy3.10 + display_name: Encryption RHEL8 PyPy3.10 run_on: - rhel87-small batchtime: 10080 @@ -348,12 +348,12 @@ buildvariants: test_encryption: "true" PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 tags: [encryption_tag] - - name: encryption-crypt_shared-rhel8-py3.9 + - name: encryption-crypt_shared-rhel8-python3.9 tasks: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: Encryption crypt_shared RHEL8 py3.9 + display_name: Encryption crypt_shared RHEL8 Python3.9 run_on: - rhel87-small batchtime: 10080 @@ -362,12 +362,12 @@ buildvariants: test_crypt_shared: "true" PYTHON_BINARY: /opt/python/3.9/bin/python3 tags: [encryption_tag] - - name: encryption-crypt_shared-rhel8-py3.13 + - name: encryption-crypt_shared-rhel8-python3.13 tasks: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: Encryption crypt_shared RHEL8 py3.13 + display_name: Encryption crypt_shared RHEL8 Python3.13 run_on: - rhel87-small batchtime: 10080 @@ -381,7 +381,7 @@ buildvariants: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: Encryption crypt_shared RHEL8 pypy3.10 + display_name: Encryption crypt_shared RHEL8 PyPy3.10 run_on: - rhel87-small batchtime: 10080 @@ -390,12 +390,12 @@ buildvariants: test_crypt_shared: "true" PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 tags: [encryption_tag] - - name: encryption-pyopenssl-rhel8-py3.9 + - name: encryption-pyopenssl-rhel8-python3.9 tasks: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: Encryption PyOpenSSL RHEL8 py3.9 + display_name: Encryption PyOpenSSL RHEL8 Python3.9 run_on: - rhel87-small batchtime: 10080 @@ -404,12 +404,12 @@ buildvariants: test_encryption_pyopenssl: "true" PYTHON_BINARY: /opt/python/3.9/bin/python3 tags: [encryption_tag] - - name: encryption-pyopenssl-rhel8-py3.13 + - name: encryption-pyopenssl-rhel8-python3.13 tasks: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: Encryption PyOpenSSL RHEL8 py3.13 + display_name: Encryption PyOpenSSL RHEL8 Python3.13 run_on: - rhel87-small batchtime: 10080 @@ -423,7 +423,7 @@ buildvariants: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: Encryption PyOpenSSL RHEL8 pypy3.10 + display_name: Encryption PyOpenSSL RHEL8 PyPy3.10 run_on: - rhel87-small batchtime: 10080 @@ -432,29 +432,29 @@ buildvariants: test_encryption_pyopenssl: "true" PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 tags: [encryption_tag] - - name: encryption-rhel8-py3.10 + - name: encryption-rhel8-python3.10 tasks: - name: .sharded_cluster .auth .ssl .sync_async - display_name: Encryption RHEL8 py3.10 + display_name: Encryption RHEL8 Python3.10 run_on: - rhel87-small expansions: test_encryption: "true" PYTHON_BINARY: /opt/python/3.10/bin/python3 - - name: encryption-crypt_shared-rhel8-py3.11 + - name: encryption-crypt_shared-rhel8-python3.11 tasks: - name: .replica_set .noauth .ssl .sync_async - display_name: Encryption crypt_shared RHEL8 py3.11 + display_name: Encryption crypt_shared RHEL8 Python3.11 run_on: - rhel87-small expansions: test_encryption: "true" test_crypt_shared: "true" PYTHON_BINARY: /opt/python/3.11/bin/python3 - - name: encryption-pyopenssl-rhel8-py3.12 + - name: encryption-pyopenssl-rhel8-python3.12 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Encryption PyOpenSSL RHEL8 py3.12 + display_name: Encryption PyOpenSSL RHEL8 Python3.12 run_on: - rhel87-small expansions: @@ -464,16 +464,16 @@ buildvariants: - name: encryption-rhel8-pypy3.9 tasks: - name: .sharded_cluster .auth .ssl .sync_async - display_name: Encryption RHEL8 pypy3.9 + display_name: Encryption RHEL8 PyPy3.9 run_on: - rhel87-small expansions: test_encryption: "true" PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 - - name: encryption-macos-py3.9 + - name: encryption-macos-python3.9 tasks: - name: .latest .replica_set .sync_async - display_name: Encryption macOS py3.9 + display_name: Encryption macOS Python3.9 run_on: - macos-14 batchtime: 10080 @@ -481,10 +481,10 @@ buildvariants: test_encryption: "true" PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.9/bin/python3 tags: [encryption_tag] - - name: encryption-macos-py3.13 + - name: encryption-macos-python3.13 tasks: - name: .latest .replica_set .sync_async - display_name: Encryption macOS py3.13 + display_name: Encryption macOS Python3.13 run_on: - macos-14 batchtime: 10080 @@ -492,10 +492,10 @@ buildvariants: test_encryption: "true" PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3 tags: [encryption_tag] - - name: encryption-crypt_shared-macos-py3.9 + - name: encryption-crypt_shared-macos-python3.9 tasks: - name: .latest .replica_set .sync_async - display_name: Encryption crypt_shared macOS py3.9 + display_name: Encryption crypt_shared macOS Python3.9 run_on: - macos-14 batchtime: 10080 @@ -504,10 +504,10 @@ buildvariants: test_crypt_shared: "true" PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.9/bin/python3 tags: [encryption_tag] - - name: encryption-crypt_shared-macos-py3.13 + - name: encryption-crypt_shared-macos-python3.13 tasks: - name: .latest .replica_set .sync_async - display_name: Encryption crypt_shared macOS py3.13 + display_name: Encryption crypt_shared macOS Python3.13 run_on: - macos-14 batchtime: 10080 @@ -516,10 +516,10 @@ buildvariants: test_crypt_shared: "true" PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3 tags: [encryption_tag] - - name: encryption-win64-py3.9 + - name: encryption-win64-python3.9 tasks: - name: .latest .replica_set .sync_async - display_name: Encryption Win64 py3.9 + display_name: Encryption Win64 Python3.9 run_on: - windows-64-vsMulti-small batchtime: 10080 @@ -527,10 +527,10 @@ buildvariants: test_encryption: "true" PYTHON_BINARY: C:/python/Python39/python.exe tags: [encryption_tag] - - name: encryption-win64-py3.13 + - name: encryption-win64-python3.13 tasks: - name: .latest .replica_set .sync_async - display_name: Encryption Win64 py3.13 + display_name: Encryption Win64 Python3.13 run_on: - windows-64-vsMulti-small batchtime: 10080 @@ -538,10 +538,10 @@ buildvariants: test_encryption: "true" PYTHON_BINARY: C:/python/Python313/python.exe tags: [encryption_tag] - - name: encryption-crypt_shared-win64-py3.9 + - name: encryption-crypt_shared-win64-python3.9 tasks: - name: .latest .replica_set .sync_async - display_name: Encryption crypt_shared Win64 py3.9 + display_name: Encryption crypt_shared Win64 Python3.9 run_on: - windows-64-vsMulti-small batchtime: 10080 @@ -550,10 +550,10 @@ buildvariants: test_crypt_shared: "true" PYTHON_BINARY: C:/python/Python39/python.exe tags: [encryption_tag] - - name: encryption-crypt_shared-win64-py3.13 + - name: encryption-crypt_shared-win64-python3.13 tasks: - name: .latest .replica_set .sync_async - display_name: Encryption crypt_shared Win64 py3.13 + display_name: Encryption crypt_shared Win64 Python3.13 run_on: - windows-64-vsMulti-small batchtime: 10080 @@ -564,46 +564,46 @@ buildvariants: tags: [encryption_tag] # Enterprise auth tests - - name: auth-enterprise-macos-py3.9-auth + - name: auth-enterprise-macos-python3.9-auth tasks: - name: test-enterprise-auth - display_name: Auth Enterprise macOS py3.9 Auth + display_name: Auth Enterprise macOS Python3.9 Auth run_on: - macos-14 expansions: AUTH: auth PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.9/bin/python3 - - name: auth-enterprise-rhel8-py3.10-auth + - name: auth-enterprise-rhel8-python3.10-auth tasks: - name: test-enterprise-auth - display_name: Auth Enterprise RHEL8 py3.10 Auth + display_name: Auth Enterprise RHEL8 Python3.10 Auth run_on: - rhel87-small expansions: AUTH: auth PYTHON_BINARY: /opt/python/3.10/bin/python3 - - name: auth-enterprise-rhel8-py3.11-auth + - name: auth-enterprise-rhel8-python3.11-auth tasks: - name: test-enterprise-auth - display_name: Auth Enterprise RHEL8 py3.11 Auth + display_name: Auth Enterprise RHEL8 Python3.11 Auth run_on: - rhel87-small expansions: AUTH: auth PYTHON_BINARY: /opt/python/3.11/bin/python3 - - name: auth-enterprise-rhel8-py3.12-auth + - name: auth-enterprise-rhel8-python3.12-auth tasks: - name: test-enterprise-auth - display_name: Auth Enterprise RHEL8 py3.12 Auth + display_name: Auth Enterprise RHEL8 Python3.12 Auth run_on: - rhel87-small expansions: AUTH: auth PYTHON_BINARY: /opt/python/3.12/bin/python3 - - name: auth-enterprise-win64-py3.13-auth + - name: auth-enterprise-win64-python3.13-auth tasks: - name: test-enterprise-auth - display_name: Auth Enterprise Win64 py3.13 Auth + display_name: Auth Enterprise Win64 Python3.13 Auth run_on: - windows-64-vsMulti-small expansions: @@ -612,7 +612,7 @@ buildvariants: - name: auth-enterprise-rhel8-pypy3.9-auth tasks: - name: test-enterprise-auth - display_name: Auth Enterprise RHEL8 pypy3.9 Auth + display_name: Auth Enterprise RHEL8 PyPy3.9 Auth run_on: - rhel87-small expansions: @@ -621,7 +621,7 @@ buildvariants: - name: auth-enterprise-rhel8-pypy3.10-auth tasks: - name: test-enterprise-auth - display_name: Auth Enterprise RHEL8 pypy3.10 Auth + display_name: Auth Enterprise RHEL8 PyPy3.10 Auth run_on: - rhel87-small expansions: @@ -629,10 +629,10 @@ buildvariants: PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 # Green framework tests - - name: green-eventlet-rhel8-py3.9 + - name: green-eventlet-rhel8-python3.9 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Green Eventlet RHEL8 py3.9 + display_name: Green Eventlet RHEL8 Python3.9 run_on: - rhel87-small expansions: @@ -640,10 +640,10 @@ buildvariants: AUTH: auth SSL: ssl PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: green-gevent-rhel8-py3.9 + - name: green-gevent-rhel8-python3.9 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Green Gevent RHEL8 py3.9 + display_name: Green Gevent RHEL8 Python3.9 run_on: - rhel87-small expansions: @@ -651,10 +651,10 @@ buildvariants: AUTH: auth SSL: ssl PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: green-eventlet-rhel8-py3.12 + - name: green-eventlet-rhel8-python3.12 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Green Eventlet RHEL8 py3.12 + display_name: Green Eventlet RHEL8 Python3.12 run_on: - rhel87-small expansions: @@ -662,10 +662,10 @@ buildvariants: AUTH: auth SSL: ssl PYTHON_BINARY: /opt/python/3.12/bin/python3 - - name: green-gevent-rhel8-py3.12 + - name: green-gevent-rhel8-python3.12 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Green Gevent RHEL8 py3.12 + display_name: Green Gevent RHEL8 Python3.12 run_on: - rhel87-small expansions: @@ -675,87 +675,87 @@ buildvariants: PYTHON_BINARY: /opt/python/3.12/bin/python3 # Load balancer tests - - name: load-balancer-rhel8-v6.0-py3.9 + - name: load-balancer-rhel8-v6.0-python3.9 tasks: - name: .load-balancer - display_name: Load Balancer RHEL8 v6.0 py3.9 + display_name: Load Balancer RHEL8 v6.0 Python3.9 run_on: - rhel87-small batchtime: 10080 expansions: - PYTHON_BINARY: /opt/python/3.9/bin/python3 VERSION: "6.0" - - name: load-balancer-rhel8-v7.0-py3.9 + PYTHON_BINARY: /opt/python/3.9/bin/python3 + - name: load-balancer-rhel8-v7.0-python3.9 tasks: - name: .load-balancer - display_name: Load Balancer RHEL8 v7.0 py3.9 + display_name: Load Balancer RHEL8 v7.0 Python3.9 run_on: - rhel87-small batchtime: 10080 expansions: - PYTHON_BINARY: /opt/python/3.9/bin/python3 VERSION: "7.0" - - name: load-balancer-rhel8-v8.0-py3.9 + PYTHON_BINARY: /opt/python/3.9/bin/python3 + - name: load-balancer-rhel8-v8.0-python3.9 tasks: - name: .load-balancer - display_name: Load Balancer RHEL8 v8.0 py3.9 + display_name: Load Balancer RHEL8 v8.0 Python3.9 run_on: - rhel87-small batchtime: 10080 expansions: - PYTHON_BINARY: /opt/python/3.9/bin/python3 VERSION: "8.0" - - name: load-balancer-rhel8-rapid-py3.9 + PYTHON_BINARY: /opt/python/3.9/bin/python3 + - name: load-balancer-rhel8-rapid-python3.9 tasks: - name: .load-balancer - display_name: Load Balancer RHEL8 rapid py3.9 + display_name: Load Balancer RHEL8 rapid Python3.9 run_on: - rhel87-small batchtime: 10080 expansions: - PYTHON_BINARY: /opt/python/3.9/bin/python3 VERSION: rapid - - name: load-balancer-rhel8-latest-py3.9 + PYTHON_BINARY: /opt/python/3.9/bin/python3 + - name: load-balancer-rhel8-latest-python3.9 tasks: - name: .load-balancer - display_name: Load Balancer RHEL8 latest py3.9 + display_name: Load Balancer RHEL8 latest Python3.9 run_on: - rhel87-small batchtime: 10080 expansions: - PYTHON_BINARY: /opt/python/3.9/bin/python3 VERSION: latest + PYTHON_BINARY: /opt/python/3.9/bin/python3 # Mockupdb tests - - name: mockupdb-rhel8-py3.9 + - name: mockupdb-rhel8-python3.9 tasks: - name: mockupdb - display_name: MockupDB RHEL8 py3.9 + display_name: MockupDB RHEL8 Python3.9 run_on: - rhel87-small expansions: PYTHON_BINARY: /opt/python/3.9/bin/python3 # Mod wsgi tests - - name: mod_wsgi-ubuntu-22-py3.9 + - name: mod_wsgi-ubuntu-22-python3.9 tasks: - name: mod-wsgi-standalone - name: mod-wsgi-replica-set - name: mod-wsgi-embedded-mode-standalone - name: mod-wsgi-embedded-mode-replica-set - display_name: mod_wsgi Ubuntu-22 py3.9 + display_name: mod_wsgi Ubuntu-22 Python3.9 run_on: - ubuntu2204-small expansions: MOD_WSGI_VERSION: "4" PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: mod_wsgi-ubuntu-22-py3.13 + - name: mod_wsgi-ubuntu-22-python3.13 tasks: - name: mod-wsgi-standalone - name: mod-wsgi-replica-set - name: mod-wsgi-embedded-mode-standalone - name: mod-wsgi-embedded-mode-replica-set - display_name: mod_wsgi Ubuntu-22 py3.13 + display_name: mod_wsgi Ubuntu-22 Python3.13 run_on: - ubuntu2204-small expansions: @@ -763,46 +763,46 @@ buildvariants: PYTHON_BINARY: /opt/python/3.13/bin/python3 # No c ext tests - - name: no-c-ext-rhel8-py3.9 + - name: no-c-ext-rhel8-python3.9 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: No C Ext RHEL8 py3.9 + display_name: No C Ext RHEL8 Python3.9 run_on: - rhel87-small expansions: NO_EXT: "1" PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: no-c-ext-rhel8-py3.10 + - name: no-c-ext-rhel8-python3.10 tasks: - name: .replica_set .noauth .nossl .sync_async - display_name: No C Ext RHEL8 py3.10 + display_name: No C Ext RHEL8 Python3.10 run_on: - rhel87-small expansions: NO_EXT: "1" PYTHON_BINARY: /opt/python/3.10/bin/python3 - - name: no-c-ext-rhel8-py3.11 + - name: no-c-ext-rhel8-python3.11 tasks: - name: .sharded_cluster .noauth .nossl .sync_async - display_name: No C Ext RHEL8 py3.11 + display_name: No C Ext RHEL8 Python3.11 run_on: - rhel87-small expansions: NO_EXT: "1" PYTHON_BINARY: /opt/python/3.11/bin/python3 - - name: no-c-ext-rhel8-py3.12 + - name: no-c-ext-rhel8-python3.12 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: No C Ext RHEL8 py3.12 + display_name: No C Ext RHEL8 Python3.12 run_on: - rhel87-small expansions: NO_EXT: "1" PYTHON_BINARY: /opt/python/3.12/bin/python3 - - name: no-c-ext-rhel8-py3.13 + - name: no-c-ext-rhel8-python3.13 tasks: - name: .replica_set .noauth .nossl .sync_async - display_name: No C Ext RHEL8 py3.13 + display_name: No C Ext RHEL8 Python3.13 run_on: - rhel87-small expansions: @@ -810,10 +810,10 @@ buildvariants: PYTHON_BINARY: /opt/python/3.13/bin/python3 # Ocsp tests - - name: ocsp-rhel8-v4.4-py3.9 + - name: ocsp-rhel8-v4.4-python3.9 tasks: - name: .ocsp - display_name: OCSP RHEL8 v4.4 py3.9 + display_name: OCSP RHEL8 v4.4 Python3.9 run_on: - rhel87-small batchtime: 20160 @@ -821,12 +821,12 @@ buildvariants: AUTH: noauth SSL: ssl TOPOLOGY: server - PYTHON_BINARY: /opt/python/3.9/bin/python3 VERSION: "4.4" - - name: ocsp-rhel8-v5.0-py3.10 + PYTHON_BINARY: /opt/python/3.9/bin/python3 + - name: ocsp-rhel8-v5.0-python3.10 tasks: - name: .ocsp - display_name: OCSP RHEL8 v5.0 py3.10 + display_name: OCSP RHEL8 v5.0 Python3.10 run_on: - rhel87-small batchtime: 20160 @@ -834,12 +834,12 @@ buildvariants: AUTH: noauth SSL: ssl TOPOLOGY: server - PYTHON_BINARY: /opt/python/3.10/bin/python3 VERSION: "5.0" - - name: ocsp-rhel8-v6.0-py3.11 + PYTHON_BINARY: /opt/python/3.10/bin/python3 + - name: ocsp-rhel8-v6.0-python3.11 tasks: - name: .ocsp - display_name: OCSP RHEL8 v6.0 py3.11 + display_name: OCSP RHEL8 v6.0 Python3.11 run_on: - rhel87-small batchtime: 20160 @@ -847,12 +847,12 @@ buildvariants: AUTH: noauth SSL: ssl TOPOLOGY: server - PYTHON_BINARY: /opt/python/3.11/bin/python3 VERSION: "6.0" - - name: ocsp-rhel8-v7.0-py3.12 + PYTHON_BINARY: /opt/python/3.11/bin/python3 + - name: ocsp-rhel8-v7.0-python3.12 tasks: - name: .ocsp - display_name: OCSP RHEL8 v7.0 py3.12 + display_name: OCSP RHEL8 v7.0 Python3.12 run_on: - rhel87-small batchtime: 20160 @@ -860,12 +860,12 @@ buildvariants: AUTH: noauth SSL: ssl TOPOLOGY: server - PYTHON_BINARY: /opt/python/3.12/bin/python3 VERSION: "7.0" - - name: ocsp-rhel8-v8.0-py3.13 + PYTHON_BINARY: /opt/python/3.12/bin/python3 + - name: ocsp-rhel8-v8.0-python3.13 tasks: - name: .ocsp - display_name: OCSP RHEL8 v8.0 py3.13 + display_name: OCSP RHEL8 v8.0 Python3.13 run_on: - rhel87-small batchtime: 20160 @@ -873,12 +873,12 @@ buildvariants: AUTH: noauth SSL: ssl TOPOLOGY: server - PYTHON_BINARY: /opt/python/3.13/bin/python3 VERSION: "8.0" + PYTHON_BINARY: /opt/python/3.13/bin/python3 - name: ocsp-rhel8-rapid-pypy3.9 tasks: - name: .ocsp - display_name: OCSP RHEL8 rapid pypy3.9 + display_name: OCSP RHEL8 rapid PyPy3.9 run_on: - rhel87-small batchtime: 20160 @@ -886,12 +886,12 @@ buildvariants: AUTH: noauth SSL: ssl TOPOLOGY: server - PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 VERSION: rapid + PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 - name: ocsp-rhel8-latest-pypy3.10 tasks: - name: .ocsp - display_name: OCSP RHEL8 latest pypy3.10 + display_name: OCSP RHEL8 latest PyPy3.10 run_on: - rhel87-small batchtime: 20160 @@ -899,12 +899,12 @@ buildvariants: AUTH: noauth SSL: ssl TOPOLOGY: server - PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 VERSION: latest - - name: ocsp-win64-v4.4-py3.9 + PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 + - name: ocsp-win64-v4.4-python3.9 tasks: - name: .ocsp-rsa !.ocsp-staple - display_name: OCSP Win64 v4.4 py3.9 + display_name: OCSP Win64 v4.4 Python3.9 run_on: - windows-64-vsMulti-small batchtime: 20160 @@ -912,12 +912,12 @@ buildvariants: AUTH: noauth SSL: ssl TOPOLOGY: server - PYTHON_BINARY: C:/python/Python39/python.exe VERSION: "4.4" - - name: ocsp-win64-v8.0-py3.13 + PYTHON_BINARY: C:/python/Python39/python.exe + - name: ocsp-win64-v8.0-python3.13 tasks: - name: .ocsp-rsa !.ocsp-staple - display_name: OCSP Win64 v8.0 py3.13 + display_name: OCSP Win64 v8.0 Python3.13 run_on: - windows-64-vsMulti-small batchtime: 20160 @@ -925,12 +925,12 @@ buildvariants: AUTH: noauth SSL: ssl TOPOLOGY: server - PYTHON_BINARY: C:/python/Python313/python.exe VERSION: "8.0" - - name: ocsp-macos-v4.4-py3.9 + PYTHON_BINARY: C:/python/Python313/python.exe + - name: ocsp-macos-v4.4-python3.9 tasks: - name: .ocsp-rsa !.ocsp-staple - display_name: OCSP macOS v4.4 py3.9 + display_name: OCSP macOS v4.4 Python3.9 run_on: - macos-14 batchtime: 20160 @@ -938,12 +938,12 @@ buildvariants: AUTH: noauth SSL: ssl TOPOLOGY: server - PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.9/bin/python3 VERSION: "4.4" - - name: ocsp-macos-v8.0-py3.13 + PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.9/bin/python3 + - name: ocsp-macos-v8.0-python3.13 tasks: - name: .ocsp-rsa !.ocsp-staple - display_name: OCSP macOS v8.0 py3.13 + display_name: OCSP macOS v8.0 Python3.13 run_on: - macos-14 batchtime: 20160 @@ -951,8 +951,8 @@ buildvariants: AUTH: noauth SSL: ssl TOPOLOGY: server - PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3 VERSION: "8.0" + PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3 # Oidc auth tests - name: auth-oidc-ubuntu-22 @@ -981,55 +981,55 @@ buildvariants: batchtime: 20160 # Pyopenssl tests - - name: pyopenssl-macos-py3.9 + - name: pyopenssl-macos-python3.9 tasks: - name: .replica_set .noauth .nossl .sync_async - name: .7.0 .noauth .nossl .sync_async - display_name: PyOpenSSL macOS py3.9 + display_name: PyOpenSSL macOS Python3.9 run_on: - macos-14 batchtime: 10080 expansions: test_pyopenssl: "true" PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.9/bin/python3 - - name: pyopenssl-rhel8-py3.10 + - name: pyopenssl-rhel8-python3.10 tasks: - name: .replica_set .auth .ssl .sync_async - name: .7.0 .auth .ssl .sync_async - display_name: PyOpenSSL RHEL8 py3.10 + display_name: PyOpenSSL RHEL8 Python3.10 run_on: - rhel87-small batchtime: 10080 expansions: test_pyopenssl: "true" PYTHON_BINARY: /opt/python/3.10/bin/python3 - - name: pyopenssl-rhel8-py3.11 + - name: pyopenssl-rhel8-python3.11 tasks: - name: .replica_set .auth .ssl .sync_async - name: .7.0 .auth .ssl .sync_async - display_name: PyOpenSSL RHEL8 py3.11 + display_name: PyOpenSSL RHEL8 Python3.11 run_on: - rhel87-small batchtime: 10080 expansions: test_pyopenssl: "true" PYTHON_BINARY: /opt/python/3.11/bin/python3 - - name: pyopenssl-rhel8-py3.12 + - name: pyopenssl-rhel8-python3.12 tasks: - name: .replica_set .auth .ssl .sync_async - name: .7.0 .auth .ssl .sync_async - display_name: PyOpenSSL RHEL8 py3.12 + display_name: PyOpenSSL RHEL8 Python3.12 run_on: - rhel87-small batchtime: 10080 expansions: test_pyopenssl: "true" PYTHON_BINARY: /opt/python/3.12/bin/python3 - - name: pyopenssl-win64-py3.13 + - name: pyopenssl-win64-python3.13 tasks: - name: .replica_set .auth .ssl .sync_async - name: .7.0 .auth .ssl .sync_async - display_name: PyOpenSSL Win64 py3.13 + display_name: PyOpenSSL Win64 Python3.13 run_on: - windows-64-vsMulti-small batchtime: 10080 @@ -1040,7 +1040,7 @@ buildvariants: tasks: - name: .replica_set .auth .ssl .sync_async - name: .7.0 .auth .ssl .sync_async - display_name: PyOpenSSL RHEL8 pypy3.9 + display_name: PyOpenSSL RHEL8 PyPy3.9 run_on: - rhel87-small batchtime: 10080 @@ -1051,7 +1051,7 @@ buildvariants: tasks: - name: .replica_set .auth .ssl .sync_async - name: .7.0 .auth .ssl .sync_async - display_name: PyOpenSSL RHEL8 pypy3.10 + display_name: PyOpenSSL RHEL8 PyPy3.10 run_on: - rhel87-small batchtime: 10080 @@ -1060,34 +1060,34 @@ buildvariants: PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 # Search index tests - - name: search-index-helpers-rhel8-py3.9 + - name: search-index-helpers-rhel8-python3.9 tasks: - name: test_atlas_task_group_search_indexes - display_name: Search Index Helpers RHEL8 py3.9 + display_name: Search Index Helpers RHEL8 Python3.9 run_on: - rhel87-small expansions: PYTHON_BINARY: /opt/python/3.9/bin/python3 # Server tests - - name: test-rhel8-py3.9-cov + - name: test-rhel8-python3.9-cov tasks: - name: .standalone .sync_async - name: .replica_set .sync_async - name: .sharded_cluster .sync_async - display_name: "* Test RHEL8 py3.9 cov" + display_name: "* Test RHEL8 Python3.9 cov" run_on: - rhel87-small expansions: COVERAGE: coverage PYTHON_BINARY: /opt/python/3.9/bin/python3 tags: [coverage_tag] - - name: test-rhel8-py3.13-cov + - name: test-rhel8-python3.13-cov tasks: - name: .standalone .sync_async - name: .replica_set .sync_async - name: .sharded_cluster .sync_async - display_name: "* Test RHEL8 py3.13 cov" + display_name: "* Test RHEL8 Python3.13 cov" run_on: - rhel87-small expansions: @@ -1099,41 +1099,41 @@ buildvariants: - name: .standalone .sync_async - name: .replica_set .sync_async - name: .sharded_cluster .sync_async - display_name: "* Test RHEL8 pypy3.10 cov" + display_name: "* Test RHEL8 PyPy3.10 cov" run_on: - rhel87-small expansions: COVERAGE: coverage PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 tags: [coverage_tag] - - name: test-rhel8-py3.10 + - name: test-rhel8-python3.10 tasks: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: "* Test RHEL8 py3.10" + display_name: "* Test RHEL8 Python3.10" run_on: - rhel87-small expansions: COVERAGE: coverage PYTHON_BINARY: /opt/python/3.10/bin/python3 - - name: test-rhel8-py3.11 + - name: test-rhel8-python3.11 tasks: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: "* Test RHEL8 py3.11" + display_name: "* Test RHEL8 Python3.11" run_on: - rhel87-small expansions: COVERAGE: coverage PYTHON_BINARY: /opt/python/3.11/bin/python3 - - name: test-rhel8-py3.12 + - name: test-rhel8-python3.12 tasks: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: "* Test RHEL8 py3.12" + display_name: "* Test RHEL8 Python3.12" run_on: - rhel87-small expansions: @@ -1144,35 +1144,33 @@ buildvariants: - name: .sharded_cluster .auth .ssl .sync_async - name: .replica_set .noauth .ssl .sync_async - name: .standalone .noauth .nossl .sync_async - display_name: "* Test RHEL8 pypy3.9" + display_name: "* Test RHEL8 PyPy3.9" run_on: - rhel87-small expansions: COVERAGE: coverage PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 - - name: test-macos-py3.9 + - name: test-macos-python3.9 tasks: - name: .sharded_cluster .auth .ssl !.sync_async - name: .replica_set .noauth .ssl !.sync_async - name: .standalone .noauth .nossl !.sync_async - display_name: "* Test macOS py3.9" + display_name: "* Test macOS Python3.9" run_on: - macos-14 expansions: - SKIP_CSOT_TESTS: "true" PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.9/bin/python3 - - name: test-macos-py3.13 + - name: test-macos-python3.13 tasks: - name: .sharded_cluster .auth .ssl !.sync_async - name: .replica_set .noauth .ssl !.sync_async - name: .standalone .noauth .nossl !.sync_async - display_name: "* Test macOS py3.13" + display_name: "* Test macOS Python3.13" run_on: - macos-14 expansions: - SKIP_CSOT_TESTS: "true" PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3 - - name: test-macos-arm64-py3.9 + - name: test-macos-arm64-python3.9 tasks: - name: .sharded_cluster .auth .ssl .6.0 !.sync_async - name: .replica_set .noauth .ssl .6.0 !.sync_async @@ -1189,13 +1187,12 @@ buildvariants: - name: .sharded_cluster .auth .ssl .latest !.sync_async - name: .replica_set .noauth .ssl .latest !.sync_async - name: .standalone .noauth .nossl .latest !.sync_async - display_name: "* Test macOS Arm64 py3.9" + display_name: "* Test macOS Arm64 Python3.9" run_on: - macos-14-arm64 expansions: - SKIP_CSOT_TESTS: "true" PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.9/bin/python3 - - name: test-macos-arm64-py3.13 + - name: test-macos-arm64-python3.13 tasks: - name: .sharded_cluster .auth .ssl .6.0 !.sync_async - name: .replica_set .noauth .ssl .6.0 !.sync_async @@ -1212,62 +1209,57 @@ buildvariants: - name: .sharded_cluster .auth .ssl .latest !.sync_async - name: .replica_set .noauth .ssl .latest !.sync_async - name: .standalone .noauth .nossl .latest !.sync_async - display_name: "* Test macOS Arm64 py3.13" + display_name: "* Test macOS Arm64 Python3.13" run_on: - macos-14-arm64 expansions: - SKIP_CSOT_TESTS: "true" PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3 - - name: test-win64-py3.9 + - name: test-win64-python3.9 tasks: - name: .sharded_cluster .auth .ssl !.sync_async - name: .replica_set .noauth .ssl !.sync_async - name: .standalone .noauth .nossl !.sync_async - display_name: "* Test Win64 py3.9" + display_name: "* Test Win64 Python3.9" run_on: - windows-64-vsMulti-small expansions: - SKIP_CSOT_TESTS: "true" PYTHON_BINARY: C:/python/Python39/python.exe - - name: test-win64-py3.13 + - name: test-win64-python3.13 tasks: - name: .sharded_cluster .auth .ssl !.sync_async - name: .replica_set .noauth .ssl !.sync_async - name: .standalone .noauth .nossl !.sync_async - display_name: "* Test Win64 py3.13" + display_name: "* Test Win64 Python3.13" run_on: - windows-64-vsMulti-small expansions: - SKIP_CSOT_TESTS: "true" PYTHON_BINARY: C:/python/Python313/python.exe - - name: test-win32-py3.9 + - name: test-win32-python3.9 tasks: - name: .sharded_cluster .auth .ssl !.sync_async - name: .replica_set .noauth .ssl !.sync_async - name: .standalone .noauth .nossl !.sync_async - display_name: "* Test Win32 py3.9" + display_name: "* Test Win32 Python3.9" run_on: - windows-64-vsMulti-small expansions: - SKIP_CSOT_TESTS: "true" PYTHON_BINARY: C:/python/32/Python39/python.exe - - name: test-win32-py3.13 + - name: test-win32-python3.13 tasks: - name: .sharded_cluster .auth .ssl !.sync_async - name: .replica_set .noauth .ssl !.sync_async - name: .standalone .noauth .nossl !.sync_async - display_name: "* Test Win32 py3.13" + display_name: "* Test Win32 Python3.13" run_on: - windows-64-vsMulti-small expansions: - SKIP_CSOT_TESTS: "true" PYTHON_BINARY: C:/python/32/Python313/python.exe # Serverless tests - - name: serverless-rhel8-py3.9 + - name: serverless-rhel8-python3.9 tasks: - name: serverless_task_group - display_name: Serverless RHEL8 py3.9 + display_name: Serverless RHEL8 Python3.9 run_on: - rhel87-small batchtime: 10080 @@ -1276,10 +1268,10 @@ buildvariants: AUTH: auth SSL: ssl PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: serverless-rhel8-py3.13 + - name: serverless-rhel8-python3.13 tasks: - name: serverless_task_group - display_name: Serverless RHEL8 py3.13 + display_name: Serverless RHEL8 Python3.13 run_on: - rhel87-small batchtime: 10080 @@ -1290,7 +1282,7 @@ buildvariants: PYTHON_BINARY: /opt/python/3.13/bin/python3 # Stable api tests - - name: stable-api-require-v1-rhel8-py3.9-auth + - name: stable-api-require-v1-rhel8-python3.9-auth tasks: - name: .standalone .5.0 .noauth .nossl .sync_async - name: .standalone .6.0 .noauth .nossl .sync_async @@ -1298,7 +1290,7 @@ buildvariants: - name: .standalone .8.0 .noauth .nossl .sync_async - name: .standalone .rapid .noauth .nossl .sync_async - name: .standalone .latest .noauth .nossl .sync_async - display_name: Stable API require v1 RHEL8 py3.9 Auth + display_name: Stable API require v1 RHEL8 Python3.9 Auth run_on: - rhel87-small expansions: @@ -1307,7 +1299,7 @@ buildvariants: MONGODB_API_VERSION: "1" PYTHON_BINARY: /opt/python/3.9/bin/python3 tags: [versionedApi_tag] - - name: stable-api-accept-v2-rhel8-py3.9-auth + - name: stable-api-accept-v2-rhel8-python3.9-auth tasks: - name: .standalone .5.0 .noauth .nossl .sync_async - name: .standalone .6.0 .noauth .nossl .sync_async @@ -1315,7 +1307,7 @@ buildvariants: - name: .standalone .8.0 .noauth .nossl .sync_async - name: .standalone .rapid .noauth .nossl .sync_async - name: .standalone .latest .noauth .nossl .sync_async - display_name: Stable API accept v2 RHEL8 py3.9 Auth + display_name: Stable API accept v2 RHEL8 Python3.9 Auth run_on: - rhel87-small expansions: @@ -1323,7 +1315,7 @@ buildvariants: ORCHESTRATION_FILE: versioned-api-testing.json PYTHON_BINARY: /opt/python/3.9/bin/python3 tags: [versionedApi_tag] - - name: stable-api-require-v1-rhel8-py3.13-auth + - name: stable-api-require-v1-rhel8-python3.13-auth tasks: - name: .standalone .5.0 .noauth .nossl .sync_async - name: .standalone .6.0 .noauth .nossl .sync_async @@ -1331,7 +1323,7 @@ buildvariants: - name: .standalone .8.0 .noauth .nossl .sync_async - name: .standalone .rapid .noauth .nossl .sync_async - name: .standalone .latest .noauth .nossl .sync_async - display_name: Stable API require v1 RHEL8 py3.13 Auth + display_name: Stable API require v1 RHEL8 Python3.13 Auth run_on: - rhel87-small expansions: @@ -1340,7 +1332,7 @@ buildvariants: MONGODB_API_VERSION: "1" PYTHON_BINARY: /opt/python/3.13/bin/python3 tags: [versionedApi_tag] - - name: stable-api-accept-v2-rhel8-py3.13-auth + - name: stable-api-accept-v2-rhel8-python3.13-auth tasks: - name: .standalone .5.0 .noauth .nossl .sync_async - name: .standalone .6.0 .noauth .nossl .sync_async @@ -1348,7 +1340,7 @@ buildvariants: - name: .standalone .8.0 .noauth .nossl .sync_async - name: .standalone .rapid .noauth .nossl .sync_async - name: .standalone .latest .noauth .nossl .sync_async - display_name: Stable API accept v2 RHEL8 py3.13 Auth + display_name: Stable API accept v2 RHEL8 Python3.13 Auth run_on: - rhel87-small expansions: @@ -1358,7 +1350,7 @@ buildvariants: tags: [versionedApi_tag] # Storage engine tests - - name: storage-inmemory-rhel8-py3.9 + - name: storage-inmemory-rhel8-python3.9 tasks: - name: .standalone .noauth .nossl .4.0 .sync_async - name: .standalone .noauth .nossl .4.4 .sync_async @@ -1368,17 +1360,17 @@ buildvariants: - name: .standalone .noauth .nossl .8.0 .sync_async - name: .standalone .noauth .nossl .rapid .sync_async - name: .standalone .noauth .nossl .latest .sync_async - display_name: Storage InMemory RHEL8 py3.9 + display_name: Storage InMemory RHEL8 Python3.9 run_on: - rhel87-small expansions: STORAGE_ENGINE: inmemory PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: storage-mmapv1-rhel8-py3.9 + - name: storage-mmapv1-rhel8-python3.9 tasks: - name: .standalone .4.0 .noauth .nossl .sync_async - name: .replica_set .4.0 .noauth .nossl .sync_async - display_name: Storage MMAPv1 RHEL8 py3.9 + display_name: Storage MMAPv1 RHEL8 Python3.9 run_on: - rhel87-small expansions: diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index 0c9c8bb03a..98d400037c 100644 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -46,6 +46,11 @@ export PROJECT="$project" export PIP_QUIET=1 EOT +# Skip CSOT tests on non-linux platforms. +if [ "$(uname -s)" != "Linux" ]; then + echo "export SKIP_CSOT_TESTS=1" >> $SCRIPT_DIR/env.sh +fi + # Add these expansions to make it easier to call out tests scripts from the EVG yaml cat < expansion.yml DRIVERS_TOOLS: "$DRIVERS_TOOLS" diff --git a/.evergreen/scripts/generate_config.py b/.evergreen/scripts/generate_config.py index 6d614a9afe..210a403d39 100644 --- a/.evergreen/scripts/generate_config.py +++ b/.evergreen/scripts/generate_config.py @@ -48,13 +48,26 @@ class Host: name: str run_on: str display_name: str + variables: dict[str, str] | None -HOSTS["rhel8"] = Host("rhel8", "rhel87-small", "RHEL8") -HOSTS["win64"] = Host("win64", "windows-64-vsMulti-small", "Win64") -HOSTS["win32"] = Host("win32", "windows-64-vsMulti-small", "Win32") -HOSTS["macos"] = Host("macos", "macos-14", "macOS") -HOSTS["macos-arm64"] = Host("macos-arm64", "macos-14-arm64", "macOS Arm64") +# Hosts with toolchains. +HOSTS["rhel8"] = Host("rhel8", "rhel87-small", "RHEL8", dict()) +HOSTS["win64"] = Host("win64", "windows-64-vsMulti-small", "Win64", dict()) +HOSTS["win32"] = Host("win32", "windows-64-vsMulti-small", "Win32", dict()) +HOSTS["macos"] = Host("macos", "macos-14", "macOS", dict()) +HOSTS["macos-arm64"] = Host("macos-arm64", "macos-14-arm64", "macOS Arm64", dict()) +HOSTS["ubuntu20"] = Host("ubuntu20", "ubuntu2004-small", "Ubuntu-20", dict()) +HOSTS["ubuntu22"] = Host("ubuntu22", "ubuntu2204-small", "Ubuntu-22", dict()) +HOSTS["rhel7"] = Host("rhel7", "rhel79-small", "RHEL7", dict()) +DEFAULT_HOST = HOSTS["rhel8"] + +# Other hosts +OTHER_HOSTS = ["RHEL9-FIPS", "RHEL8-zseries", "RHEL8-POWER8", "RHEL8-arm64"] +for name, run_on in zip( + OTHER_HOSTS, ["rhel92-fips", "rhel8-zseries-small", "rhel8-power-small", "rhel82-arm64-small"] +): + HOSTS[name] = Host(name, run_on, name, dict()) ############## @@ -62,59 +75,102 @@ class Host: ############## -def create_variant( +def create_variant_generic( task_names: list[str], display_name: str, *, - python: str | None = None, - version: str | None = None, - host: str | None = None, + host: Host | None = None, + default_run_on="rhel87-small", + expansions: dict | None = None, **kwargs: Any, ) -> BuildVariant: """Create a build variant for the given inputs.""" task_refs = [EvgTaskRef(name=n) for n in task_names] - kwargs.setdefault("expansions", dict()) - expansions = kwargs.pop("expansions", dict()).copy() - host = host or "rhel8" - run_on = [HOSTS[host].run_on] - name = display_name.replace(" ", "-").lower() - if python: - expansions["PYTHON_BINARY"] = get_python_binary(python, host) - if version: - expansions["VERSION"] = version - expansions = expansions or None + expansions = expansions and expansions.copy() or dict() + if "run_on" in kwargs: + run_on = kwargs.pop("run_on") + elif host: + run_on = [host.run_on] + if host.variables: + expansions.update(host.variables) + else: + run_on = [default_run_on] + if isinstance(run_on, str): + run_on = [run_on] + name = display_name.replace(" ", "-").replace("*-", "").lower() return BuildVariant( name=name, display_name=display_name, tasks=task_refs, - expansions=expansions, + expansions=expansions or None, run_on=run_on, **kwargs, ) -def get_python_binary(python: str, host: str) -> str: +def create_variant( + task_names: list[str], + display_name: str, + *, + version: str | None = None, + host: Host | None = None, + python: str | None = None, + expansions: dict | None = None, + **kwargs: Any, +) -> BuildVariant: + expansions = expansions and expansions.copy() or dict() + if version: + expansions["VERSION"] = version + if python: + expansions["PYTHON_BINARY"] = get_python_binary(python, host) + return create_variant_generic( + task_names, display_name, version=version, host=host, expansions=expansions, **kwargs + ) + + +def get_python_binary(python: str, host: Host) -> str: """Get the appropriate python binary given a python version and host.""" - if host in ["win64", "win32"]: - if host == "win32": + name = host.name + if name in ["win64", "win32"]: + if name == "win32": base = "C:/python/32" else: base = "C:/python" python = python.replace(".", "") return f"{base}/Python{python}/python.exe" - if host == "rhel8": + if name in ["rhel8", "ubuntu22", "ubuntu20", "rhel7"]: return f"/opt/python/{python}/bin/python3" - if host in ["macos", "macos-arm64"]: + if name in ["macos", "macos-arm64"]: return f"/Library/Frameworks/Python.Framework/Versions/{python}/bin/python3" - raise ValueError(f"no match found for python {python} on {host}") + raise ValueError(f"no match found for python {python} on {name}") + + +def get_versions_from(min_version: str) -> list[str]: + """Get all server versions starting from a minimum version.""" + min_version_float = float(min_version) + rapid_latest = ["rapid", "latest"] + versions = [v for v in ALL_VERSIONS if v not in rapid_latest] + return [v for v in versions if float(v) >= min_version_float] + rapid_latest -def get_display_name(base: str, host: str, **kwargs) -> str: +def get_versions_until(max_version: str) -> list[str]: + """Get all server version up to a max version.""" + max_version_float = float(max_version) + versions = [v for v in ALL_VERSIONS if v not in ["rapid", "latest"]] + versions = [v for v in versions if float(v) <= max_version_float] + if not len(versions): + raise ValueError(f"No server versions found less <= {max_version}") + return versions + + +def get_display_name(base: str, host: Host | None = None, **kwargs) -> str: """Get the display name of a variant.""" - display_name = f"{base} {HOSTS[host].display_name}" + display_name = base + if host is not None: + display_name += f" {host.display_name}" version = kwargs.pop("VERSION", None) if version: if version not in ["rapid", "latest"]: @@ -124,7 +180,9 @@ def get_display_name(base: str, host: str, **kwargs) -> str: name = value if key.lower() == "python": if not value.startswith("pypy"): - name = f"py{value}" + name = f"Python{value}" + else: + name = f"PyPy{value.replace('pypy', '')}" elif key.lower() in DISPLAY_LOOKUP: name = DISPLAY_LOOKUP[key.lower()][value] else: @@ -166,10 +224,10 @@ def create_ocsp_variants() -> list[BuildVariant]: expansions = dict(AUTH="noauth", SSL="ssl", TOPOLOGY="server") base_display = "OCSP test" - # OCSP tests on rhel8 with all servers v4.4+ and all python versions. + # OCSP tests on default host with all servers v4.4+ and all python versions. versions = [v for v in ALL_VERSIONS if v != "4.0"] for version, python in zip_cycle(versions, ALL_PYTHONS): - host = "rhel8" + host = DEFAULT_HOST variant = create_variant( [".ocsp"], get_display_name(base_display, host, version, python), @@ -183,7 +241,8 @@ def create_ocsp_variants() -> list[BuildVariant]: # OCSP tests on Windows and MacOS. # MongoDB servers on these hosts do not staple OCSP responses and only support RSA. - for host, version in product(["win64", "macos"], ["4.4", "8.0"]): + for host_name, version in product(["win64", "macos"], ["4.4", "8.0"]): + host = HOSTS[host_name] python = CPYTHONS[0] if version == "4.4" else CPYTHONS[-1] variant = create_variant( [".ocsp-rsa !.ocsp-staple"], @@ -203,11 +262,12 @@ def create_server_variants() -> list[BuildVariant]: variants = [] # Run the full matrix on linux with min and max CPython, and latest pypy. - host = "rhel8" - for python, (auth, ssl) in product([*MIN_MAX_PYTHON, PYPYS[-1]], AUTH_SSLS): - display_name = f"Test {host}" - expansions = dict(AUTH=auth, SSL=ssl, COVERAGE="coverage") - display_name = get_display_name("Test", host, python=python, **expansions) + host = DEFAULT_HOST + # Prefix the display name with an asterisk so it is sorted first. + base_display_name = "* Test" + for python in [*MIN_MAX_PYTHON, PYPYS[-1]]: + expansions = dict(COVERAGE="coverage") + display_name = get_display_name(base_display_name, host, python=python, **expansions) variant = create_variant( [f".{t}" for t in TOPOLOGIES], display_name, @@ -235,24 +295,17 @@ def create_server_variants() -> list[BuildVariant]: variants.append(variant) # Test a subset on each of the other platforms. - for host in ("macos", "macos-arm64", "win64", "win32"): - for (python, (auth, ssl), topology), sync in product( - zip_cycle(MIN_MAX_PYTHON, AUTH_SSLS, TOPOLOGIES), SYNCS - ): - test_suite = "default" if sync == "sync" else "default_async" - tasks = [f".{topology}"] + for host_name in ("macos", "macos-arm64", "win64", "win32"): + for python in MIN_MAX_PYTHON: + tasks = [f"{t} !.sync_async" for t in SUB_TASKS] # MacOS arm64 only works on server versions 6.0+ - if host == "macos-arm64": - tasks = [f".{topology} .{version}" for version in VERSIONS_6_0_PLUS] - expansions = dict(AUTH=auth, SSL=ssl, TEST_SUITES=test_suite, SKIP_CSOT_TESTS="true") - display_name = get_display_name("Test", host, python=python, **expansions) - variant = create_variant( - tasks, - display_name, - python=python, - host=host, - expansions=expansions, - ) + if host_name == "macos-arm64": + tasks = [] + for version in get_versions_from("6.0"): + tasks.extend(f"{t} .{version} !.sync_async" for t in SUB_TASKS) + host = HOSTS[host_name] + display_name = get_display_name(base_display_name, host, python=python) + variant = create_variant(tasks, display_name, python=python, host=host) variants.append(variant) return variants @@ -271,7 +324,7 @@ def get_encryption_expansions(encryption, ssl="ssl"): expansions["test_encryption_pyopenssl"] = "true" return expansions - host = "rhel8" + host = DEFAULT_HOST # Test against all server versions and topolgies for the three main python versions. encryptions = ["Encryption", "Encryption crypt_shared", "Encryption PyOpenSSL"] @@ -306,10 +359,10 @@ def get_encryption_expansions(encryption, ssl="ssl"): # Test on macos and linux on one server version and topology for min and max python. encryptions = ["Encryption", "Encryption crypt_shared"] - task_names = [".latest .replica_set"] - for host, encryption, python in product(["macos", "win64"], encryptions, MIN_MAX_PYTHON): - ssl = "ssl" if python == CPYTHONS[0] else "nossl" - expansions = get_encryption_expansions(encryption, ssl) + task_names = [".latest .replica_set .sync_async"] + for host_name, encryption, python in product(["macos", "win64"], encryptions, MIN_MAX_PYTHON): + host = HOSTS[host_name] + expansions = get_encryption_expansions(encryption) display_name = get_display_name(encryption, host, python=python, **expansions) variant = create_variant( task_names, @@ -325,9 +378,8 @@ def get_encryption_expansions(encryption, ssl="ssl"): def create_load_balancer_variants(): - # Load balancer tests - run all supported versions for all combinations of auth and ssl and system python. - host = "rhel8" - task_names = ["load-balancer-test"] + # Load balancer tests - run all supported server versions using the lowest supported python. + host = DEFAULT_HOST batchtime = BATCHTIME_WEEK expansions_base = dict(test_loadbalancer="true") versions = ["6.0", "7.0", "8.0", "latest", "rapid"] @@ -353,8 +405,9 @@ def create_load_balancer_variants(): def create_compression_variants(): # Compression tests - standalone versions of each server, across python versions, with and without c extensions. # PyPy interpreters are always tested without extensions. - host = "rhel8" - task_names = dict(snappy=[".standalone"], zlib=[".standalone"], zstd=[".standalone !.4.0"]) + host = DEFAULT_HOST + base_task = ".standalone .noauth .nossl .sync_async" + task_names = dict(snappy=[base_task], zlib=[base_task], zstd=[f"{base_task} !.4.0"]) variants = [] for ind, (compressor, c_ext) in enumerate(product(["snappy", "zlib", "zstd"], C_EXTS)): expansions = dict(COMPRESSORS=compressor) @@ -396,12 +449,12 @@ def create_enterprise_auth_variants(): # All python versions across platforms. for python in ALL_PYTHONS: if python == CPYTHONS[0]: - host = "macos" + host = HOSTS["macos"] elif python == CPYTHONS[-1]: - host = "win64" + host = HOSTS["win64"] else: - host = "rhel8" - display_name = get_display_name("Enterprise Auth", host, python=python, **expansions) + host = DEFAULT_HOST + display_name = get_display_name("Auth Enterprise", host, python=python, **expansions) variant = create_variant( ["test-enterprise-auth"], display_name, host=host, python=python, expansions=expansions ) @@ -420,13 +473,11 @@ def create_pyopenssl_variants(): # Only test "noauth" with min python. auth = "noauth" if python == CPYTHONS[0] else "auth" if python == CPYTHONS[0]: - host = "macos" + host = HOSTS["macos"] elif python == CPYTHONS[-1]: - host = "win64" + host = HOSTS["win64"] else: - host = "rhel8" - expansions = dict(AUTH=auth) - expansions.update(base_expansions) + host = DEFAULT_HOST display_name = get_display_name(base_name, host, python=python) variant = create_variant( @@ -442,6 +493,334 @@ def create_pyopenssl_variants(): return variants +def create_storage_engine_variants(): + host = DEFAULT_HOST + engines = ["InMemory", "MMAPv1"] + variants = [] + for engine in engines: + python = CPYTHONS[0] + expansions = dict(STORAGE_ENGINE=engine.lower()) + if engine == engines[0]: + tasks = [f".standalone .noauth .nossl .{v} .sync_async" for v in ALL_VERSIONS] + else: + # MongoDB 4.2 drops support for MMAPv1 + versions = get_versions_until("4.0") + tasks = [f".standalone .{v} .noauth .nossl .sync_async" for v in versions] + [ + f".replica_set .{v} .noauth .nossl .sync_async" for v in versions + ] + display_name = get_display_name(f"Storage {engine}", host, python=python) + variant = create_variant( + tasks, display_name, host=host, python=python, expansions=expansions + ) + variants.append(variant) + return variants + + +def create_stable_api_variants(): + host = DEFAULT_HOST + tags = ["versionedApi_tag"] + tasks = [f".standalone .{v} .noauth .nossl .sync_async" for v in get_versions_from("5.0")] + variants = [] + types = ["require v1", "accept v2"] + + # All python versions across platforms. + for python, test_type in product(MIN_MAX_PYTHON, types): + expansions = dict(AUTH="auth") + # Test against a cluster with requireApiVersion=1. + if test_type == types[0]: + # REQUIRE_API_VERSION is set to make drivers-evergreen-tools + # start a cluster with the requireApiVersion parameter. + expansions["REQUIRE_API_VERSION"] = "1" + # MONGODB_API_VERSION is the apiVersion to use in the test suite. + expansions["MONGODB_API_VERSION"] = "1" + else: + # Test against a cluster with acceptApiVersion2 but without + # requireApiVersion, and don't automatically add apiVersion to + # clients created in the test suite. + expansions["ORCHESTRATION_FILE"] = "versioned-api-testing.json" + base_display_name = f"Stable API {test_type}" + display_name = get_display_name(base_display_name, host, python=python, **expansions) + variant = create_variant( + tasks, display_name, host=host, python=python, tags=tags, expansions=expansions + ) + variants.append(variant) + + return variants + + +def create_green_framework_variants(): + variants = [] + tasks = [".standalone .noauth .nossl .sync_async"] + host = DEFAULT_HOST + for python, framework in product([CPYTHONS[0], CPYTHONS[-2]], ["eventlet", "gevent"]): + expansions = dict(GREEN_FRAMEWORK=framework, AUTH="auth", SSL="ssl") + display_name = get_display_name(f"Green {framework.capitalize()}", host, python=python) + variant = create_variant( + tasks, display_name, host=host, python=python, expansions=expansions + ) + variants.append(variant) + return variants + + +def create_no_c_ext_variants(): + variants = [] + host = DEFAULT_HOST + for python, topology in zip_cycle(CPYTHONS, TOPOLOGIES): + tasks = [f".{topology} .noauth .nossl .sync_async"] + expansions = dict() + handle_c_ext(C_EXTS[0], expansions) + display_name = get_display_name("No C Ext", host, python=python) + variant = create_variant( + tasks, display_name, host=host, python=python, expansions=expansions + ) + variants.append(variant) + return variants + + +def create_atlas_data_lake_variants(): + variants = [] + host = HOSTS["ubuntu22"] + for python, c_ext in product(MIN_MAX_PYTHON, C_EXTS): + tasks = ["atlas-data-lake-tests"] + expansions = dict(AUTH="auth") + handle_c_ext(c_ext, expansions) + display_name = get_display_name("Atlas Data Lake", host, python=python, **expansions) + variant = create_variant( + tasks, display_name, host=host, python=python, expansions=expansions + ) + variants.append(variant) + return variants + + +def create_mod_wsgi_variants(): + variants = [] + host = HOSTS["ubuntu22"] + tasks = [ + "mod-wsgi-standalone", + "mod-wsgi-replica-set", + "mod-wsgi-embedded-mode-standalone", + "mod-wsgi-embedded-mode-replica-set", + ] + expansions = dict(MOD_WSGI_VERSION="4") + for python in MIN_MAX_PYTHON: + display_name = get_display_name("mod_wsgi", host, python=python) + variant = create_variant( + tasks, display_name, host=host, python=python, expansions=expansions + ) + variants.append(variant) + return variants + + +def create_disable_test_commands_variants(): + host = DEFAULT_HOST + expansions = dict(AUTH="auth", SSL="ssl", DISABLE_TEST_COMMANDS="1") + python = CPYTHONS[0] + display_name = get_display_name("Disable test commands", host, python=python) + tasks = [".latest .sync_async"] + return [create_variant(tasks, display_name, host=host, python=python, expansions=expansions)] + + +def create_serverless_variants(): + host = DEFAULT_HOST + batchtime = BATCHTIME_WEEK + expansions = dict(test_serverless="true", AUTH="auth", SSL="ssl") + tasks = ["serverless_task_group"] + base_name = "Serverless" + return [ + create_variant( + tasks, + get_display_name(base_name, host, python=python), + host=host, + python=python, + expansions=expansions, + batchtime=batchtime, + ) + for python in MIN_MAX_PYTHON + ] + + +def create_oidc_auth_variants(): + variants = [] + other_tasks = ["testazureoidc_task_group", "testgcpoidc_task_group", "testk8soidc_task_group"] + for host_name in ["ubuntu22", "macos", "win64"]: + tasks = ["testoidc_task_group"] + if host_name == "ubuntu22": + tasks += other_tasks + host = HOSTS[host_name] + variants.append( + create_variant( + tasks, + get_display_name("Auth OIDC", host), + host=host, + batchtime=BATCHTIME_WEEK * 2, + ) + ) + return variants + + +def create_search_index_variants(): + host = DEFAULT_HOST + python = CPYTHONS[0] + return [ + create_variant( + ["test_atlas_task_group_search_indexes"], + get_display_name("Search Index Helpers", host, python=python), + python=python, + host=host, + ) + ] + + +def create_mockupdb_variants(): + host = DEFAULT_HOST + python = CPYTHONS[0] + return [ + create_variant( + ["mockupdb"], + get_display_name("MockupDB", host, python=python), + python=python, + host=host, + ) + ] + + +def create_doctests_variants(): + host = DEFAULT_HOST + python = CPYTHONS[0] + return [ + create_variant( + ["doctests"], + get_display_name("Doctests", host, python=python), + python=python, + host=host, + ) + ] + + +def create_atlas_connect_variants(): + host = DEFAULT_HOST + return [ + create_variant( + ["atlas-connect"], + get_display_name("Atlas connect", host, python=python), + python=python, + host=host, + ) + for python in MIN_MAX_PYTHON + ] + + +def create_aws_auth_variants(): + variants = [] + tasks = [ + "aws-auth-test-4.4", + "aws-auth-test-5.0", + "aws-auth-test-6.0", + "aws-auth-test-7.0", + "aws-auth-test-8.0", + "aws-auth-test-rapid", + "aws-auth-test-latest", + ] + + for host_name, python in product(["ubuntu20", "win64", "macos"], MIN_MAX_PYTHON): + expansions = dict() + if host_name != "ubuntu20": + expansions["skip_ECS_auth_test"] = "true" + if host_name == "macos": + expansions["skip_EC2_auth_test"] = "true" + expansions["skip_web_identity_auth_test"] = "true" + host = HOSTS[host_name] + variant = create_variant( + tasks, + get_display_name("Auth AWS", host, python=python), + host=host, + python=python, + expansions=expansions, + ) + variants.append(variant) + return variants + + +def create_alternative_hosts_variants(): + expansions = dict(SKIP_HATCH="true") + batchtime = BATCHTIME_WEEK + variants = [] + + host = HOSTS["rhel7"] + variants.append( + create_variant( + [".5.0 .standalone !.sync_async"], + get_display_name("OpenSSL 1.0.2", host, python=CPYTHONS[0], **expansions), + host=host, + python=CPYTHONS[0], + batchtime=batchtime, + expansions=expansions, + ) + ) + + for host_name in OTHER_HOSTS: + host = HOSTS[host_name] + variants.append( + create_variant( + [".6.0 .standalone !.sync_async"], + display_name=get_display_name("Other hosts", host, **expansions), + expansions=expansions, + batchtime=batchtime, + host=host, + ) + ) + return variants + + +############## +# Tasks +############## + + +def create_server_tasks(): + tasks = [] + for topo, version, (auth, ssl), sync in product(TOPOLOGIES, ALL_VERSIONS, AUTH_SSLS, SYNCS): + name = f"test-{version}-{topo}-{auth}-{ssl}-{sync}".lower() + tags = [version, topo, auth, ssl, sync] + bootstrap_vars = dict( + VERSION=version, + TOPOLOGY=topo if topo != "standalone" else "server", + AUTH=auth, + SSL=ssl, + ) + bootstrap_func = FunctionCall(func="bootstrap mongo-orchestration", vars=bootstrap_vars) + test_suites = "" + if sync == "sync": + test_suites = "default" + elif sync == "async": + test_suites = "default_async" + test_vars = dict( + AUTH=auth, + SSL=ssl, + SYNC=sync, + TEST_SUITES=test_suites, + ) + test_func = FunctionCall(func="run tests", vars=test_vars) + tasks.append(EvgTask(name=name, tags=tags, commands=[bootstrap_func, test_func])) + return tasks + + +def create_load_balancer_tasks(): + tasks = [] + for auth, ssl in AUTH_SSLS: + name = f"test-load-balancer-{auth}-{ssl}".lower() + tags = ["load-balancer", auth, ssl] + bootstrap_vars = dict(TOPOLOGY="sharded_cluster", AUTH=auth, SSL=ssl, LOAD_BALANCER="true") + bootstrap_func = FunctionCall(func="bootstrap mongo-orchestration", vars=bootstrap_vars) + balancer_func = FunctionCall(func="run load-balancer") + test_vars = dict(AUTH=auth, SSL=ssl, test_loadbalancer="true") + test_func = FunctionCall(func="run tests", vars=test_vars) + tasks.append( + EvgTask(name=name, tags=tags, commands=[bootstrap_func, balancer_func, test_func]) + ) + return tasks + + ################## # Generate Config ################## diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2dc070d7c6..e620cb1801 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -39,6 +39,7 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} + persist-credentials: false - uses: actions/setup-python@v5 # Initializes the CodeQL tools for scanning. diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 858d269e08..a4c5a8279b 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -48,6 +48,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false ref: ${{ inputs.ref }} - uses: actions/setup-python@v5 @@ -106,6 +107,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false ref: ${{ inputs.ref }} - uses: actions/setup-python@v5 diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index 40991440d3..12cfaa4b27 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -20,6 +20,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: "3.9" @@ -55,6 +57,8 @@ jobs: name: CPython ${{ matrix.python-version }}-${{ matrix.os }} steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - if: ${{ matrix.python-version == '3.13t' }} name: Setup free-threaded Python uses: deadsnakes/action@v3.2.0 @@ -99,6 +103,8 @@ jobs: name: DocTest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Python uses: actions/setup-python@v5 with: @@ -121,6 +127,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: cache: 'pip' @@ -139,6 +147,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: cache: 'pip' @@ -160,6 +170,8 @@ jobs: python: ["3.9", "3.11"] steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: "${{matrix.python}}" @@ -177,6 +189,8 @@ jobs: name: "Make an sdist" steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: cache: 'pip' diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000000..31afeb6655 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,32 @@ +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@v4 + with: + persist-credentials: false + - name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + - name: Get zizmor + run: cargo install zizmor + - name: Run zizmor 🌈 + run: zizmor --format sarif . > results.sarif + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif + category: zizmor diff --git a/bson/binary.py b/bson/binary.py index f03173a8ef..6dc5058c2c 100644 --- a/bson/binary.py +++ b/bson/binary.py @@ -16,7 +16,7 @@ import struct from dataclasses import dataclass from enum import Enum -from typing import TYPE_CHECKING, Any, Optional, Sequence, Tuple, Type, Union +from typing import TYPE_CHECKING, Any, Optional, Sequence, Tuple, Type, Union, overload from uuid import UUID """Tools for representing BSON binary data. @@ -195,7 +195,7 @@ class UuidRepresentation: VECTOR_SUBTYPE = 9 -"""**(BETA)** BSON binary subtype for densely packed vector data. +"""BSON binary subtype for densely packed vector data. .. versionadded:: 4.10 """ @@ -207,7 +207,7 @@ class UuidRepresentation: class BinaryVectorDtype(Enum): - """**(BETA)** Datatypes of vector subtype. + """Datatypes of vector subtype. :param FLOAT32: (0x27) Pack list of :class:`float` as float32 :param INT8: (0x03) Pack list of :class:`int` in [-128, 127] as signed int8 @@ -229,7 +229,7 @@ class BinaryVectorDtype(Enum): @dataclass class BinaryVector: - """**(BETA)** Vector of numbers along with metadata for binary interoperability. + """Vector of numbers along with metadata for binary interoperability. .. versionadded:: 4.10 """ @@ -256,7 +256,7 @@ class Binary(bytes): the difference between what should be considered binary data and what should be considered a string when we encode to BSON. - **(BETA)** Subtype 9 provides a space-efficient representation of 1-dimensional vector data. + Subtype 9 provides a space-efficient representation of 1-dimensional vector data. Its data is prepended with two bytes of metadata. The first (dtype) describes its data type, such as float32 or int8. The second (padding) prescribes the number of bits to ignore in the final byte. @@ -278,7 +278,7 @@ class Binary(bytes): Support any bytes-like type that implements the buffer protocol. .. versionchanged:: 4.10 - **(BETA)** Addition of vector subtype. + Addition of vector subtype. """ _type_marker = 5 @@ -397,6 +397,18 @@ def as_uuid(self, uuid_representation: int = UuidRepresentation.STANDARD) -> UUI f"cannot decode subtype {self.subtype} to {UUID_REPRESENTATION_NAMES[uuid_representation]}" ) + @classmethod + @overload + def from_vector(cls: Type[Binary], vector: BinaryVector) -> Binary: + ... + + @classmethod + @overload + def from_vector( + cls: Type[Binary], vector: list[int, float], dtype: BinaryVectorDtype, padding: int = 0 + ) -> Binary: + ... + @classmethod def from_vector( cls: Type[Binary], @@ -404,7 +416,7 @@ def from_vector( dtype: Optional[BinaryVectorDtype] = None, padding: Optional[int] = None, ) -> Binary: - """**(BETA)** Create a BSON :class:`~bson.binary.Binary` of Vector subtype. + """Create a BSON :class:`~bson.binary.Binary` of Vector subtype. To interpret the representation of the numbers, a data type must be included. See :class:`~bson.binary.BinaryVectorDtype` for available types and descriptions. @@ -447,7 +459,7 @@ def from_vector( return cls(metadata + data, subtype=VECTOR_SUBTYPE) def as_vector(self) -> BinaryVector: - """**(BETA)** From the Binary, create a list of numbers, along with dtype and padding. + """From the Binary, create a list of numbers, along with dtype and padding. :return: BinaryVector diff --git a/pymongo/__init__.py b/pymongo/__init__.py index 6416f939e8..58f6ff338b 100644 --- a/pymongo/__init__.py +++ b/pymongo/__init__.py @@ -88,6 +88,7 @@ from pymongo import _csot from pymongo._version import __version__, get_version_string, version_tuple +from pymongo.asynchronous.mongo_client import AsyncMongoClient from pymongo.common import MAX_SUPPORTED_WIRE_VERSION, MIN_SUPPORTED_WIRE_VERSION, has_c from pymongo.cursor import CursorType from pymongo.operations import ( @@ -104,14 +105,6 @@ from pymongo.synchronous.mongo_client import MongoClient from pymongo.write_concern import WriteConcern -try: - from pymongo.asynchronous.mongo_client import AsyncMongoClient -except Exception as e: - # PYTHON-4781: Importing asyncio can fail on Windows. - import warnings as _warnings - - _warnings.warn(f"Failed to import Async PyMongo: {e!r}", ImportWarning, stacklevel=2) - version = __version__ """Current version of PyMongo.""" diff --git a/pymongo/asynchronous/auth_oidc.py b/pymongo/asynchronous/auth_oidc.py index f5801b85d4..f1c15045de 100644 --- a/pymongo/asynchronous/auth_oidc.py +++ b/pymongo/asynchronous/auth_oidc.py @@ -55,7 +55,7 @@ def _get_authenticator( properties = credentials.mechanism_properties # Validate that the address is allowed. - if not properties.environment: + if properties.human_callback is not None: found = False allowed_hosts = properties.allowed_hosts for patt in allowed_hosts: diff --git a/pymongo/asynchronous/bulk.py b/pymongo/asynchronous/bulk.py index e6cfe5b36e..6770d7b34e 100644 --- a/pymongo/asynchronous/bulk.py +++ b/pymongo/asynchronous/bulk.py @@ -140,8 +140,8 @@ def add_update( self, selector: Mapping[str, Any], update: Union[Mapping[str, Any], _Pipeline], - multi: bool = False, - upsert: bool = False, + multi: bool, + upsert: Optional[bool], collation: Optional[Mapping[str, Any]] = None, array_filters: Optional[list[Mapping[str, Any]]] = None, hint: Union[str, dict[str, Any], None] = None, @@ -149,9 +149,9 @@ def add_update( ) -> None: """Create an update document and add it to the list of ops.""" validate_ok_for_update(update) - cmd: dict[str, Any] = dict( # noqa: C406 - [("q", selector), ("u", update), ("multi", multi), ("upsert", upsert)] - ) + cmd: dict[str, Any] = {"q": selector, "u": update, "multi": multi} + if upsert is not None: + cmd["upsert"] = upsert if collation is not None: self.uses_collation = True cmd["collation"] = collation @@ -173,14 +173,16 @@ def add_replace( self, selector: Mapping[str, Any], replacement: Mapping[str, Any], - upsert: bool = False, + upsert: Optional[bool], collation: Optional[Mapping[str, Any]] = None, hint: Union[str, dict[str, Any], None] = None, sort: Optional[Mapping[str, Any]] = None, ) -> None: """Create a replace document and add it to the list of ops.""" validate_ok_for_replace(replacement) - cmd = {"q": selector, "u": replacement, "multi": False, "upsert": upsert} + cmd: dict[str, Any] = {"q": selector, "u": replacement} + if upsert is not None: + cmd["upsert"] = upsert if collation is not None: self.uses_collation = True cmd["collation"] = collation @@ -200,7 +202,7 @@ def add_delete( hint: Union[str, dict[str, Any], None] = None, ) -> None: """Create a delete document and add it to the list of ops.""" - cmd = {"q": selector, "limit": limit} + cmd: dict[str, Any] = {"q": selector, "limit": limit} if collation is not None: self.uses_collation = True cmd["collation"] = collation diff --git a/pymongo/asynchronous/client_bulk.py b/pymongo/asynchronous/client_bulk.py index a6f7178e47..0dcdaa6c07 100644 --- a/pymongo/asynchronous/client_bulk.py +++ b/pymongo/asynchronous/client_bulk.py @@ -106,20 +106,13 @@ def __init__( self.bypass_doc_val = bypass_document_validation self.comment = comment self.verbose_results = verbose_results - self.ops: list[tuple[str, Mapping[str, Any]]] = [] self.namespaces: list[str] = [] self.idx_offset: int = 0 self.total_ops: int = 0 - self.executed = False - self.uses_upsert = False self.uses_collation = False self.uses_array_filters = False - self.uses_hint_update = False - self.uses_hint_delete = False - self.uses_sort = False - self.is_retryable = self.client.options.retry_writes self.retrying = False self.started_retryable_write = False @@ -144,7 +137,7 @@ def add_update( namespace: str, selector: Mapping[str, Any], update: Union[Mapping[str, Any], _Pipeline], - multi: bool = False, + multi: bool, upsert: Optional[bool] = None, collation: Optional[Mapping[str, Any]] = None, array_filters: Optional[list[Mapping[str, Any]]] = None, @@ -160,19 +153,16 @@ def add_update( "multi": multi, } if upsert is not None: - self.uses_upsert = True cmd["upsert"] = upsert if array_filters is not None: self.uses_array_filters = True cmd["arrayFilters"] = array_filters if hint is not None: - self.uses_hint_update = True cmd["hint"] = hint if collation is not None: self.uses_collation = True cmd["collation"] = collation if sort is not None: - self.uses_sort = True cmd["sort"] = sort if multi: # A bulk_write containing an update_many is not retryable. @@ -200,16 +190,13 @@ def add_replace( "multi": False, } if upsert is not None: - self.uses_upsert = True cmd["upsert"] = upsert if hint is not None: - self.uses_hint_update = True cmd["hint"] = hint if collation is not None: self.uses_collation = True cmd["collation"] = collation if sort is not None: - self.uses_sort = True cmd["sort"] = sort self.ops.append(("replace", cmd)) self.namespaces.append(namespace) @@ -226,7 +213,6 @@ def add_delete( """Create a delete document and add it to the list of ops.""" cmd = {"delete": -1, "filter": selector, "multi": multi} if hint is not None: - self.uses_hint_delete = True cmd["hint"] = hint if collation is not None: self.uses_collation = True diff --git a/pymongo/asynchronous/monitor.py b/pymongo/asynchronous/monitor.py index 780704fabb..2ad57b03e7 100644 --- a/pymongo/asynchronous/monitor.py +++ b/pymongo/asynchronous/monitor.py @@ -149,6 +149,7 @@ def __init__( self._listeners = self._settings._pool_options._event_listeners self._publish = self._listeners is not None and self._listeners.enabled_for_server_heartbeat self._cancel_context: Optional[_CancellationContext] = None + self._conn_id: Optional[int] = None self._rtt_monitor = _RttMonitor( topology, topology_settings, @@ -243,6 +244,7 @@ async def _check_server(self) -> ServerDescription: Returns a ServerDescription. """ + self._conn_id = None start = time.monotonic() try: try: @@ -272,6 +274,7 @@ async def _check_server(self) -> ServerDescription: awaited=awaited, durationMS=duration * 1000, failure=error, + driverConnectionId=self._conn_id, message=_SDAMStatusMessage.HEARTBEAT_FAIL, ) await self._reset_connection() @@ -314,6 +317,8 @@ async def _check_once(self) -> ServerDescription: ) self._cancel_context = conn.cancel_context + # Record the connection id so we can later attach it to the failed log message. + self._conn_id = conn.id response, round_trip_time = await self._check_with_socket(conn) if not response.awaitable: await self._rtt_monitor.add_sample(round_trip_time) diff --git a/pymongo/auth_shared.py b/pymongo/auth_shared.py index 1e1ce7b4d8..9534bd74ad 100644 --- a/pymongo/auth_shared.py +++ b/pymongo/auth_shared.py @@ -100,8 +100,8 @@ def _validate_canonicalize_host_name(value: str | bool) -> str | bool: def _build_credentials_tuple( mech: str, source: Optional[str], - user: str, - passwd: str, + user: Optional[str], + passwd: Optional[str], extra: Mapping[str, Any], database: Optional[str], ) -> MongoCredential: @@ -161,6 +161,8 @@ def _build_credentials_tuple( "::1", ] allowed_hosts = properties.get("ALLOWED_HOSTS", default_allowed) + if properties.get("ALLOWED_HOSTS", None) is not None and human_callback is None: + raise ConfigurationError("ALLOWED_HOSTS is only valid with OIDC_HUMAN_CALLBACK") msg = ( "authentication with MONGODB-OIDC requires providing either a callback or a environment" ) @@ -207,7 +209,7 @@ def _build_credentials_tuple( environment=environ, allowed_hosts=allowed_hosts, token_resource=token_resource, - username=user, + username=user or "", ) return MongoCredential(mech, "$external", user, passwd, oidc_props, _Cache()) diff --git a/pymongo/common.py b/pymongo/common.py index d4601a0eb5..5661de011c 100644 --- a/pymongo/common.py +++ b/pymongo/common.py @@ -873,8 +873,10 @@ def get_setter_key(x: str) -> str: validator = _get_validator(opt, URI_OPTIONS_VALIDATOR_MAP, normed_key=normed_key) validated = validator(opt, value) except (ValueError, TypeError, ConfigurationError) as exc: - if normed_key == "authmechanismproperties" and any( - p in str(exc) for p in _MECH_PROP_MUST_RAISE + if ( + normed_key == "authmechanismproperties" + and any(p in str(exc) for p in _MECH_PROP_MUST_RAISE) + and "is not a supported auth mechanism property" not in str(exc) ): raise if warn: diff --git a/pymongo/operations.py b/pymongo/operations.py index 8905048c4e..482ab68003 100644 --- a/pymongo/operations.py +++ b/pymongo/operations.py @@ -332,7 +332,7 @@ def __init__( self, filter: Mapping[str, Any], replacement: Union[_DocumentType, RawBSONDocument], - upsert: bool = False, + upsert: Optional[bool] = None, collation: Optional[_CollationIn] = None, hint: Optional[_IndexKeyHint] = None, namespace: Optional[str] = None, @@ -693,7 +693,7 @@ def _add_to_bulk(self, bulkobj: _AgnosticBulk) -> None: self._filter, self._doc, True, - bool(self._upsert), + self._upsert, collation=validate_collation_or_none(self._collation), array_filters=self._array_filters, hint=self._hint, diff --git a/pymongo/synchronous/auth_oidc.py b/pymongo/synchronous/auth_oidc.py index 6381a408ab..5a8967d96b 100644 --- a/pymongo/synchronous/auth_oidc.py +++ b/pymongo/synchronous/auth_oidc.py @@ -55,7 +55,7 @@ def _get_authenticator( properties = credentials.mechanism_properties # Validate that the address is allowed. - if not properties.environment: + if properties.human_callback is not None: found = False allowed_hosts = properties.allowed_hosts for patt in allowed_hosts: diff --git a/pymongo/synchronous/bulk.py b/pymongo/synchronous/bulk.py index 7fb29a977f..0b709f1acf 100644 --- a/pymongo/synchronous/bulk.py +++ b/pymongo/synchronous/bulk.py @@ -140,8 +140,8 @@ def add_update( self, selector: Mapping[str, Any], update: Union[Mapping[str, Any], _Pipeline], - multi: bool = False, - upsert: bool = False, + multi: bool, + upsert: Optional[bool], collation: Optional[Mapping[str, Any]] = None, array_filters: Optional[list[Mapping[str, Any]]] = None, hint: Union[str, dict[str, Any], None] = None, @@ -149,9 +149,9 @@ def add_update( ) -> None: """Create an update document and add it to the list of ops.""" validate_ok_for_update(update) - cmd: dict[str, Any] = dict( # noqa: C406 - [("q", selector), ("u", update), ("multi", multi), ("upsert", upsert)] - ) + cmd: dict[str, Any] = {"q": selector, "u": update, "multi": multi} + if upsert is not None: + cmd["upsert"] = upsert if collation is not None: self.uses_collation = True cmd["collation"] = collation @@ -173,14 +173,16 @@ def add_replace( self, selector: Mapping[str, Any], replacement: Mapping[str, Any], - upsert: bool = False, + upsert: Optional[bool], collation: Optional[Mapping[str, Any]] = None, hint: Union[str, dict[str, Any], None] = None, sort: Optional[Mapping[str, Any]] = None, ) -> None: """Create a replace document and add it to the list of ops.""" validate_ok_for_replace(replacement) - cmd = {"q": selector, "u": replacement, "multi": False, "upsert": upsert} + cmd: dict[str, Any] = {"q": selector, "u": replacement} + if upsert is not None: + cmd["upsert"] = upsert if collation is not None: self.uses_collation = True cmd["collation"] = collation @@ -200,7 +202,7 @@ def add_delete( hint: Union[str, dict[str, Any], None] = None, ) -> None: """Create a delete document and add it to the list of ops.""" - cmd = {"q": selector, "limit": limit} + cmd: dict[str, Any] = {"q": selector, "limit": limit} if collation is not None: self.uses_collation = True cmd["collation"] = collation diff --git a/pymongo/synchronous/client_bulk.py b/pymongo/synchronous/client_bulk.py index 6cb4275417..625e8429eb 100644 --- a/pymongo/synchronous/client_bulk.py +++ b/pymongo/synchronous/client_bulk.py @@ -106,20 +106,13 @@ def __init__( self.bypass_doc_val = bypass_document_validation self.comment = comment self.verbose_results = verbose_results - self.ops: list[tuple[str, Mapping[str, Any]]] = [] self.namespaces: list[str] = [] self.idx_offset: int = 0 self.total_ops: int = 0 - self.executed = False - self.uses_upsert = False self.uses_collation = False self.uses_array_filters = False - self.uses_hint_update = False - self.uses_hint_delete = False - self.uses_sort = False - self.is_retryable = self.client.options.retry_writes self.retrying = False self.started_retryable_write = False @@ -144,7 +137,7 @@ def add_update( namespace: str, selector: Mapping[str, Any], update: Union[Mapping[str, Any], _Pipeline], - multi: bool = False, + multi: bool, upsert: Optional[bool] = None, collation: Optional[Mapping[str, Any]] = None, array_filters: Optional[list[Mapping[str, Any]]] = None, @@ -160,19 +153,16 @@ def add_update( "multi": multi, } if upsert is not None: - self.uses_upsert = True cmd["upsert"] = upsert if array_filters is not None: self.uses_array_filters = True cmd["arrayFilters"] = array_filters if hint is not None: - self.uses_hint_update = True cmd["hint"] = hint if collation is not None: self.uses_collation = True cmd["collation"] = collation if sort is not None: - self.uses_sort = True cmd["sort"] = sort if multi: # A bulk_write containing an update_many is not retryable. @@ -200,16 +190,13 @@ def add_replace( "multi": False, } if upsert is not None: - self.uses_upsert = True cmd["upsert"] = upsert if hint is not None: - self.uses_hint_update = True cmd["hint"] = hint if collation is not None: self.uses_collation = True cmd["collation"] = collation if sort is not None: - self.uses_sort = True cmd["sort"] = sort self.ops.append(("replace", cmd)) self.namespaces.append(namespace) @@ -226,7 +213,6 @@ def add_delete( """Create a delete document and add it to the list of ops.""" cmd = {"delete": -1, "filter": selector, "multi": multi} if hint is not None: - self.uses_hint_delete = True cmd["hint"] = hint if collation is not None: self.uses_collation = True diff --git a/pymongo/synchronous/monitor.py b/pymongo/synchronous/monitor.py index d82a5f976d..a0b7635ab1 100644 --- a/pymongo/synchronous/monitor.py +++ b/pymongo/synchronous/monitor.py @@ -149,6 +149,7 @@ def __init__( self._listeners = self._settings._pool_options._event_listeners self._publish = self._listeners is not None and self._listeners.enabled_for_server_heartbeat self._cancel_context: Optional[_CancellationContext] = None + self._conn_id: Optional[int] = None self._rtt_monitor = _RttMonitor( topology, topology_settings, @@ -243,6 +244,7 @@ def _check_server(self) -> ServerDescription: Returns a ServerDescription. """ + self._conn_id = None start = time.monotonic() try: try: @@ -272,6 +274,7 @@ def _check_server(self) -> ServerDescription: awaited=awaited, durationMS=duration * 1000, failure=error, + driverConnectionId=self._conn_id, message=_SDAMStatusMessage.HEARTBEAT_FAIL, ) self._reset_connection() @@ -314,6 +317,8 @@ def _check_once(self) -> ServerDescription: ) self._cancel_context = conn.cancel_context + # Record the connection id so we can later attach it to the failed log message. + self._conn_id = conn.id response, round_trip_time = self._check_with_socket(conn) if not response.awaitable: self._rtt_monitor.add_sample(round_trip_time) diff --git a/requirements/typing.txt b/requirements/typing.txt index b1f07604dc..ad799ea368 100644 --- a/requirements/typing.txt +++ b/requirements/typing.txt @@ -1,5 +1,5 @@ mypy==1.13.0 -pyright==1.1.385 +pyright==1.1.388 typing_extensions -r ./encryption.txt -r ./ocsp.txt diff --git a/test/asynchronous/unified_format.py b/test/asynchronous/unified_format.py index b2c889c461..0ea6081758 100644 --- a/test/asynchronous/unified_format.py +++ b/test/asynchronous/unified_format.py @@ -1324,8 +1324,8 @@ def format_logs(log_list): if log.module == "ocsp_support": continue data = json_util.loads(log.getMessage()) - client = data.pop("clientId") if "clientId" in data else data.pop("topologyId") - client_to_log[client].append( + client_id = data.get("clientId", data.get("topologyId")) + client_to_log[client_id].append( { "level": log.levelname.lower(), "component": log.name.replace("pymongo.", "", 1), diff --git a/test/auth_oidc/test_auth_oidc.py b/test/auth_oidc/test_auth_oidc.py index a0127304c1..7a78f3d2f6 100644 --- a/test/auth_oidc/test_auth_oidc.py +++ b/test/auth_oidc/test_auth_oidc.py @@ -38,11 +38,17 @@ from pymongo._azure_helpers import _get_azure_response from pymongo._gcp_helpers import _get_gcp_response from pymongo.auth_oidc_shared import _get_k8s_token +from pymongo.auth_shared import _build_credentials_tuple from pymongo.cursor_shared import CursorType from pymongo.errors import AutoReconnect, ConfigurationError, OperationFailure from pymongo.hello import HelloCompat from pymongo.operations import InsertOne -from pymongo.synchronous.auth_oidc import OIDCCallback, OIDCCallbackContext, OIDCCallbackResult +from pymongo.synchronous.auth_oidc import ( + OIDCCallback, + OIDCCallbackContext, + OIDCCallbackResult, + _get_authenticator, +) from pymongo.uri_parser import parse_uri ROOT = Path(__file__).parent.parent.resolve() @@ -103,7 +109,6 @@ def fail_point(self, command_args): client.close() -@pytest.mark.auth_oidc class TestAuthOIDCHuman(OIDCTestBase): uri: str @@ -838,12 +843,35 @@ def test_2_4_invalid_client_configuration_with_callback(self): self.create_client(authmechanismproperties=props) def test_2_5_invalid_use_of_ALLOWED_HOSTS(self): - # Create an OIDC configured client with auth mechanism properties `{"ENVIRONMENT": "azure", "ALLOWED_HOSTS": []}`. - props: Dict = {"ENVIRONMENT": "azure", "ALLOWED_HOSTS": []} + # Create an OIDC configured client with auth mechanism properties `{"ENVIRONMENT": "test", "ALLOWED_HOSTS": []}`. + props: Dict = {"ENVIRONMENT": "test", "ALLOWED_HOSTS": []} # Assert it returns a client configuration error. with self.assertRaises(ConfigurationError): self.create_client(authmechanismproperties=props) + # Create an OIDC configured client with auth mechanism properties `{"OIDC_CALLBACK": "", "ALLOWED_HOSTS": []}`. + props: Dict = {"OIDC_CALLBACK": self.create_request_cb(), "ALLOWED_HOSTS": []} + # Assert it returns a client configuration error. + with self.assertRaises(ConfigurationError): + self.create_client(authmechanismproperties=props) + + def test_2_6_ALLOWED_HOSTS_defaults_ignored(self): + # Create a MongoCredential for OIDC with a machine callback. + props = {"OIDC_CALLBACK": self.create_request_cb()} + extra = dict(authmechanismproperties=props) + mongo_creds = _build_credentials_tuple("MONGODB-OIDC", None, "foo", None, extra, "test") + # Assert that creating an authenticator for example.com does not result in an error. + authenticator = _get_authenticator(mongo_creds, ("example.com", 30)) + assert authenticator.properties.username == "foo" + + # Create a MongoCredential for OIDC with an ENVIRONMENT. + props = {"ENVIRONMENT": "test"} + extra = dict(authmechanismproperties=props) + mongo_creds = _build_credentials_tuple("MONGODB-OIDC", None, None, None, extra, "test") + # Assert that creating an authenticator for example.com does not result in an error. + authenticator = _get_authenticator(mongo_creds, ("example.com", 30)) + assert authenticator.properties.username == "" + def test_3_1_authentication_failure_with_cached_tokens_fetch_a_new_token_and_retry(self): # Create a MongoClient and an OIDC callback that implements the provider logic. client = self.create_client() @@ -909,7 +937,7 @@ def test_3_3_unexpected_error_code_does_not_clear_cache(self): # Assert that the callback has been called once. self.assertEqual(self.request_called, 1) - def test_4_1_reauthentication_succeds(self): + def test_4_1_reauthentication_succeeds(self): # Create a ``MongoClient`` configured with a custom OIDC callback that # implements the provider logic. client = self.create_client() diff --git a/test/discovery_and_monitoring/unified/logging-replicaset.json b/test/discovery_and_monitoring/unified/logging-replicaset.json index e6738225cd..fe6ac60b68 100644 --- a/test/discovery_and_monitoring/unified/logging-replicaset.json +++ b/test/discovery_and_monitoring/unified/logging-replicaset.json @@ -357,6 +357,7 @@ }, "durationMS": { "$$type": [ + "double", "int", "long" ] @@ -398,6 +399,7 @@ }, "durationMS": { "$$type": [ + "double", "int", "long" ] @@ -439,6 +441,7 @@ }, "durationMS": { "$$type": [ + "double", "int", "long" ] @@ -589,6 +592,7 @@ }, "durationMS": { "$$type": [ + "double", "int", "long" ] diff --git a/test/discovery_and_monitoring/unified/logging-sharded.json b/test/discovery_and_monitoring/unified/logging-sharded.json index 61b27f5be0..3788708ab0 100644 --- a/test/discovery_and_monitoring/unified/logging-sharded.json +++ b/test/discovery_and_monitoring/unified/logging-sharded.json @@ -324,6 +324,7 @@ }, "durationMS": { "$$type": [ + "double", "int", "long" ] @@ -475,6 +476,7 @@ }, "durationMS": { "$$type": [ + "double", "int", "long" ] diff --git a/test/discovery_and_monitoring/unified/logging-standalone.json b/test/discovery_and_monitoring/unified/logging-standalone.json index 1ee6dbe899..0682a1a4fb 100644 --- a/test/discovery_and_monitoring/unified/logging-standalone.json +++ b/test/discovery_and_monitoring/unified/logging-standalone.json @@ -339,6 +339,7 @@ }, "durationMS": { "$$type": [ + "double", "int", "long" ] @@ -500,6 +501,7 @@ }, "durationMS": { "$$type": [ + "double", "int", "long" ] diff --git a/test/test_bson.py b/test/test_bson.py index 5dc1377bcd..b431f700dc 100644 --- a/test/test_bson.py +++ b/test/test_bson.py @@ -802,6 +802,12 @@ def test_vector(self): assert float_binary == Binary.from_vector( BinaryVector(list_vector, BinaryVectorDtype.FLOAT32) ) + # Confirm kwargs cannot be passed when BinaryVector is provided + with self.assertRaises(ValueError): + Binary.from_vector( + BinaryVector(list_vector, BinaryVectorDtype.PACKED_BIT, padding), + dtype=BinaryVectorDtype.PACKED_BIT, + ) # type: ignore[call-overload] def test_unicode_regex(self): """Tests we do not get a segfault for C extension on unicode RegExs. diff --git a/test/unified-test-format/invalid/runOnRequirement-authMechanism-type.json b/test/unified-test-format/invalid/runOnRequirement-authMechanism-type.json index b97654a743..007f3f304c 100644 --- a/test/unified-test-format/invalid/runOnRequirement-authMechanism-type.json +++ b/test/unified-test-format/invalid/runOnRequirement-authMechanism-type.json @@ -9,9 +9,7 @@ "tests": [ { "description": "foo", - "operations": [ - - ] + "operations": [] } ] } diff --git a/test/unified-test-format/valid-fail/operator-matchAsDocument.json b/test/unified-test-format/valid-fail/operator-matchAsDocument.json new file mode 100644 index 0000000000..24f6be9cb8 --- /dev/null +++ b/test/unified-test-format/valid-fail/operator-matchAsDocument.json @@ -0,0 +1,205 @@ +{ + "description": "operator-matchAsDocument", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "test", + "documents": [ + { + "_id": 1, + "json": "{ \"x\": 1, \"y\": 2 }" + }, + { + "_id": 2, + "json": "1" + }, + { + "_id": 3, + "json": "[ \"foo\" ]" + }, + { + "_id": 4, + "json": "{ \"x\" }" + } + ] + } + ], + "tests": [ + { + "description": "matchAsDocument with non-matching filter", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "json": { + "$$matchAsDocument": { + "x": 1, + "y": "two" + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument evaluates special operators", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "json": { + "$$matchAsDocument": { + "x": 1, + "y": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument does not permit extra fields", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "json": { + "$$matchAsDocument": { + "x": 1 + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument expects JSON object but given scalar", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 2, + "json": { + "$$matchAsDocument": { + "$$matchAsRoot": {} + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument expects JSON object but given array", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 3 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 3, + "json": { + "$$matchAsDocument": { + "$$matchAsRoot": {} + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument fails to decode Extended JSON", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 4 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 4, + "json": { + "$$matchAsDocument": { + "$$matchAsRoot": {} + } + } + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/valid-fail/operator-matchAsRoot.json b/test/unified-test-format/valid-fail/operator-matchAsRoot.json new file mode 100644 index 0000000000..ec6309418c --- /dev/null +++ b/test/unified-test-format/valid-fail/operator-matchAsRoot.json @@ -0,0 +1,67 @@ +{ + "description": "operator-matchAsRoot", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "test", + "documents": [ + { + "_id": 1, + "x": { + "y": 2, + "z": 3 + } + } + ] + } + ], + "tests": [ + { + "description": "matchAsRoot with nested document does not match", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$matchAsRoot": { + "y": 3 + } + } + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/valid-pass/matches-lte-operator.json b/test/unified-test-format/valid-pass/operator-lte.json similarity index 97% rename from test/unified-test-format/valid-pass/matches-lte-operator.json rename to test/unified-test-format/valid-pass/operator-lte.json index 4de65c5838..4a13b16d15 100644 --- a/test/unified-test-format/valid-pass/matches-lte-operator.json +++ b/test/unified-test-format/valid-pass/operator-lte.json @@ -1,5 +1,5 @@ { - "description": "matches-lte-operator", + "description": "operator-lte", "schemaVersion": "1.9", "createEntities": [ { diff --git a/test/unified-test-format/valid-pass/operator-matchAsDocument.json b/test/unified-test-format/valid-pass/operator-matchAsDocument.json new file mode 100644 index 0000000000..fd8b514d4a --- /dev/null +++ b/test/unified-test-format/valid-pass/operator-matchAsDocument.json @@ -0,0 +1,124 @@ +{ + "description": "operator-matchAsDocument", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "test", + "documents": [ + { + "_id": 1, + "json": "{ \"x\": 1, \"y\": 2.0 }" + }, + { + "_id": 2, + "json": "{ \"x\": { \"$oid\": \"57e193d7a9cc81b4027498b5\" } }" + } + ] + } + ], + "tests": [ + { + "description": "matchAsDocument performs flexible numeric comparisons", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "json": { + "$$matchAsDocument": { + "x": 1, + "y": 2 + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument evaluates special operators", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "json": { + "$$matchAsDocument": { + "x": 1, + "y": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument decodes Extended JSON", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 2, + "json": { + "$$matchAsDocument": { + "x": { + "$$type": "objectId" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/valid-pass/operator-matchAsRoot.json b/test/unified-test-format/valid-pass/operator-matchAsRoot.json new file mode 100644 index 0000000000..1966e3b377 --- /dev/null +++ b/test/unified-test-format/valid-pass/operator-matchAsRoot.json @@ -0,0 +1,151 @@ +{ + "description": "operator-matchAsRoot", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "test", + "documents": [ + { + "_id": 1, + "x": { + "y": 2, + "z": 3 + } + }, + { + "_id": 2, + "json": "{ \"x\": 1, \"y\": 2 }" + } + ] + } + ], + "tests": [ + { + "description": "matchAsRoot with nested document", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$matchAsRoot": { + "y": 2 + } + } + } + ] + } + ] + }, + { + "description": "matchAsRoot performs flexible numeric comparisons", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$matchAsRoot": { + "y": 2 + } + } + } + ] + } + ] + }, + { + "description": "matchAsRoot evaluates special operators", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$matchAsRoot": { + "y": 2, + "z": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "matchAsRoot with matchAsDocument", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 2, + "json": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "x": 1 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/test/unified_format.py b/test/unified_format.py index 7a38bd4b5e..6fca573725 100644 --- a/test/unified_format.py +++ b/test/unified_format.py @@ -1311,8 +1311,8 @@ def format_logs(log_list): if log.module == "ocsp_support": continue data = json_util.loads(log.getMessage()) - client = data.pop("clientId") if "clientId" in data else data.pop("topologyId") - client_to_log[client].append( + client_id = data.get("clientId", data.get("topologyId")) + client_to_log[client_id].append( { "level": log.levelname.lower(), "component": log.name.replace("pymongo.", "", 1), diff --git a/test/unified_format_shared.py b/test/unified_format_shared.py index 1c87fb3f18..0c685366f4 100644 --- a/test/unified_format_shared.py +++ b/test/unified_format_shared.py @@ -433,10 +433,12 @@ def _operation_lte(self, spec, actual, key_to_compare): self.test.assertLessEqual(actual[key_to_compare], spec) def _operation_matchAsDocument(self, spec, actual, key_to_compare): - self._match_document(spec, json_util.loads(actual[key_to_compare]), False) + self._match_document(spec, json_util.loads(actual[key_to_compare]), False, test=True) def _operation_matchAsRoot(self, spec, actual, key_to_compare): - self._match_document(spec, actual, True) + if key_to_compare: + actual = actual[key_to_compare] + self._match_document(spec, actual, True, test=True) def _evaluate_special_operation(self, opname, spec, actual, key_to_compare): method_name = "_operation_{}".format(opname.strip("$")) @@ -489,7 +491,7 @@ def _evaluate_if_special_operation(self, expectation, actual, key_to_compare=Non def _match_document(self, expectation, actual, is_root, test=False): if self._evaluate_if_special_operation(expectation, actual): - return + return True self.test.assertIsInstance(actual, abc.Mapping) for key, value in expectation.items(): @@ -521,25 +523,26 @@ def match_result(self, expectation, actual, in_recursive_call=False, test=True): self.test.assertIsInstance(actual, abc.MutableSequence) for e, a in zip(expectation, actual): if isinstance(e, abc.Mapping): - self._match_document(e, a, is_root=not in_recursive_call, test=test) + res = self._match_document(e, a, is_root=not in_recursive_call, test=test) else: - self.match_result(e, a, in_recursive_call=True, test=test) - return None + res = self.match_result(e, a, in_recursive_call=True, test=test) + if not res: + return False + return True # account for flexible numerics in element-wise comparison - if isinstance(expectation, int) or isinstance(expectation, float): + if isinstance(expectation, (int, float)): if test: self.test.assertEqual(expectation, actual) else: return expectation == actual - return None else: if test: self.test.assertIsInstance(actual, type(expectation)) self.test.assertEqual(expectation, actual) else: return isinstance(actual, type(expectation)) and expectation == actual - return None + return True def match_server_description(self, actual: ServerDescription, spec: dict) -> None: for field, expected in spec.items(): diff --git a/test/utils.py b/test/utils.py index 493d6f9422..b4a729a260 100644 --- a/test/utils.py +++ b/test/utils.py @@ -20,6 +20,7 @@ import copy import functools import os +import random import re import shutil import sys @@ -315,6 +316,7 @@ class MockConnection: def __init__(self): self.cancel_context = _CancellationContext() self.more_to_come = False + self.id = random.randint(0, 100) def close_conn(self, reason): pass