Compare commits

...

10 Commits
master ... v4.7

Author SHA1 Message Date
Steven Silvester
c36bfbc3f9
PYTHON-4373 [v4.7] Add SBOM file (#1658) 2024-06-05 14:52:57 -05:00
Steven Silvester
7604387148
BUMP 4.7.4.dev0 2024-06-04 13:44:12 -05:00
Steven Silvester
398be02d23
BUMP 4.7.3 2024-06-04 13:43:20 -05:00
Shane Harvey
839904f4f9
PYTHON-4261 [v4.7] Reduce verbosity of "Waiting for suitable server to become available" log message (#1657) 2024-06-04 11:02:50 -07:00
Steven Silvester
c7db2c215f
PYTHON-4384 [v4.7] Add Custom CodeQL Scanning (#1655) 2024-06-03 13:18:31 -05:00
Steven Silvester
358a4864c1
PYTHON-4441 [v4.7] Use deferred imports instead of lazy module loading (#1650) 2024-06-03 12:49:10 -05:00
Shane Harvey
fa80968120
PYTHON-4473 Optimize find/aggregate/command by avoiding duplicate calls to _get_topology (#1652) (#1654) 2024-05-31 18:03:00 -07:00
Steven Silvester
d4592b659f
PYTHON-4455 [v4.7] Improve import time on Windows (#1647) 2024-05-30 15:46:38 -05:00
Steven Silvester
f38dfd672e
BUMP 4.7.3.dev0 2024-05-07 15:22:24 -05:00
Steven Silvester
29f4d5cf89
PYTHON-4429 Prep for 4.7.2 Release (#1633) 2024-05-07 15:19:26 -05:00
28 changed files with 237 additions and 148 deletions

62
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,62 @@
name: "CodeQL"
on:
push:
branches: [ "master", "v*"]
tags: ['*']
pull_request:
schedule:
- cron: '17 10 * * 2'
concurrency:
group: codeql-${{ github.ref }}
cancel-in-progress: true
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: "ubuntu-latest"
timeout-minutes: 360
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
strategy:
fail-fast: false
matrix:
include:
- language: c-cpp
build-mode: manual
- language: python
build-mode: none
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/setup-python@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
queries: security-extended
config: |
paths-ignore:
- '.github/**'
- 'doc/**'
- 'tools/**'
- 'test/**'
- if: matrix.build-mode == 'manual'
run: |
pip install -e .
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@ -53,7 +53,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-20.04]
python-version: ["3.7", "3.11", "pypy-3.8"]
python-version: ["3.7", "3.11", "pypy-3.9"]
name: CPython ${{ matrix.python-version }}-${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@ -137,7 +137,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python: ["3.7", "3.11"]
python: ["3.8", "3.11"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5

View File

@ -8,6 +8,7 @@ exclude .git-blame-ignore-revs
exclude .pre-commit-config.yaml
exclude .readthedocs.yaml
exclude CONTRIBUTING.md
include sbom.json
exclude RELEASE.md
recursive-include doc *.rst
recursive-include doc *.py

View File

@ -1,6 +1,39 @@
Changelog
=========
Changes in Version 4.7.3
-------------------------
Version 4.7.3 has further fixes for lazily loading modules.
- Use deferred imports instead of importlib lazy module loading.
- Improve import time on Windows.
- Reduce verbosity of "Waiting for suitable server to become available" log message from info to debug.
Issues Resolved
...............
See the `PyMongo 4.7.3 release notes in JIRA`_ for the list of resolved issues
in this release.
.. _PyMongo 4.7.3 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=39865
Changes in Version 4.7.2
-------------------------
Version 4.7.2 fixes a bug introduced in 4.7.0:
- Fixed a bug where PyMongo could not be used with the Nuitka compiler.
Issues Resolved
...............
See the `PyMongo 4.7.2 release notes in JIRA`_ for the list of resolved issues
in this release.
.. _PyMongo 4.7.2 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=39710
Changes in Version 4.7.1
-------------------------

View File

@ -16,10 +16,11 @@
from __future__ import annotations
from typing import Any
from urllib.request import Request, urlopen
def _get_gcp_response(resource: str, timeout: float = 5) -> dict[str, Any]:
from urllib.request import Request, urlopen
url = "http://metadata/computeMetadata/v1/instance/service-accounts/default/identity"
url += f"?audience={resource}"
headers = {"Metadata-Flavor": "Google"}

View File

@ -1,43 +0,0 @@
# Copyright 2024-present MongoDB, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you
# may not use this file except in compliance with the License. You
# may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.
from __future__ import annotations
import importlib.util
import sys
from types import ModuleType
def lazy_import(name: str) -> ModuleType:
"""Lazily import a module by name
From https://docs.python.org/3/library/importlib.html#implementing-lazy-imports
"""
# Workaround for PYTHON-4424.
if "__compiled__" in globals():
return importlib.import_module(name)
try:
spec = importlib.util.find_spec(name)
except ValueError:
# Note: this cannot be ModuleNotFoundError, see PYTHON-4424.
raise ImportError(name=name) from None
if spec is None:
# Note: this cannot be ModuleNotFoundError, see PYTHON-4424.
raise ImportError(name=name)
assert spec is not None
loader = importlib.util.LazyLoader(spec.loader) # type:ignore[arg-type]
spec.loader = loader
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
loader.exec_module(module)
return module

View File

@ -17,7 +17,7 @@ from __future__ import annotations
from typing import Tuple, Union
version_tuple: Tuple[Union[int, str], ...] = (4, 8, 0, ".dev0")
version_tuple: Tuple[Union[int, str], ...] = (4, 7, 4, ".dev0")
def get_version_string() -> str:

View File

@ -15,15 +15,6 @@
"""MONGODB-AWS Authentication helpers."""
from __future__ import annotations
from pymongo._lazy_import import lazy_import
try:
pymongo_auth_aws = lazy_import("pymongo_auth_aws")
_HAVE_MONGODB_AWS = True
except ImportError:
_HAVE_MONGODB_AWS = False
from typing import TYPE_CHECKING, Any, Mapping, Type
import bson
@ -38,11 +29,13 @@ if TYPE_CHECKING:
def _authenticate_aws(credentials: MongoCredential, conn: Connection) -> None:
"""Authenticate using MONGODB-AWS."""
if not _HAVE_MONGODB_AWS:
try:
import pymongo_auth_aws # type:ignore[import]
except ImportError as e:
raise ConfigurationError(
"MONGODB-AWS authentication requires pymongo-auth-aws: "
"install with: python -m pip install 'pymongo[aws]'"
)
) from e
# Delayed import.
from pymongo_auth_aws.auth import ( # type:ignore[import]

View File

@ -19,7 +19,6 @@ from typing import TYPE_CHECKING, Any, Mapping, Optional, Sequence, cast
from bson.codec_options import _parse_codec_options
from pymongo import common
from pymongo.auth import MongoCredential, _build_credentials_tuple
from pymongo.compression_support import CompressionSettings
from pymongo.errors import ConfigurationError
from pymongo.monitoring import _EventListener, _EventListeners
@ -36,6 +35,7 @@ from pymongo.write_concern import WriteConcern, validate_boolean
if TYPE_CHECKING:
from bson.codec_options import CodecOptions
from pymongo.auth import MongoCredential
from pymongo.encryption_options import AutoEncryptionOpts
from pymongo.pyopenssl_context import SSLContext
from pymongo.topology_description import _ServerSelector
@ -48,6 +48,8 @@ def _parse_credentials(
mechanism = options.get("authmechanism", "DEFAULT" if username else None)
source = options.get("authsource")
if username or mechanism:
from pymongo.auth import _build_credentials_tuple
return _build_credentials_tuple(mechanism, source, username, password, options, database)
return None

View File

@ -40,8 +40,6 @@ from bson import SON
from bson.binary import UuidRepresentation
from bson.codec_options import CodecOptions, DatetimeConversion, TypeRegistry
from bson.raw_bson import RawBSONDocument
from pymongo.auth import MECHANISMS
from pymongo.auth_oidc import OIDCCallback
from pymongo.compression_support import (
validate_compressors,
validate_zlib_compression_level,
@ -380,6 +378,8 @@ def validate_read_preference_mode(dummy: Any, value: Any) -> _ServerMode:
def validate_auth_mechanism(option: str, value: Any) -> str:
"""Validate the authMechanism URI option."""
from pymongo.auth import MECHANISMS
if value not in MECHANISMS:
raise ValueError(f"{option} must be in {tuple(MECHANISMS)}")
return value
@ -444,6 +444,8 @@ def validate_auth_mechanism_properties(option: str, value: Any) -> dict[str, Uni
elif key in ["ALLOWED_HOSTS"] and isinstance(value, list):
props[key] = value
elif key in ["OIDC_CALLBACK", "OIDC_HUMAN_CALLBACK"]:
from pymongo.auth_oidc import OIDCCallback
if not isinstance(value, OIDCCallback):
raise ValueError("callback must be an OIDCCallback object")
props[key] = value

View File

@ -16,36 +16,41 @@ from __future__ import annotations
import warnings
from typing import Any, Iterable, Optional, Union
from pymongo._lazy_import import lazy_import
from pymongo.hello import HelloCompat
from pymongo.monitoring import _SENSITIVE_COMMANDS
try:
snappy = lazy_import("snappy")
_HAVE_SNAPPY = True
except ImportError:
# python-snappy isn't available.
_HAVE_SNAPPY = False
try:
zlib = lazy_import("zlib")
_HAVE_ZLIB = True
except ImportError:
# Python built without zlib support.
_HAVE_ZLIB = False
try:
zstandard = lazy_import("zstandard")
_HAVE_ZSTD = True
except ImportError:
_HAVE_ZSTD = False
from pymongo.helpers import _SENSITIVE_COMMANDS
_SUPPORTED_COMPRESSORS = {"snappy", "zlib", "zstd"}
_NO_COMPRESSION = {HelloCompat.CMD, HelloCompat.LEGACY_CMD}
_NO_COMPRESSION.update(_SENSITIVE_COMMANDS)
def _have_snappy() -> bool:
try:
import snappy # type:ignore[import] # noqa: F401
return True
except ImportError:
return False
def _have_zlib() -> bool:
try:
import zlib # noqa: F401
return True
except ImportError:
return False
def _have_zstd() -> bool:
try:
import zstandard # noqa: F401
return True
except ImportError:
return False
def validate_compressors(dummy: Any, value: Union[str, Iterable[str]]) -> list[str]:
try:
# `value` is string.
@ -58,21 +63,21 @@ def validate_compressors(dummy: Any, value: Union[str, Iterable[str]]) -> list[s
if compressor not in _SUPPORTED_COMPRESSORS:
compressors.remove(compressor)
warnings.warn(f"Unsupported compressor: {compressor}", stacklevel=2)
elif compressor == "snappy" and not _HAVE_SNAPPY:
elif compressor == "snappy" and not _have_snappy():
compressors.remove(compressor)
warnings.warn(
"Wire protocol compression with snappy is not available. "
"You must install the python-snappy module for snappy support.",
stacklevel=2,
)
elif compressor == "zlib" and not _HAVE_ZLIB:
elif compressor == "zlib" and not _have_zlib():
compressors.remove(compressor)
warnings.warn(
"Wire protocol compression with zlib is not available. "
"The zlib module is not available.",
stacklevel=2,
)
elif compressor == "zstd" and not _HAVE_ZSTD:
elif compressor == "zstd" and not _have_zstd():
compressors.remove(compressor)
warnings.warn(
"Wire protocol compression with zstandard is not available. "
@ -117,6 +122,8 @@ class SnappyContext:
@staticmethod
def compress(data: bytes) -> bytes:
import snappy
return snappy.compress(data)
@ -127,6 +134,8 @@ class ZlibContext:
self.level = level
def compress(self, data: bytes) -> bytes:
import zlib
return zlib.compress(data, self.level)
@ -137,6 +146,8 @@ class ZstdContext:
def compress(data: bytes) -> bytes:
# ZstdCompressor is not thread safe.
# TODO: Use a pool?
import zstandard
return zstandard.ZstdCompressor().compress(data)
@ -146,12 +157,18 @@ def decompress(data: bytes, compressor_id: int) -> bytes:
# https://github.com/andrix/python-snappy/issues/65
# This only matters when data is a memoryview since
# id(bytes(data)) == id(data) when data is a bytes.
import snappy
return snappy.uncompress(bytes(data))
elif compressor_id == ZlibContext.compressor_id:
import zlib
return zlib.decompress(data)
elif compressor_id == ZstdContext.compressor_id:
# ZstdDecompressor is not thread safe.
# TODO: Use a pool?
import zstandard
return zstandard.ZstdDecompressor().decompress(data)
else:
raise ValueError("Unknown compressorId %d" % (compressor_id,))

View File

@ -93,6 +93,21 @@ _REAUTHENTICATION_REQUIRED_CODE: int = 391
# Server code raised when authentication fails.
_AUTHENTICATION_FAILURE_CODE: int = 18
# Note - to avoid bugs from forgetting which if these is all lowercase and
# which are camelCase, and at the same time avoid having to add a test for
# every command, use all lowercase here and test against command_name.lower().
_SENSITIVE_COMMANDS: set = {
"authenticate",
"saslstart",
"saslcontinue",
"getnonce",
"createuser",
"updateuser",
"copydbgetnonce",
"copydbsaslstart",
"copydb",
}
def _gen_index_name(keys: _IndexList) -> str:
"""Generate an index name from the set of fields it is over."""

View File

@ -1344,8 +1344,9 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
# always send primaryPreferred when directly connected to a repl set
# member.
# Thread safe: if the type is single it cannot change.
topology = self._get_topology()
single = topology.description.topology_type == TOPOLOGY_TYPE.Single
# NOTE: We already opened the Topology when selecting a server so there's no need
# to call _get_topology() again.
single = self._topology.description.topology_type == TOPOLOGY_TYPE.Single
with self._checkout(server, session) as conn:
if single:
@ -1365,7 +1366,6 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
operation: str,
) -> ContextManager[tuple[Connection, _ServerMode]]:
assert read_preference is not None, "read_preference must not be None"
_ = self._get_topology()
server = self._select_server(read_preference, session, operation)
return self._conn_from_server(read_preference, server, session)

View File

@ -191,7 +191,7 @@ from typing import TYPE_CHECKING, Any, Mapping, Optional, Sequence
from bson.objectid import ObjectId
from pymongo.hello import Hello, HelloCompat
from pymongo.helpers import _handle_exception
from pymongo.helpers import _SENSITIVE_COMMANDS, _handle_exception
from pymongo.typings import _Address, _DocumentOut
if TYPE_CHECKING:
@ -507,22 +507,6 @@ def register(listener: _EventListener) -> None:
_LISTENERS.cmap_listeners.append(listener)
# Note - to avoid bugs from forgetting which if these is all lowercase and
# which are camelCase, and at the same time avoid having to add a test for
# every command, use all lowercase here and test against command_name.lower().
_SENSITIVE_COMMANDS: set = {
"authenticate",
"saslstart",
"saslcontinue",
"getnonce",
"createuser",
"updateuser",
"copydbgetnonce",
"copydbsaslstart",
"copydb",
}
# The "hello" command is also deemed sensitive when attempting speculative
# authentication.
def _is_speculative_authenticate(command_name: str, doc: Mapping[str, Any]) -> bool:

View File

@ -41,7 +41,7 @@ from typing import (
import bson
from bson import DEFAULT_CODEC_OPTIONS
from pymongo import __version__, _csot, auth, helpers
from pymongo import __version__, _csot, helpers
from pymongo.client_session import _validate_session_write_concern
from pymongo.common import (
MAX_BSON_SIZE,
@ -211,13 +211,14 @@ elif sys.platform == "darwin":
"version": platform.mac_ver()[0],
}
elif sys.platform == "win32":
_ver = sys.getwindowsversion()
_METADATA["os"] = {
"type": platform.system(),
# "Windows XP", "Windows 7", "Windows 10", etc.
"name": " ".join((platform.system(), platform.release())),
"architecture": platform.machine(),
# Windows patch level (e.g. 5.1.2600-SP3)
"version": "-".join(platform.win32_ver()[1:3]),
"type": "Windows",
"name": "Windows",
# Avoid using platform calls, see PYTHON-4455.
"architecture": os.environ.get("PROCESSOR_ARCHITECTURE") or platform.machine(),
# Windows patch level (e.g. 10.0.17763-SP0).
"version": ".".join(map(str, _ver[:3])) + f"-SP{_ver[-1] or '0'}",
}
elif sys.platform.startswith("java"):
_name, _ver, _arch = platform.java_ver()[-1]
@ -859,6 +860,8 @@ class Connection:
if creds:
if creds.mechanism == "DEFAULT" and creds.username:
cmd["saslSupportedMechs"] = creds.source + "." + creds.username
from pymongo import auth
auth_ctx = auth._AuthContext.from_credentials(creds, self.address)
if auth_ctx:
speculative_authenticate = auth_ctx.speculate_command()
@ -1090,6 +1093,8 @@ class Connection:
if not self.ready:
creds = self.opts._credentials
if creds:
from pymongo import auth
auth.authenticate(creds, self, reauthenticate=reauthenticate)
self.ready = True
if self.enabled_for_cmap:

View File

@ -25,10 +25,11 @@ from errno import EINTR as _EINTR
from ipaddress import ip_address as _ip_address
from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union
import cryptography.x509 as x509
import service_identity
from OpenSSL import SSL as _SSL
from OpenSSL import crypto as _crypto
from pymongo._lazy_import import lazy_import
from pymongo.errors import ConfigurationError as _ConfigurationError
from pymongo.errors import _CertificateError # type:ignore[attr-defined]
from pymongo.ocsp_cache import _OCSPCache
@ -37,14 +38,9 @@ from pymongo.socket_checker import SocketChecker as _SocketChecker
from pymongo.socket_checker import _errno_from_exception
from pymongo.write_concern import validate_boolean
_x509 = lazy_import("cryptography.x509")
_service_identity = lazy_import("service_identity")
_service_identity_pyopenssl = lazy_import("service_identity.pyopenssl")
if TYPE_CHECKING:
from ssl import VerifyMode
from cryptography.x509 import Certificate
_T = TypeVar("_T")
@ -184,7 +180,7 @@ class _CallbackData:
"""Data class which is passed to the OCSP callback."""
def __init__(self) -> None:
self.trusted_ca_certs: Optional[list[Certificate]] = None
self.trusted_ca_certs: Optional[list[x509.Certificate]] = None
self.check_ocsp_endpoint: Optional[bool] = None
self.ocsp_response_cache = _OCSPCache()
@ -336,11 +332,12 @@ class SSLContext:
"""Attempt to load CA certs from Windows trust store."""
cert_store = self._ctx.get_cert_store()
oid = _stdlibssl.Purpose.SERVER_AUTH.oid
for cert, encoding, trust in _stdlibssl.enum_certificates(store): # type: ignore
if encoding == "x509_asn":
if trust is True or oid in trust:
cert_store.add_cert(
_crypto.X509.from_cryptography(_x509.load_der_x509_certificate(cert))
_crypto.X509.from_cryptography(x509.load_der_x509_certificate(cert))
)
def load_default_certs(self) -> None:
@ -404,14 +401,16 @@ class SSLContext:
# XXX: Do this in a callback registered with
# SSLContext.set_info_callback? See Twisted for an example.
if self.check_hostname and server_hostname is not None:
from service_identity import pyopenssl
try:
if _is_ip_address(server_hostname):
_service_identity_pyopenssl.verify_ip_address(ssl_conn, server_hostname)
pyopenssl.verify_ip_address(ssl_conn, server_hostname)
else:
_service_identity_pyopenssl.verify_hostname(ssl_conn, server_hostname)
except (
_service_identity.SICertificateError,
_service_identity.SIVerificationError,
pyopenssl.verify_hostname(ssl_conn, server_hostname)
except ( # type:ignore[misc]
service_identity.SICertificateError,
service_identity.SIVerificationError,
) as exc:
raise _CertificateError(str(exc)) from None
return ssl_conn

View File

@ -17,17 +17,22 @@ from __future__ import annotations
import ipaddress
import random
from typing import Any, Optional, Union
from typing import TYPE_CHECKING, Any, Optional, Union
from pymongo.common import CONNECT_TIMEOUT
from pymongo.errors import ConfigurationError
try:
if TYPE_CHECKING:
from dns import resolver
_HAVE_DNSPYTHON = True
except ImportError:
_HAVE_DNSPYTHON = False
def _have_dnspython() -> bool:
try:
import dns # noqa: F401
return True
except ImportError:
return False
# dnspython can return bytes or str from various parts
@ -40,6 +45,8 @@ def maybe_decode(text: Union[str, bytes]) -> str:
# PYTHON-2667 Lazily call dns.resolver methods for compatibility with eventlet.
def _resolve(*args: Any, **kwargs: Any) -> resolver.Answer:
from dns import resolver
if hasattr(resolver, "resolve"):
# dnspython >= 2
return resolver.resolve(*args, **kwargs)
@ -81,6 +88,8 @@ class _SrvResolver:
raise ConfigurationError(_INVALID_HOST_MSG % (fqdn,))
def get_options(self) -> Optional[str]:
from dns import resolver
try:
results = _resolve(self.__fqdn, "TXT", lifetime=self.__connect_timeout)
except (resolver.NoAnswer, resolver.NXDOMAIN):

View File

@ -44,7 +44,6 @@ from pymongo.lock import _create_lock
from pymongo.logger import (
_SERVER_SELECTION_LOGGER,
_debug_log,
_info_log,
_ServerSelectionStatusMessage,
)
from pymongo.monitor import SrvMonitor
@ -306,7 +305,7 @@ class Topology:
)
if not logged_waiting:
_info_log(
_debug_log(
_SERVER_SELECTION_LOGGER,
message=_ServerSelectionStatusMessage.WAITING,
selector=selector,

View File

@ -40,7 +40,7 @@ from pymongo.common import (
get_validated_options,
)
from pymongo.errors import ConfigurationError, InvalidURI
from pymongo.srv_resolver import _HAVE_DNSPYTHON, _SrvResolver
from pymongo.srv_resolver import _have_dnspython, _SrvResolver
from pymongo.typings import _Address
if TYPE_CHECKING:
@ -472,7 +472,7 @@ def parse_uri(
is_srv = False
scheme_free = uri[SCHEME_LEN:]
elif uri.startswith(SRV_SCHEME):
if not _HAVE_DNSPYTHON:
if not _have_dnspython():
python_path = sys.executable or "python"
raise ConfigurationError(
'The "dnspython" module must be '

10
sbom.json Normal file
View File

@ -0,0 +1,10 @@
{
"metadata": {
"timestamp": "2024-06-05T10:36:04.606968+00:00"
},
"serialNumber": "urn:uuid:a94f1412-ea1f-4821-b9f0-e14788b0776e",
"version": 1,
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5"
}

View File

@ -194,7 +194,7 @@
}
},
{
"level": "info",
"level": "debug",
"component": "serverSelection",
"data": {
"message": "Waiting for suitable server to become available",

View File

@ -184,7 +184,7 @@
}
},
{
"level": "info",
"level": "debug",
"component": "serverSelection",
"data": {
"message": "Waiting for suitable server to become available",

View File

@ -193,7 +193,7 @@
}
},
{
"level": "info",
"level": "debug",
"component": "serverSelection",
"data": {
"message": "Waiting for suitable server to become available",

View File

@ -211,7 +211,7 @@
}
},
{
"level": "info",
"level": "debug",
"component": "serverSelection",
"data": {
"message": "Waiting for suitable server to become available",

View File

@ -86,7 +86,7 @@ from pymongo import event_loggers, message, monitoring
from pymongo.client_options import ClientOptions
from pymongo.command_cursor import CommandCursor
from pymongo.common import _UUID_REPRESENTATIONS, CONNECT_TIMEOUT
from pymongo.compression_support import _HAVE_SNAPPY, _HAVE_ZSTD
from pymongo.compression_support import _have_snappy, _have_zstd
from pymongo.cursor import Cursor, CursorType
from pymongo.database import Database
from pymongo.driver_info import DriverInfo
@ -1558,7 +1558,7 @@ class TestClient(IntegrationTest):
self.assertEqual(opts.compressors, ["zlib"])
self.assertEqual(opts.zlib_compression_level, -1)
if not _HAVE_SNAPPY:
if not _have_snappy():
uri = "mongodb://localhost:27017/?compressors=snappy"
client = MongoClient(uri, connect=False)
opts = compression_settings(client)
@ -1573,7 +1573,7 @@ class TestClient(IntegrationTest):
opts = compression_settings(client)
self.assertEqual(opts.compressors, ["snappy", "zlib"])
if not _HAVE_ZSTD:
if not _have_zstd():
uri = "mongodb://localhost:27017/?compressors=zstd"
client = MongoClient(uri, connect=False)
opts = compression_settings(client)

View File

@ -28,7 +28,7 @@ import pymongo
from pymongo import common
from pymongo.errors import ConfigurationError
from pymongo.mongo_client import MongoClient
from pymongo.srv_resolver import _HAVE_DNSPYTHON
from pymongo.srv_resolver import _have_dnspython
WAIT_TIME = 0.1
@ -148,7 +148,7 @@ class TestSrvPolling(unittest.TestCase):
return True
def run_scenario(self, dns_response, expect_change):
self.assertEqual(_HAVE_DNSPYTHON, True)
self.assertEqual(_have_dnspython(), True)
if callable(dns_response):
dns_resolver_response = dns_response
else:

View File

@ -27,7 +27,7 @@ sys.path[0:0] = [""]
from test import clear_warning_registry, unittest
from pymongo.common import INTERNAL_URI_OPTION_NAME_MAP, validate
from pymongo.compression_support import _HAVE_SNAPPY
from pymongo.compression_support import _have_snappy
from pymongo.uri_parser import SRV_SCHEME, parse_uri
CONN_STRING_TEST_PATH = os.path.join(
@ -95,7 +95,7 @@ def run_scenario_in_dir(target_workdir):
def create_test(test, test_workdir):
def run_scenario(self):
compressors = (test.get("options") or {}).get("compressors", [])
if "snappy" in compressors and not _HAVE_SNAPPY:
if "snappy" in compressors and not _have_snappy():
self.skipTest("This test needs the snappy module.")
valid = True
warning = False

View File

@ -39,9 +39,9 @@ from pymongo.collection import ReturnDocument
from pymongo.cursor import CursorType
from pymongo.errors import ConfigurationError, OperationFailure
from pymongo.hello import HelloCompat
from pymongo.helpers import _SENSITIVE_COMMANDS
from pymongo.lock import _create_lock
from pymongo.monitoring import (
_SENSITIVE_COMMANDS,
ConnectionCheckedInEvent,
ConnectionCheckedOutEvent,
ConnectionCheckOutFailedEvent,