Skip to content

Commit

Permalink
Merge branch 'master' into PYTHON-4636
Browse files Browse the repository at this point in the history
  • Loading branch information
blink1073 authored Sep 30, 2024
2 parents 0e03581 + 821811e commit c8b8395
Show file tree
Hide file tree
Showing 37 changed files with 1,293 additions and 223 deletions.
3 changes: 3 additions & 0 deletions .evergreen/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ if [ -n "$PERF_TEST" ]; then
python -m pip install simplejson
start_time=$(date +%s)
TEST_SUITES="perf"
# PYTHON-4769 Run perf_test.py directly otherwise pytest's test collection negatively
# affects the benchmark results.
TEST_ARGS="test/performance/perf_test.py $TEST_ARGS"
fi

echo "Running $AUTH tests over $SSL with python $(which python)"
Expand Down
4 changes: 2 additions & 2 deletions bson/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1324,7 +1324,7 @@ def decode_iter(
elements = data[position : position + obj_size]
position += obj_size

yield _bson_to_dict(elements, opts) # type:ignore[misc, type-var]
yield _bson_to_dict(elements, opts) # type:ignore[misc]


@overload
Expand Down Expand Up @@ -1370,7 +1370,7 @@ def decode_file_iter(
raise InvalidBSON("cut off in middle of objsize")
obj_size = _UNPACK_INT_FROM(size_data, 0)[0] - 4
elements = size_data + file_obj.read(max(0, obj_size))
yield _bson_to_dict(elements, opts) # type:ignore[type-var, arg-type, misc]
yield _bson_to_dict(elements, opts) # type:ignore[arg-type, misc]


def is_valid(bson: bytes) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion bson/decimal128.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ def __init__(self, value: _VALUE_OPTIONS) -> None:
"from list or tuple. Must have exactly 2 "
"elements."
)
self.__high, self.__low = value # type: ignore
self.__high, self.__low = value
else:
raise TypeError(f"Cannot convert {value!r} to Decimal128")

Expand Down
2 changes: 1 addition & 1 deletion bson/json_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ def __new__(
"JSONOptions.datetime_representation must be one of LEGACY, "
"NUMBERLONG, or ISO8601 from DatetimeRepresentation."
)
self = cast(JSONOptions, super().__new__(cls, *args, **kwargs)) # type:ignore[arg-type]
self = cast(JSONOptions, super().__new__(cls, *args, **kwargs))
if json_mode not in (JSONMode.LEGACY, JSONMode.RELAXED, JSONMode.CANONICAL):
raise ValueError(
"JSONOptions.json_mode must be one of LEGACY, RELAXED, "
Expand Down
2 changes: 1 addition & 1 deletion bson/son.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def __init__(
self.update(kwargs)

def __new__(cls: Type[SON[_Key, _Value]], *args: Any, **kwargs: Any) -> SON[_Key, _Value]:
instance = super().__new__(cls, *args, **kwargs) # type: ignore[type-var]
instance = super().__new__(cls, *args, **kwargs)
instance.__keys = []
return instance

Expand Down
5 changes: 3 additions & 2 deletions hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ features = ["docs","test"]
test = "sphinx-build -E -b doctest doc ./doc/_build/doctest"

[envs.typing]
features = ["encryption", "ocsp", "zstd", "aws"]
dependencies = ["mypy==1.2.0","pyright==1.1.290", "certifi", "typing_extensions"]
pre-install-commands = [
"pip install -q -r requirements/typing.txt",
]
[envs.typing.scripts]
check-mypy = [
"mypy --install-types --non-interactive bson gridfs tools pymongo",
Expand Down
21 changes: 9 additions & 12 deletions pymongo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +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
from pymongo.common import MAX_SUPPORTED_WIRE_VERSION, MIN_SUPPORTED_WIRE_VERSION, has_c
from pymongo.cursor import CursorType
from pymongo.operations import (
DeleteMany,
Expand All @@ -105,18 +104,16 @@
from pymongo.synchronous.mongo_client import MongoClient
from pymongo.write_concern import WriteConcern

version = __version__
"""Current version of PyMongo."""

try:
from pymongo.asynchronous.mongo_client import AsyncMongoClient
except Exception as e:
# PYTHON-4781: Importing asyncio can fail on Windows.
import warnings as _warnings

def has_c() -> bool:
"""Is the C extension installed?"""
try:
from pymongo import _cmessage # type: ignore[attr-defined] # noqa: F401
_warnings.warn(f"Failed to import Async PyMongo: {e!r}", ImportWarning, stacklevel=2)

return True
except ImportError:
return False
version = __version__
"""Current version of PyMongo."""


def timeout(seconds: Optional[float]) -> ContextManager[None]:
Expand Down
3 changes: 1 addition & 2 deletions pymongo/_csot.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,13 @@ def __init__(self, timeout: Optional[float]):
self._timeout = timeout
self._tokens: Optional[tuple[Token[Optional[float]], Token[float], Token[float]]] = None

def __enter__(self) -> _TimeoutContext:
def __enter__(self) -> None:
timeout_token = TIMEOUT.set(self._timeout)
prev_deadline = DEADLINE.get()
next_deadline = time.monotonic() + self._timeout if self._timeout else float("inf")
deadline_token = DEADLINE.set(min(prev_deadline, next_deadline))
rtt_token = RTT.set(0.0)
self._tokens = (timeout_token, deadline_token, rtt_token)
return self

def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
if self._tokens:
Expand Down
23 changes: 22 additions & 1 deletion pymongo/asynchronous/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
TypeVar,
Union,
cast,
overload,
)

from bson.codec_options import DEFAULT_CODEC_OPTIONS, CodecOptions
Expand Down Expand Up @@ -332,13 +333,33 @@ def database(self) -> AsyncDatabase[_DocumentType]:
"""
return self._database

@overload
def with_options(
self,
codec_options: None = None,
read_preference: Optional[_ServerMode] = ...,
write_concern: Optional[WriteConcern] = ...,
read_concern: Optional[ReadConcern] = ...,
) -> AsyncCollection[_DocumentType]:
...

@overload
def with_options(
self,
codec_options: bson.CodecOptions[_DocumentTypeArg],
read_preference: Optional[_ServerMode] = ...,
write_concern: Optional[WriteConcern] = ...,
read_concern: Optional[ReadConcern] = ...,
) -> AsyncCollection[_DocumentTypeArg]:
...

def with_options(
self,
codec_options: Optional[bson.CodecOptions[_DocumentTypeArg]] = None,
read_preference: Optional[_ServerMode] = None,
write_concern: Optional[WriteConcern] = None,
read_concern: Optional[ReadConcern] = None,
) -> AsyncCollection[_DocumentType]:
) -> AsyncCollection[_DocumentType] | AsyncCollection[_DocumentTypeArg]:
"""Get a clone of this collection changing the specified settings.
>>> coll1.read_preference
Expand Down
22 changes: 21 additions & 1 deletion pymongo/asynchronous/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,33 @@ def name(self) -> str:
"""The name of this :class:`AsyncDatabase`."""
return self._name

@overload
def with_options(
self,
codec_options: None = None,
read_preference: Optional[_ServerMode] = ...,
write_concern: Optional[WriteConcern] = ...,
read_concern: Optional[ReadConcern] = ...,
) -> AsyncDatabase[_DocumentType]:
...

@overload
def with_options(
self,
codec_options: bson.CodecOptions[_DocumentTypeArg],
read_preference: Optional[_ServerMode] = ...,
write_concern: Optional[WriteConcern] = ...,
read_concern: Optional[ReadConcern] = ...,
) -> AsyncDatabase[_DocumentTypeArg]:
...

def with_options(
self,
codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None,
read_preference: Optional[_ServerMode] = None,
write_concern: Optional[WriteConcern] = None,
read_concern: Optional[ReadConcern] = None,
) -> AsyncDatabase[_DocumentType]:
) -> AsyncDatabase[_DocumentType] | AsyncDatabase[_DocumentTypeArg]:
"""Get a clone of this database changing the specified settings.
>>> db1.read_preference
Expand Down
9 changes: 5 additions & 4 deletions pymongo/asynchronous/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ async def _configured_socket(
and not options.tls_allow_invalid_hostnames
):
try:
ssl.match_hostname(ssl_sock.getpeercert(), hostname=host)
ssl.match_hostname(ssl_sock.getpeercert(), hostname=host) # type:ignore[attr-defined]
except _CertificateError:
ssl_sock.close()
raise
Expand Down Expand Up @@ -992,7 +992,8 @@ def __init__(
# from the right side.
self.conns: collections.deque = collections.deque()
self.active_contexts: set[_CancellationContext] = set()
self.lock = _ALock(_create_lock())
_lock = _create_lock()
self.lock = _ALock(_lock)
self.active_sockets = 0
# Monotonically increasing connection ID required for CMAP Events.
self.next_connection_id = 1
Expand All @@ -1018,15 +1019,15 @@ def __init__(
# The first portion of the wait queue.
# Enforces: maxPoolSize
# Also used for: clearing the wait queue
self.size_cond = _ACondition(threading.Condition(self.lock)) # type: ignore[arg-type]
self.size_cond = _ACondition(threading.Condition(_lock))
self.requests = 0
self.max_pool_size = self.opts.max_pool_size
if not self.max_pool_size:
self.max_pool_size = float("inf")
# The second portion of the wait queue.
# Enforces: maxConnecting
# Also used for: clearing the wait queue
self._max_connecting_cond = _ACondition(threading.Condition(self.lock)) # type: ignore[arg-type]
self._max_connecting_cond = _ACondition(threading.Condition(_lock))
self._max_connecting = self.opts.max_connecting
self._pending = 0
self._client_id = client_id
Expand Down
5 changes: 3 additions & 2 deletions pymongo/asynchronous/topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,9 @@ def __init__(self, topology_settings: TopologySettings):
self._seed_addresses = list(topology_description.server_descriptions())
self._opened = False
self._closed = False
self._lock = _ALock(_create_lock())
self._condition = _ACondition(self._settings.condition_class(self._lock)) # type: ignore[arg-type]
_lock = _create_lock()
self._lock = _ALock(_lock)
self._condition = _ACondition(self._settings.condition_class(_lock))
self._servers: dict[_Address, Server] = {}
self._pid: Optional[int] = None
self._max_cluster_time: Optional[ClusterTime] = None
Expand Down
12 changes: 11 additions & 1 deletion pymongo/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,7 @@ def get_normed_key(x: str) -> str:
return x

def get_setter_key(x: str) -> str:
return options.cased_key(x) # type: ignore[attr-defined]
return options.cased_key(x)

else:
validated_options = {}
Expand Down Expand Up @@ -1060,3 +1060,13 @@ def update(self, other: Mapping[str, Any]) -> None: # type: ignore[override]

def cased_key(self, key: str) -> Any:
return self.__casedkeys[key.lower()]


def has_c() -> bool:
"""Is the C extension installed?"""
try:
from pymongo import _cmessage # type: ignore[attr-defined] # noqa: F401

return True
except ImportError:
return False
2 changes: 1 addition & 1 deletion pymongo/compression_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

def _have_snappy() -> bool:
try:
import snappy # type:ignore[import] # noqa: F401
import snappy # type:ignore[import-not-found] # noqa: F401

return True
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion pymongo/encryption_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from typing import TYPE_CHECKING, Any, Mapping, Optional

try:
import pymongocrypt # type:ignore[import] # noqa: F401
import pymongocrypt # type:ignore[import-untyped] # noqa: F401

# Check for pymongocrypt>=1.10.
from pymongocrypt import synchronous as _ # noqa: F401
Expand Down
Loading

0 comments on commit c8b8395

Please sign in to comment.