Skip to content

Commit

Permalink
Merge branch 'master' into DRIVERS-2926-BSON-Binary-Vectors
Browse files Browse the repository at this point in the history
  • Loading branch information
blink1073 authored Oct 1, 2024
2 parents 8aaa2f6 + 545b88c commit dfb322c
Show file tree
Hide file tree
Showing 41 changed files with 1,312 additions and 226 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: 5 additions & 0 deletions doc/async-tutorial.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Async Tutorial
==============

.. warning:: This API is currently in beta, meaning the classes, methods,
and behaviors described within may change before the full release.
If you come across any bugs during your use of this API,
please file a Jira ticket in the "Python Driver" project at https://jira.mongodb.org/browse/PYTHON.

.. code-block:: pycon
from pymongo import AsyncMongoClient
Expand Down
13 changes: 12 additions & 1 deletion doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,20 @@ Changelog
Changes in Version 4.10.0
-------------------------

- Provisional **(BETA)** support for a new Binary BSON subtype (9) used for efficient storage and retrieval of vectors:
- Added provisional **(BETA)** support for a new Binary BSON subtype (9) used for efficient storage and retrieval of vectors:
densely packed arrays of numbers, all of the same type.
This includes new methods :meth:`~bson.binary.Binary.from_vector` and :meth:`~bson.binary.Binary.as_vector`.
- Added C extension use to client metadata, for example: ``{"driver": {"name": "PyMongo|c", "version": "4.10.0"}, ...}``
- Fixed a bug where :class:`~pymongo.asynchronous.mongo_client.AsyncMongoClient` could deadlock.
- Fixed a bug where PyMongo could fail to import on Windows if ``asyncio`` is misconfigured.

Issues Resolved
...............

See the `PyMongo 4.10 release notes in JIRA`_ for the list of resolved issues
in this release.

.. _PyMongo 4.10 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=40553

Changes in Version 4.9.0
-------------------------
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 dfb322c

Please sign in to comment.