diff --git a/bson/__init__.py b/bson/__init__.py index 41865d2fd9..29ec155a03 100644 --- a/bson/__init__.py +++ b/bson/__init__.py @@ -90,9 +90,7 @@ BinaryIO, Callable, NoReturn, - Optional, TypeVar, - Union, cast, overload, ) @@ -248,7 +246,7 @@ def get_data_and_view(data: Any) -> tuple[Any, memoryview]: return data, memoryview(data) -def _raw_as_bytes(raw: Union[bytes, bytearray, memoryview]) -> bytes: +def _raw_as_bytes(raw: bytes | bytearray | memoryview) -> bytes: """Convert a raw BSON buffer (bytes, bytearray, or memoryview) to bytes.""" return raw if isinstance(raw, bytes) else bytes(raw) @@ -391,7 +389,7 @@ def _get_array( def _get_binary( data: Any, _view: Any, position: int, obj_end: int, opts: CodecOptions[Any], dummy1: Any -) -> tuple[Union[Binary, uuid.UUID], int]: +) -> tuple[Binary | uuid.UUID, int]: """Decode a BSON binary to bson.binary.Binary or python UUID.""" length, subtype = _UNPACK_LENGTH_SUBTYPE_FROM(data, position) position += 5 @@ -449,7 +447,7 @@ def _get_boolean( def _get_date( data: Any, _view: Any, position: int, dummy0: int, opts: CodecOptions[Any], dummy1: Any -) -> tuple[Union[datetime.datetime, DatetimeMS], int]: +) -> tuple[datetime.datetime | DatetimeMS, int]: """Decode a BSON datetime to python datetime.datetime.""" return _millis_to_datetime(_UNPACK_LONG_FROM(data, position)[0], opts), position + 8 @@ -691,7 +689,7 @@ def gen_list_name() -> Generator[bytes, None, None]: yield (str(next(counter)) + "\x00").encode("utf8") -def _make_c_string_check(string: Union[str, bytes]) -> bytes: +def _make_c_string_check(string: str | bytes) -> bytes: """Make a 'C' string, checking for embedded NUL characters.""" if isinstance(string, bytes): if b"\x00" in string: @@ -709,7 +707,7 @@ def _make_c_string_check(string: Union[str, bytes]) -> bytes: return _utf_8_encode(string)[0] + b"\x00" -def _make_c_string(string: Union[str, bytes]) -> bytes: +def _make_c_string(string: str | bytes) -> bytes: """Make a 'C' string.""" if isinstance(string, bytes): try: @@ -1094,8 +1092,8 @@ def decode(data: _ReadableBuffer, codec_options: CodecOptions[_DocumentType]) -> def decode( - data: _ReadableBuffer, codec_options: Optional[CodecOptions[_DocumentType]] = None -) -> Union[dict[str, Any], _DocumentType]: + data: _ReadableBuffer, codec_options: CodecOptions[_DocumentType] | None = None +) -> dict[str, Any] | _DocumentType: """Decode BSON to a document. By default, returns a BSON document represented as a Python @@ -1124,7 +1122,7 @@ def decode( if not isinstance(opts, CodecOptions): raise _CODEC_OPTIONS_TYPE_ERROR - return cast("Union[dict[str, Any], _DocumentType]", _bson_to_dict(data, opts)) + return cast("dict[str, Any] | _DocumentType", _bson_to_dict(data, opts)) def _decode_all(data: _ReadableBuffer, opts: CodecOptions[_DocumentType]) -> list[_DocumentType]: @@ -1177,8 +1175,8 @@ def decode_all( def decode_all( - data: _ReadableBuffer, codec_options: Optional[CodecOptions[_DocumentType]] = None -) -> Union[list[dict[str, Any]], list[_DocumentType]]: + data: _ReadableBuffer, codec_options: CodecOptions[_DocumentType] | None = None +) -> list[dict[str, Any]] | list[_DocumentType]: """Decode BSON data to multiple documents. `data` must be a bytes-like object implementing the buffer protocol that @@ -1232,7 +1230,7 @@ def _decode_selective( return doc -def _array_of_documents_to_buffer(data: Union[memoryview, bytes]) -> bytes: +def _array_of_documents_to_buffer(data: memoryview | bytes) -> bytes: # Extract the raw bytes of each document. position = 0 view = memoryview(data) @@ -1329,8 +1327,8 @@ def decode_iter( def decode_iter( - data: bytes, codec_options: Optional[CodecOptions[_DocumentType]] = None -) -> Union[Iterator[dict[str, Any]], Iterator[_DocumentType]]: + data: bytes, codec_options: CodecOptions[_DocumentType] | None = None +) -> Iterator[dict[str, Any]] | Iterator[_DocumentType]: """Decode BSON data to multiple documents as a generator. Works similarly to the decode_all function, but yields one document at a @@ -1365,20 +1363,20 @@ def decode_iter( @overload def decode_file_iter( - file_obj: Union[BinaryIO, IO[bytes]], codec_options: None = None + file_obj: BinaryIO | IO[bytes], codec_options: None = None ) -> Iterator[dict[str, Any]]: ... @overload def decode_file_iter( - file_obj: Union[BinaryIO, IO[bytes]], codec_options: CodecOptions[_DocumentType] + file_obj: BinaryIO | IO[bytes], codec_options: CodecOptions[_DocumentType] ) -> Iterator[_DocumentType]: ... def decode_file_iter( - file_obj: Union[BinaryIO, IO[bytes]], - codec_options: Optional[CodecOptions[_DocumentType]] = None, -) -> Union[Iterator[dict[str, Any]], Iterator[_DocumentType]]: + file_obj: BinaryIO | IO[bytes], + codec_options: CodecOptions[_DocumentType] | None = None, +) -> Iterator[dict[str, Any]] | Iterator[_DocumentType]: """Decode bson data from a file to multiple documents as a generator. Works similarly to the decode_all function, but reads from the file object diff --git a/bson/binary.py b/bson/binary.py index 85f59db527..5731a489eb 100644 --- a/bson/binary.py +++ b/bson/binary.py @@ -17,7 +17,7 @@ import warnings from collections.abc import Sequence from enum import Enum -from typing import TYPE_CHECKING, Any, Optional, Union, overload +from typing import TYPE_CHECKING, Any, overload from uuid import UUID """Tools for representing BSON binary data. @@ -240,7 +240,7 @@ class BinaryVector: def __init__( self, - data: Union[Sequence[float | int], npt.NDArray[np.number]], + data: Sequence[float | int] | npt.NDArray[np.number], dtype: BinaryVectorDtype, padding: int = 0, ): @@ -311,7 +311,7 @@ class Binary(bytes): def __new__( cls: type[Binary], - data: Union[memoryview, bytes, bytearray, _mmap, _array[Any]], + data: memoryview | bytes | bytearray | _mmap | _array[Any], subtype: int = BINARY_SUBTYPE, ) -> Binary: if not isinstance(subtype, int): @@ -434,7 +434,7 @@ def from_vector(cls: type[Binary], vector: BinaryVector) -> Binary: ... @overload def from_vector( cls: type[Binary], - vector: Union[list[int], list[float]], + vector: list[int] | list[float], dtype: BinaryVectorDtype, padding: int = 0, ) -> Binary: ... @@ -451,9 +451,9 @@ def from_vector( @classmethod def from_vector( cls: type[Binary], - vector: Union[BinaryVector, list[int], list[float], npt.NDArray[np.number]], - dtype: Optional[BinaryVectorDtype] = None, - padding: Optional[int] = None, + vector: BinaryVector | list[int] | list[float] | npt.NDArray[np.number], + dtype: BinaryVectorDtype | None = None, + padding: int | None = None, ) -> Binary: """Create a BSON :class:`~bson.binary.Binary` of Vector subtype. diff --git a/bson/code.py b/bson/code.py index 983c566a3d..d74090f79b 100644 --- a/bson/code.py +++ b/bson/code.py @@ -18,7 +18,7 @@ from collections.abc import Mapping from collections.abc import Mapping as _Mapping -from typing import Any, Optional, Union +from typing import Any class Code(str): @@ -49,12 +49,12 @@ class Code(str): """ _type_marker = 13 - __scope: Union[Mapping[str, Any], None] + __scope: Mapping[str, Any] | None def __new__( cls: type[Code], - code: Union[str, Code], - scope: Optional[Mapping[str, Any]] = None, + code: str | Code, + scope: Mapping[str, Any] | None = None, **kwargs: Any, ) -> Code: if not isinstance(code, str): @@ -84,7 +84,7 @@ def __new__( return self @property - def scope(self) -> Optional[Mapping[str, Any]]: + def scope(self) -> Mapping[str, Any] | None: """Scope dictionary for this instance or ``None``.""" return self.__scope diff --git a/bson/codec_options.py b/bson/codec_options.py index 622d317f44..40238ea753 100644 --- a/bson/codec_options.py +++ b/bson/codec_options.py @@ -27,8 +27,6 @@ Callable, Generic, NamedTuple, - Optional, - Union, cast, ) @@ -104,7 +102,7 @@ class TypeCodec(TypeEncoder, TypeDecoder): """ -_Codec = Union[TypeEncoder, TypeDecoder, TypeCodec] +_Codec = TypeEncoder | TypeDecoder | TypeCodec _Fallback = Callable[[Any], Any] @@ -136,8 +134,8 @@ class TypeRegistry: def __init__( self, - type_codecs: Optional[Iterable[_Codec]] = None, - fallback_encoder: Optional[_Fallback] = None, + type_codecs: Iterable[_Codec] | None = None, + fallback_encoder: _Fallback | None = None, ) -> None: self.__type_codecs = list(type_codecs or []) self._fallback_encoder = fallback_encoder @@ -168,7 +166,7 @@ def codecs(self) -> list[TypeEncoder | TypeDecoder | TypeCodec]: return self.__type_codecs @property - def fallback_encoder(self) -> Optional[_Fallback]: + def fallback_encoder(self) -> _Fallback | None: """The fallback encoder in this registry.""" return self._fallback_encoder @@ -238,9 +236,9 @@ class _BaseCodecOptions(NamedTuple): tz_aware: bool uuid_representation: int unicode_decode_error_handler: str - tzinfo: Optional[datetime.tzinfo] + tzinfo: datetime.tzinfo | None type_registry: TypeRegistry - datetime_conversion: Optional[DatetimeConversion] + datetime_conversion: DatetimeConversion | None if TYPE_CHECKING: @@ -249,20 +247,20 @@ class CodecOptions(tuple[_DocumentType], Generic[_DocumentType]): document_class: type[_DocumentType] tz_aware: bool uuid_representation: int - unicode_decode_error_handler: Optional[str] - tzinfo: Optional[datetime.tzinfo] + unicode_decode_error_handler: str | None + tzinfo: datetime.tzinfo | None type_registry: TypeRegistry - datetime_conversion: Optional[int] + datetime_conversion: int | None def __new__( cls: type[CodecOptions[_DocumentType]], - document_class: Optional[type[_DocumentType]] = ..., + document_class: type[_DocumentType] | None = ..., tz_aware: bool = ..., - uuid_representation: Optional[int] = ..., - unicode_decode_error_handler: Optional[str] = ..., - tzinfo: Optional[datetime.tzinfo] = ..., - type_registry: Optional[TypeRegistry] = ..., - datetime_conversion: Optional[int] = ..., + uuid_representation: int | None = ..., + unicode_decode_error_handler: str | None = ..., + tzinfo: datetime.tzinfo | None = ..., + type_registry: TypeRegistry | None = ..., + datetime_conversion: int | None = ..., ) -> CodecOptions[_DocumentType]: ... # CodecOptions API @@ -372,13 +370,13 @@ def __init__(self, *args, **kwargs): def __new__( cls: type[CodecOptions], - document_class: Optional[type[Mapping[str, Any]]] = None, + document_class: type[Mapping[str, Any]] | None = None, tz_aware: bool = False, - uuid_representation: Optional[int] = UuidRepresentation.UNSPECIFIED, + uuid_representation: int | None = UuidRepresentation.UNSPECIFIED, unicode_decode_error_handler: str = "strict", - tzinfo: Optional[datetime.tzinfo] = None, - type_registry: Optional[TypeRegistry] = None, - datetime_conversion: Optional[DatetimeConversion] = DatetimeConversion.DATETIME, + tzinfo: datetime.tzinfo | None = None, + type_registry: TypeRegistry | None = None, + datetime_conversion: DatetimeConversion | None = DatetimeConversion.DATETIME, ) -> CodecOptions: doc_class = document_class or dict # Generic aliases like SON[str, Any] or dict[str, Any] aren't classes, so diff --git a/bson/datetime_ms.py b/bson/datetime_ms.py index f5a38e63f6..698bee1a45 100644 --- a/bson/datetime_ms.py +++ b/bson/datetime_ms.py @@ -21,7 +21,7 @@ import calendar import datetime -from typing import Any, Union, cast +from typing import Any, cast from bson.codec_options import DEFAULT_CODEC_OPTIONS, CodecOptions, DatetimeConversion from bson.errors import InvalidBSON @@ -41,7 +41,7 @@ class DatetimeMS: __slots__ = ("_value",) - def __init__(self, value: Union[int, datetime.datetime]): + def __init__(self, value: int | datetime.datetime): """Represents a BSON UTC datetime. BSON UTC datetimes are defined as an int64 of milliseconds since the @@ -74,10 +74,10 @@ def __hash__(self) -> int: def __repr__(self) -> str: return type(self).__name__ + "(" + str(self._value) + ")" - def __lt__(self, other: Union[DatetimeMS, int]) -> bool: + def __lt__(self, other: DatetimeMS | int) -> bool: return self._value < other - def __le__(self, other: Union[DatetimeMS, int]) -> bool: + def __le__(self, other: DatetimeMS | int) -> bool: return self._value <= other def __eq__(self, other: Any) -> bool: @@ -90,10 +90,10 @@ def __ne__(self, other: Any) -> bool: return self._value != other._value return True - def __gt__(self, other: Union[DatetimeMS, int]) -> bool: + def __gt__(self, other: DatetimeMS | int) -> bool: return self._value > other - def __ge__(self, other: Union[DatetimeMS, int]) -> bool: + def __ge__(self, other: DatetimeMS | int) -> bool: return self._value >= other _type_marker = 9 @@ -146,9 +146,7 @@ def _max_datetime_ms(tz: datetime.tzinfo = utc) -> int: return min(_MAX_UTC_MS, _MAX_UTC_MS - offset_millis) -def _millis_to_datetime( - millis: int, opts: CodecOptions[Any] -) -> Union[datetime.datetime, DatetimeMS]: +def _millis_to_datetime(millis: int, opts: CodecOptions[Any]) -> datetime.datetime | DatetimeMS: """Convert milliseconds since epoch UTC to datetime.""" if ( opts.datetime_conversion == DatetimeConversion.DATETIME diff --git a/bson/dbref.py b/bson/dbref.py index a6bcb467ab..e8b8f18dfa 100644 --- a/bson/dbref.py +++ b/bson/dbref.py @@ -18,7 +18,7 @@ from collections.abc import Mapping from copy import deepcopy -from typing import Any, Optional +from typing import Any from bson._helpers import _getstate_slots, _setstate_slots from bson.son import SON @@ -37,8 +37,8 @@ def __init__( self, collection: str, id: Any, - database: Optional[str] = None, - _extra: Optional[Mapping[str, Any]] = None, + database: str | None = None, + _extra: Mapping[str, Any] | None = None, **kwargs: Any, ) -> None: """Initialize a new :class:`DBRef`. @@ -79,7 +79,7 @@ def id(self) -> Any: return self.__id @property - def database(self) -> Optional[str]: + def database(self) -> str | None: """Get the name of this DBRef's database. Returns None if this DBRef doesn't specify a database. diff --git a/bson/decimal128.py b/bson/decimal128.py index b7304fc3f0..533b75d040 100644 --- a/bson/decimal128.py +++ b/bson/decimal128.py @@ -23,7 +23,7 @@ import struct from collections.abc import Sequence from decimal import Decimal -from typing import Any, Union +from typing import Any from bson.codec_options import TypeDecoder, TypeEncoder @@ -61,7 +61,7 @@ } _DEC128_CTX = decimal.Context(**_CTX_OPTIONS.copy()) # type: ignore -_VALUE_OPTIONS = Union[decimal.Decimal, float, str, tuple[int, Sequence[int], int]] +_VALUE_OPTIONS = decimal.Decimal | float | str | tuple[int, Sequence[int], int] class DecimalEncoder(TypeEncoder): diff --git a/bson/errors.py b/bson/errors.py index a9e082ec11..85fd3e2a6a 100644 --- a/bson/errors.py +++ b/bson/errors.py @@ -16,7 +16,7 @@ from __future__ import annotations -from typing import Any, Optional +from typing import Any class BSONError(Exception): @@ -34,7 +34,7 @@ class InvalidStringData(BSONError): class InvalidDocument(BSONError): """Raised when trying to create a BSON object from an invalid document.""" - def __init__(self, message: str, document: Optional[Any] = None) -> None: + def __init__(self, message: str, document: Any | None = None) -> None: super().__init__(message) self._document = document diff --git a/bson/json_util.py b/bson/json_util.py index 8d84184487..97bb204b71 100644 --- a/bson/json_util.py +++ b/bson/json_util.py @@ -116,8 +116,6 @@ TYPE_CHECKING, Any, Callable, - Optional, - Union, cast, ) @@ -304,9 +302,9 @@ def __init__(self, *args: Any, **kwargs: Any): def __new__( cls: type[JSONOptions], - strict_number_long: Optional[bool] = None, - datetime_representation: Optional[int] = None, - strict_uuid: Optional[bool] = None, + strict_number_long: bool | None = None, + datetime_representation: int | None = None, + strict_uuid: bool | None = None, json_mode: int = JSONMode.RELAXED, *args: Any, **kwargs: Any, @@ -453,7 +451,7 @@ def dumps(obj: Any, *args: Any, **kwargs: Any) -> str: return json.dumps(_json_convert(obj, json_options), *args, **kwargs) -def loads(s: Union[str, bytes, bytearray], *args: Any, **kwargs: Any) -> Any: +def loads(s: str | bytes | bytearray, *args: Any, **kwargs: Any) -> Any: """Helper function that wraps :func:`json.loads`. Automatically passes the object_hook for BSON type conversion. @@ -530,7 +528,7 @@ def _parse_legacy_regex(doc: Any, dummy0: Any) -> Any: return Regex(pattern, flags) -def _parse_legacy_uuid(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: +def _parse_legacy_uuid(doc: Any, json_options: JSONOptions) -> Binary | uuid.UUID: """Decode a JSON legacy $uuid to Python UUID.""" if len(doc) != 1: raise TypeError(f"Bad $uuid, extra field(s): {doc}") @@ -542,7 +540,7 @@ def _parse_legacy_uuid(doc: Any, json_options: JSONOptions) -> Union[Binary, uui return uuid.UUID(doc["$uuid"]) -def _binary_or_uuid(data: Any, subtype: int, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: +def _binary_or_uuid(data: Any, subtype: int, json_options: JSONOptions) -> Binary | uuid.UUID: # special handling for UUID if subtype in ALL_UUID_SUBTYPES: uuid_representation = json_options.uuid_representation @@ -563,7 +561,7 @@ def _binary_or_uuid(data: Any, subtype: int, json_options: JSONOptions) -> Union return Binary(data, subtype) -def _parse_legacy_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: +def _parse_legacy_binary(doc: Any, json_options: JSONOptions) -> Binary | uuid.UUID: if isinstance(doc["$type"], int): doc["$type"] = "{:02x}".format(doc["$type"]) subtype = int(doc["$type"], 16) @@ -573,7 +571,7 @@ def _parse_legacy_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, u return _binary_or_uuid(data, subtype, json_options) -def _parse_canonical_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: +def _parse_canonical_binary(doc: Any, json_options: JSONOptions) -> Binary | uuid.UUID: binary = doc["$binary"] b64 = binary["base64"] subtype = binary["subType"] @@ -590,7 +588,7 @@ def _parse_canonical_binary(doc: Any, json_options: JSONOptions) -> Union[Binary def _parse_canonical_datetime( doc: Any, json_options: JSONOptions -) -> Union[datetime.datetime, DatetimeMS]: +) -> datetime.datetime | DatetimeMS: """Decode a JSON datetime to python datetime.datetime.""" dtm = doc["$date"] if len(doc) != 1: @@ -783,7 +781,7 @@ def _parse_canonical_maxkey(doc: Any, dummy0: Any) -> MaxKey: return MaxKey() -def _parse_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: +def _parse_binary(doc: Any, json_options: JSONOptions) -> Binary | uuid.UUID: if "$type" in doc: return _parse_legacy_binary(doc, json_options) else: diff --git a/bson/objectid.py b/bson/objectid.py index c5591aaac4..bf76117782 100644 --- a/bson/objectid.py +++ b/bson/objectid.py @@ -22,7 +22,7 @@ import threading import time from random import SystemRandom -from typing import Any, NoReturn, Optional, Union +from typing import Any, NoReturn from bson.datetime_ms import _datetime_to_millis from bson.errors import InvalidId @@ -59,7 +59,7 @@ class ObjectId: _type_marker = 7 - def __init__(self, oid: Optional[Union[str, ObjectId, bytes]] = None) -> None: + def __init__(self, oid: str | ObjectId | bytes | None = None) -> None: """Initialize a new ObjectId. An ObjectId is a 12-byte unique identifier consisting of: diff --git a/bson/raw_bson.py b/bson/raw_bson.py index 411916fc7a..d6fc5a3de3 100644 --- a/bson/raw_bson.py +++ b/bson/raw_bson.py @@ -55,7 +55,7 @@ import copyreg from collections.abc import ItemsView, Iterator, Mapping -from typing import Any, Optional +from typing import Any from bson import _get_object_size, _raw_as_bytes, _raw_to_dict from bson.codec_options import _RAW_BSON_DOCUMENT_MARKER, CodecOptions @@ -92,7 +92,7 @@ class RawBSONDocument(Mapping[str, Any]): def __init__( self, bson_bytes: bytes | memoryview, - codec_options: Optional[CodecOptions[RawBSONDocument]] = None, + codec_options: CodecOptions[RawBSONDocument] | None = None, ) -> None: """Create a new :class:`RawBSONDocument` @@ -127,7 +127,7 @@ class from the standard library so it can be used like a read-only `document_class` must be :class:`RawBSONDocument`. """ self.__raw = bson_bytes - self.__inflated_doc: Optional[Mapping[str, Any]] = None + self.__inflated_doc: Mapping[str, Any] | None = None # Can't default codec_options to DEFAULT_RAW_BSON_OPTIONS in signature, # it refers to this class RawBSONDocument. if codec_options is None: @@ -189,7 +189,7 @@ def __eq__(self, other: Any) -> bool: __hash__ = None # type: ignore[assignment] - def __getstate__(self) -> tuple[Optional[dict[str, Any]], dict[str, Any]]: + def __getstate__(self) -> tuple[dict[str, Any] | None, dict[str, Any]]: slots_state: dict[str, Any] = { name: getattr(self, name) for name in copyreg._slotnames(type(self)) # type: ignore[attr-defined] diff --git a/bson/regex.py b/bson/regex.py index 39d1814fa0..787302177b 100644 --- a/bson/regex.py +++ b/bson/regex.py @@ -18,7 +18,7 @@ import re from re import Pattern -from typing import Any, Generic, TypeVar, Union +from typing import Any, Generic, TypeVar from bson._helpers import _getstate_slots, _setstate_slots from bson.son import RE_TYPE @@ -84,7 +84,7 @@ def from_native(cls: type[Regex[Any]], regex: Pattern[_T]) -> Regex[_T]: return Regex(regex.pattern, regex.flags) - def __init__(self, pattern: _T, flags: Union[str, int] = 0) -> None: + def __init__(self, pattern: _T, flags: str | int = 0) -> None: """BSON regular expression data. This class is useful to store and retrieve regular expressions that are diff --git a/bson/son.py b/bson/son.py index 84b8028069..7b3c373e4f 100644 --- a/bson/son.py +++ b/bson/son.py @@ -29,9 +29,7 @@ from re import Pattern from typing import ( Any, - Optional, TypeVar, - Union, cast, ) @@ -56,7 +54,7 @@ class SON(dict[_Key, _Value]): def __init__( self, - data: Optional[Union[Mapping[_Key, _Value], Iterable[tuple[_Key, _Value]]]] = None, + data: Mapping[_Key, _Value] | Iterable[tuple[_Key, _Value]] | None = None, **kwargs: Any, ) -> None: self.__keys = [] @@ -135,7 +133,7 @@ def setdefault(self, key: _Key, default: _Value) -> _Value: self[key] = default return default - def pop(self, key: _Key, *args: Union[_Value, _T]) -> Union[_Value, _T]: + def pop(self, key: _Key, *args: _Value | _T) -> _Value | _T: if len(args) > 1: raise TypeError("pop expected at most 2 arguments, got " + repr(1 + len(args))) try: @@ -155,7 +153,7 @@ def popitem(self) -> tuple[_Key, _Value]: del self[k] return (k, v) - def update(self, other: Optional[Any] = None, **kwargs: _Value) -> None: + def update(self, other: Any | None = None, **kwargs: _Value) -> None: # Make progressively weaker assumptions about "other" if other is None: pass @@ -172,8 +170,8 @@ def update(self, other: Optional[Any] = None, **kwargs: _Value) -> None: self.update(kwargs) def get( # type: ignore[override] - self, key: _Key, default: Optional[Union[_Value, _T]] = None - ) -> Union[_Value, _T, None]: + self, key: _Key, default: _Value | _T | None = None + ) -> _Value | _T | None: try: return self[key] except KeyError: diff --git a/bson/timestamp.py b/bson/timestamp.py index 02a3fff627..3c23732333 100644 --- a/bson/timestamp.py +++ b/bson/timestamp.py @@ -18,7 +18,7 @@ import calendar import datetime -from typing import Any, Union +from typing import Any from bson._helpers import _getstate_slots, _setstate_slots from bson.tz_util import utc @@ -36,7 +36,7 @@ class Timestamp: _type_marker = 17 - def __init__(self, time: Union[datetime.datetime, int], inc: int) -> None: + def __init__(self, time: datetime.datetime | int, inc: int) -> None: """Create a new :class:`Timestamp`. This class is only for use with the MongoDB opLog. If you need diff --git a/bson/tz_util.py b/bson/tz_util.py index 18a543633e..249f1aa38a 100644 --- a/bson/tz_util.py +++ b/bson/tz_util.py @@ -17,7 +17,6 @@ from __future__ import annotations from datetime import datetime, timedelta, tzinfo -from typing import Optional, Union ZERO: timedelta = timedelta(0) @@ -30,7 +29,7 @@ class FixedOffset(tzinfo): Defining __getinitargs__ enables pickling / copying. """ - def __init__(self, offset: Union[float, timedelta], name: str) -> None: + def __init__(self, offset: float | timedelta, name: str) -> None: if isinstance(offset, timedelta): self.__offset = offset else: @@ -43,13 +42,13 @@ def __getinitargs__(self) -> tuple[timedelta, str]: def __repr__(self) -> str: return f"{self.__class__.__name__}({self.__offset!r}, {self.__name!r})" - def utcoffset(self, dt: Optional[datetime]) -> timedelta: + def utcoffset(self, dt: datetime | None) -> timedelta: return self.__offset - def tzname(self, dt: Optional[datetime]) -> str: + def tzname(self, dt: datetime | None) -> str: return self.__name - def dst(self, dt: Optional[datetime]) -> timedelta: + def dst(self, dt: datetime | None) -> timedelta: return ZERO diff --git a/fuzz/fuzz_decode.py b/fuzz/fuzz_decode.py index b323c97092..261059b349 100755 --- a/fuzz/fuzz_decode.py +++ b/fuzz/fuzz_decode.py @@ -8,8 +8,6 @@ from __future__ import annotations -from typing import Optional - import atheris from fuzz_shared import OTHER_ERRORS, load_pure_python_bson, main, normalize @@ -25,7 +23,7 @@ _BOTH_IMPLS = _bson_c.has_c() -def _decode_with(bson_mod, data: bytes) -> Optional[dict]: +def _decode_with(bson_mod, data: bytes) -> dict | None: """Run bson_mod.decode(data), returning the document or None on expected errors.""" try: return bson_mod.decode(data) diff --git a/fuzz/fuzz_decode_all.py b/fuzz/fuzz_decode_all.py index fa01549607..e8a9812fc6 100755 --- a/fuzz/fuzz_decode_all.py +++ b/fuzz/fuzz_decode_all.py @@ -8,8 +8,6 @@ from __future__ import annotations -from typing import Optional - import atheris from fuzz_shared import OTHER_ERRORS, load_pure_python_bson, main, normalize @@ -25,7 +23,7 @@ _BOTH_IMPLS = _bson_c.has_c() -def _decode_all_with(bson_mod, data: bytes) -> Optional[list]: +def _decode_all_with(bson_mod, data: bytes) -> list | None: """Run bson_mod.decode_all(data), returning the documents or None on expected errors.""" try: return bson_mod.decode_all(data) diff --git a/gridfs/asynchronous/grid_file.py b/gridfs/asynchronous/grid_file.py index f3d816f4dd..52122fe1dc 100644 --- a/gridfs/asynchronous/grid_file.py +++ b/gridfs/asynchronous/grid_file.py @@ -22,7 +22,7 @@ import math from collections import abc from collections.abc import Iterable, Mapping -from typing import Any, NoReturn, Optional, cast +from typing import Any, NoReturn, cast from bson.int64 import Int64 from bson.objectid import ObjectId @@ -64,7 +64,7 @@ _IS_SYNC = False -def _disallow_transactions(session: Optional[AsyncClientSession]) -> None: +def _disallow_transactions(session: AsyncClientSession | None) -> None: if session and session.in_transaction: raise InvalidOperation("GridFS does not support multi-document transactions") @@ -158,7 +158,7 @@ async def put(self, data: Any, **kwargs: Any) -> Any: await grid_file.write(data) return grid_file._id - async def get(self, file_id: Any, session: Optional[AsyncClientSession] = None) -> AsyncGridOut: + async def get(self, file_id: Any, session: AsyncClientSession | None = None) -> AsyncGridOut: """Get a file from GridFS by ``"_id"``. Returns an instance of :class:`~gridfs.grid_file.GridOut`, @@ -179,9 +179,9 @@ async def get(self, file_id: Any, session: Optional[AsyncClientSession] = None) async def get_version( self, - filename: Optional[str] = None, - version: Optional[int] = -1, - session: Optional[AsyncClientSession] = None, + filename: str | None = None, + version: int | None = -1, + session: AsyncClientSession | None = None, **kwargs: Any, ) -> AsyncGridOut: """Get a file from GridFS by ``"filename"`` or metadata fields. @@ -238,8 +238,8 @@ async def get_version( async def get_last_version( self, - filename: Optional[str] = None, - session: Optional[AsyncClientSession] = None, + filename: str | None = None, + session: AsyncClientSession | None = None, **kwargs: Any, ) -> AsyncGridOut: """Get the most recent version of a file in GridFS by ``"filename"`` @@ -259,7 +259,7 @@ async def get_last_version( return await self.get_version(filename=filename, session=session, **kwargs) # TODO add optional safe mode for chunk removal? - async def delete(self, file_id: Any, session: Optional[AsyncClientSession] = None) -> None: + async def delete(self, file_id: Any, session: AsyncClientSession | None = None) -> None: """Delete a file from GridFS by ``"_id"``. Deletes all data belonging to the file with ``"_id"``: @@ -287,7 +287,7 @@ async def delete(self, file_id: Any, session: Optional[AsyncClientSession] = Non await self._files.delete_one({"_id": {"$eq": file_id}}, session=session) await self._chunks.delete_many({"files_id": {"$eq": file_id}}, session=session) - async def list(self, session: Optional[AsyncClientSession] = None) -> list[str]: + async def list(self, session: AsyncClientSession | None = None) -> list[str]: """List the names of all files stored in this instance of :class:`GridFS`. @@ -311,11 +311,11 @@ async def list(self, session: Optional[AsyncClientSession] = None) -> list[str]: async def find_one( self, - filter: Optional[Any] = None, - session: Optional[AsyncClientSession] = None, + filter: Any | None = None, + session: AsyncClientSession | None = None, *args: Any, **kwargs: Any, - ) -> Optional[AsyncGridOut]: + ) -> AsyncGridOut | None: """Get a single file from gridfs. All arguments to :meth:`find` are also valid arguments for @@ -411,8 +411,8 @@ def find(self, *args: Any, **kwargs: Any) -> AsyncGridOutCursor: async def exists( self, - document_or_id: Optional[Any] = None, - session: Optional[AsyncClientSession] = None, + document_or_id: Any | None = None, + session: AsyncClientSession | None = None, **kwargs: Any, ) -> bool: """Check if a file exists in this instance of :class:`GridFS`. @@ -470,8 +470,8 @@ def __init__( db: AsyncDatabase[Any], bucket_name: str = "fs", chunk_size_bytes: int = DEFAULT_CHUNK_SIZE, - write_concern: Optional[WriteConcern] = None, - read_preference: Optional[_ServerMode] = None, + write_concern: WriteConcern | None = None, + read_preference: _ServerMode | None = None, ) -> None: """Create a new instance of :class:`GridFSBucket`. @@ -531,9 +531,9 @@ def __init__( def open_upload_stream( self, filename: str, - chunk_size_bytes: Optional[int] = None, - metadata: Optional[Mapping[str, Any]] = None, - session: Optional[AsyncClientSession] = None, + chunk_size_bytes: int | None = None, + metadata: Mapping[str, Any] | None = None, + session: AsyncClientSession | None = None, ) -> AsyncGridIn: """Opens a Stream that the application can write the contents of the file to. @@ -586,9 +586,9 @@ def open_upload_stream_with_id( self, file_id: Any, filename: str, - chunk_size_bytes: Optional[int] = None, - metadata: Optional[Mapping[str, Any]] = None, - session: Optional[AsyncClientSession] = None, + chunk_size_bytes: int | None = None, + metadata: Mapping[str, Any] | None = None, + session: AsyncClientSession | None = None, ) -> AsyncGridIn: """Opens a Stream that the application can write the contents of the file to. @@ -647,9 +647,9 @@ async def upload_from_stream( self, filename: str, source: Any, - chunk_size_bytes: Optional[int] = None, - metadata: Optional[Mapping[str, Any]] = None, - session: Optional[AsyncClientSession] = None, + chunk_size_bytes: int | None = None, + metadata: Mapping[str, Any] | None = None, + session: AsyncClientSession | None = None, ) -> ObjectId: """Uploads a user file to a GridFS bucket. @@ -698,9 +698,9 @@ async def upload_from_stream_with_id( file_id: Any, filename: str, source: Any, - chunk_size_bytes: Optional[int] = None, - metadata: Optional[Mapping[str, Any]] = None, - session: Optional[AsyncClientSession] = None, + chunk_size_bytes: int | None = None, + metadata: Mapping[str, Any] | None = None, + session: AsyncClientSession | None = None, ) -> None: """Uploads a user file to a GridFS bucket with a custom file id. @@ -743,7 +743,7 @@ async def upload_from_stream_with_id( await gin.write(source) async def open_download_stream( - self, file_id: Any, session: Optional[AsyncClientSession] = None + self, file_id: Any, session: AsyncClientSession | None = None ) -> AsyncGridOut: """Opens a Stream from which the application can read the contents of the stored file specified by file_id. @@ -776,7 +776,7 @@ async def open_download_stream( @_csot.apply async def download_to_stream( - self, file_id: Any, destination: Any, session: Optional[AsyncClientSession] = None + self, file_id: Any, destination: Any, session: AsyncClientSession | None = None ) -> None: """Downloads the contents of the stored file specified by file_id and writes the contents to `destination`. @@ -811,7 +811,7 @@ async def download_to_stream( destination.write(chunk) @_csot.apply - async def delete(self, file_id: Any, session: Optional[AsyncClientSession] = None) -> None: + async def delete(self, file_id: Any, session: AsyncClientSession | None = None) -> None: """Given an file_id, delete this stored file's files collection document and associated chunks from a GridFS bucket. @@ -840,7 +840,7 @@ async def delete(self, file_id: Any, session: Optional[AsyncClientSession] = Non @_csot.apply async def delete_by_name( - self, filename: str, session: Optional[AsyncClientSession] = None + self, filename: str, session: AsyncClientSession | None = None ) -> None: """Given a filename, delete this stored file's files collection document(s) and associated chunks from a GridFS bucket. @@ -915,7 +915,7 @@ def find(self, *args: Any, **kwargs: Any) -> AsyncGridOutCursor: return AsyncGridOutCursor(self._collection, *args, **kwargs) async def open_download_stream_by_name( - self, filename: str, revision: int = -1, session: Optional[AsyncClientSession] = None + self, filename: str, revision: int = -1, session: AsyncClientSession | None = None ) -> AsyncGridOut: """Opens a Stream from which the application can read the contents of `filename` and optional `revision`. @@ -974,7 +974,7 @@ async def download_to_stream_by_name( filename: str, destination: Any, revision: int = -1, - session: Optional[AsyncClientSession] = None, + session: AsyncClientSession | None = None, ) -> None: """Write the contents of `filename` (with optional `revision`) to `destination`. @@ -1022,7 +1022,7 @@ async def download_to_stream_by_name( destination.write(chunk) async def rename( - self, file_id: Any, new_filename: str, session: Optional[AsyncClientSession] = None + self, file_id: Any, new_filename: str, session: AsyncClientSession | None = None ) -> None: """Renames the stored file with the specified file_id. @@ -1054,7 +1054,7 @@ async def rename( ) async def rename_by_name( - self, filename: str, new_filename: str, session: Optional[AsyncClientSession] = None + self, filename: str, new_filename: str, session: AsyncClientSession | None = None ) -> None: """Renames the stored file with the specified filename. @@ -1089,7 +1089,7 @@ class AsyncGridIn: def __init__( self, root_collection: AsyncCollection[Any], - session: Optional[AsyncClientSession] = None, + session: AsyncClientSession | None = None, **kwargs: Any, ) -> None: """Write a file to GridFS @@ -1214,9 +1214,9 @@ def closed(self) -> bool: return self._closed _id: Any = _a_grid_in_property("_id", "The ``'_id'`` value for this file.", read_only=True) - filename: Optional[str] = _a_grid_in_property("filename", "Name of this file.") - name: Optional[str] = _a_grid_in_property("filename", "Alias for `filename`.") - content_type: Optional[str] = _a_grid_in_property( + filename: str | None = _a_grid_in_property("filename", "Name of this file.") + name: str | None = _a_grid_in_property("filename", "Alias for `filename`.") + content_type: str | None = _a_grid_in_property( "contentType", "DEPRECATED, will be removed in PyMongo 5.0. Mime-type for this file." ) length: int = _a_grid_in_property("length", "Length (in bytes) of this file.", closed_only=True) @@ -1224,7 +1224,7 @@ def closed(self) -> bool: upload_date: datetime.datetime = _a_grid_in_property( "uploadDate", "Date that this file was uploaded.", closed_only=True ) - md5: Optional[str] = _a_grid_in_property( + md5: str | None = _a_grid_in_property( "md5", "DEPRECATED, will be removed in PyMongo 5.0. MD5 of the contents of this file if an md5 sum was created.", closed_only=True, @@ -1467,9 +1467,9 @@ class AsyncGridOut(GRIDOUT_BASE_CLASS): # type: ignore def __init__( self, root_collection: AsyncCollection[Any], - file_id: Optional[int] = None, - file_document: Optional[Any] = None, - session: Optional[AsyncClientSession] = None, + file_id: int | None = None, + file_document: Any | None = None, + session: AsyncClientSession | None = None, ) -> None: """Read a file from GridFS @@ -1529,7 +1529,7 @@ def __init__( _id: Any = _a_grid_out_property("_id", "The ``'_id'`` value for this file.") filename: str = _a_grid_out_property("filename", "Name of this file.") name: str = _a_grid_out_property("filename", "Alias for `filename`.") - content_type: Optional[str] = _a_grid_out_property( + content_type: str | None = _a_grid_out_property( "contentType", "DEPRECATED, will be removed in PyMongo 5.0. Mime-type for this file." ) length: int = _a_grid_out_property("length", "Length (in bytes) of this file.") @@ -1537,13 +1537,13 @@ def __init__( upload_date: datetime.datetime = _a_grid_out_property( "uploadDate", "Date that this file was first uploaded." ) - aliases: Optional[list[str]] = _a_grid_out_property( + aliases: list[str] | None = _a_grid_out_property( "aliases", "DEPRECATED, will be removed in PyMongo 5.0. List of aliases for this file." ) - metadata: Optional[Mapping[str, Any]] = _a_grid_out_property( + metadata: Mapping[str, Any] | None = _a_grid_out_property( "metadata", "Metadata attached to this file." ) - md5: Optional[str] = _a_grid_out_property( + md5: str | None = _a_grid_out_property( "md5", "DEPRECATED, will be removed in PyMongo 5.0. MD5 of the contents of this file if an md5 sum was created.", ) @@ -1819,7 +1819,7 @@ def flush(self) -> None: def isatty(self) -> bool: return False - def truncate(self, size: Optional[int] = None) -> NoReturn: + def truncate(self, size: int | None = None) -> NoReturn: # See https://docs.python.org/3/library/io.html#io.IOBase.writable # for why truncate has to raise. raise io.UnsupportedOperation("truncate") @@ -1842,7 +1842,7 @@ def __init__( self, grid_out: AsyncGridOut, chunks: AsyncCollection[Any], - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, next_chunk: Any, ) -> None: self._id = grid_out._id @@ -1854,7 +1854,7 @@ def __init__( self._num_chunks = math.ceil(float(self._length) / self._chunk_size) self._cursor = None - _cursor: Optional[AsyncCursor[Any]] + _cursor: AsyncCursor[Any] | None def expected_chunk_length(self, chunk_n: int) -> int: if chunk_n < self._num_chunks - 1: @@ -1955,13 +1955,13 @@ class AsyncGridOutCursor(AsyncCursor): # type: ignore[type-arg] def __init__( self, collection: AsyncCollection[Any], - filter: Optional[Mapping[str, Any]] = None, + filter: Mapping[str, Any] | None = None, skip: int = 0, limit: int = 0, no_cursor_timeout: bool = False, - sort: Optional[Any] = None, + sort: Any | None = None, batch_size: int = 0, - session: Optional[AsyncClientSession] = None, + session: AsyncClientSession | None = None, ) -> None: """Create a new cursor, similar to the normal :class:`~pymongo.cursor.Cursor`. @@ -1996,7 +1996,7 @@ async def next(self) -> AsyncGridOut: next_file = await super().next() return AsyncGridOut(self._root_collection, file_document=next_file, session=self.session) - async def to_list(self, length: Optional[int] = None) -> list[AsyncGridOut]: + async def to_list(self, length: int | None = None) -> list[AsyncGridOut]: """Convert the cursor to a list.""" if length is None: return [x async for x in self] # noqa: C416,RUF100 @@ -2015,6 +2015,6 @@ def add_option(self, *args: Any, **kwargs: Any) -> NoReturn: def remove_option(self, *args: Any, **kwargs: Any) -> NoReturn: raise NotImplementedError("Method does not exist for GridOutCursor") - def _clone_base(self, session: Optional[AsyncClientSession]) -> AsyncGridOutCursor: + def _clone_base(self, session: AsyncClientSession | None) -> AsyncGridOutCursor: """Creates an empty GridOutCursor for information to be copied into.""" return AsyncGridOutCursor(self._root_collection, session=session) diff --git a/gridfs/grid_file_shared.py b/gridfs/grid_file_shared.py index 470f3f14d2..a65718aa77 100644 --- a/gridfs/grid_file_shared.py +++ b/gridfs/grid_file_shared.py @@ -2,7 +2,7 @@ import os import warnings -from typing import Any, Optional +from typing import Any from pymongo import ASCENDING from pymongo.common import MAX_MESSAGE_SIZE @@ -33,8 +33,8 @@ def _a_grid_in_property( field_name: str, docstring: str, - read_only: Optional[bool] = False, - closed_only: Optional[bool] = False, + read_only: bool | None = False, + closed_only: bool | None = False, ) -> Any: """Create a GridIn property.""" @@ -96,8 +96,8 @@ def a_getter(self: Any) -> Any: def _grid_in_property( field_name: str, docstring: str, - read_only: Optional[bool] = False, - closed_only: Optional[bool] = False, + read_only: bool | None = False, + closed_only: bool | None = False, ) -> Any: """Create a GridIn property.""" warn_str = "" diff --git a/gridfs/synchronous/grid_file.py b/gridfs/synchronous/grid_file.py index ac7301e170..b3ce525cb1 100644 --- a/gridfs/synchronous/grid_file.py +++ b/gridfs/synchronous/grid_file.py @@ -22,7 +22,7 @@ import math from collections import abc from collections.abc import Iterable, Mapping -from typing import Any, NoReturn, Optional, cast +from typing import Any, NoReturn, cast from bson.int64 import Int64 from bson.objectid import ObjectId @@ -64,7 +64,7 @@ _IS_SYNC = True -def _disallow_transactions(session: Optional[ClientSession]) -> None: +def _disallow_transactions(session: ClientSession | None) -> None: if session and session.in_transaction: raise InvalidOperation("GridFS does not support multi-document transactions") @@ -158,7 +158,7 @@ def put(self, data: Any, **kwargs: Any) -> Any: grid_file.write(data) return grid_file._id - def get(self, file_id: Any, session: Optional[ClientSession] = None) -> GridOut: + def get(self, file_id: Any, session: ClientSession | None = None) -> GridOut: """Get a file from GridFS by ``"_id"``. Returns an instance of :class:`~gridfs.grid_file.GridOut`, @@ -179,9 +179,9 @@ def get(self, file_id: Any, session: Optional[ClientSession] = None) -> GridOut: def get_version( self, - filename: Optional[str] = None, - version: Optional[int] = -1, - session: Optional[ClientSession] = None, + filename: str | None = None, + version: int | None = -1, + session: ClientSession | None = None, **kwargs: Any, ) -> GridOut: """Get a file from GridFS by ``"filename"`` or metadata fields. @@ -238,8 +238,8 @@ def get_version( def get_last_version( self, - filename: Optional[str] = None, - session: Optional[ClientSession] = None, + filename: str | None = None, + session: ClientSession | None = None, **kwargs: Any, ) -> GridOut: """Get the most recent version of a file in GridFS by ``"filename"`` @@ -259,7 +259,7 @@ def get_last_version( return self.get_version(filename=filename, session=session, **kwargs) # TODO add optional safe mode for chunk removal? - def delete(self, file_id: Any, session: Optional[ClientSession] = None) -> None: + def delete(self, file_id: Any, session: ClientSession | None = None) -> None: """Delete a file from GridFS by ``"_id"``. Deletes all data belonging to the file with ``"_id"``: @@ -287,7 +287,7 @@ def delete(self, file_id: Any, session: Optional[ClientSession] = None) -> None: self._files.delete_one({"_id": {"$eq": file_id}}, session=session) self._chunks.delete_many({"files_id": {"$eq": file_id}}, session=session) - def list(self, session: Optional[ClientSession] = None) -> list[str]: + def list(self, session: ClientSession | None = None) -> list[str]: """List the names of all files stored in this instance of :class:`GridFS`. @@ -309,11 +309,11 @@ def list(self, session: Optional[ClientSession] = None) -> list[str]: def find_one( self, - filter: Optional[Any] = None, - session: Optional[ClientSession] = None, + filter: Any | None = None, + session: ClientSession | None = None, *args: Any, **kwargs: Any, - ) -> Optional[GridOut]: + ) -> GridOut | None: """Get a single file from gridfs. All arguments to :meth:`find` are also valid arguments for @@ -409,8 +409,8 @@ def find(self, *args: Any, **kwargs: Any) -> GridOutCursor: def exists( self, - document_or_id: Optional[Any] = None, - session: Optional[ClientSession] = None, + document_or_id: Any | None = None, + session: ClientSession | None = None, **kwargs: Any, ) -> bool: """Check if a file exists in this instance of :class:`GridFS`. @@ -468,8 +468,8 @@ def __init__( db: Database[Any], bucket_name: str = "fs", chunk_size_bytes: int = DEFAULT_CHUNK_SIZE, - write_concern: Optional[WriteConcern] = None, - read_preference: Optional[_ServerMode] = None, + write_concern: WriteConcern | None = None, + read_preference: _ServerMode | None = None, ) -> None: """Create a new instance of :class:`GridFSBucket`. @@ -529,9 +529,9 @@ def __init__( def open_upload_stream( self, filename: str, - chunk_size_bytes: Optional[int] = None, - metadata: Optional[Mapping[str, Any]] = None, - session: Optional[ClientSession] = None, + chunk_size_bytes: int | None = None, + metadata: Mapping[str, Any] | None = None, + session: ClientSession | None = None, ) -> GridIn: """Opens a Stream that the application can write the contents of the file to. @@ -584,9 +584,9 @@ def open_upload_stream_with_id( self, file_id: Any, filename: str, - chunk_size_bytes: Optional[int] = None, - metadata: Optional[Mapping[str, Any]] = None, - session: Optional[ClientSession] = None, + chunk_size_bytes: int | None = None, + metadata: Mapping[str, Any] | None = None, + session: ClientSession | None = None, ) -> GridIn: """Opens a Stream that the application can write the contents of the file to. @@ -645,9 +645,9 @@ def upload_from_stream( self, filename: str, source: Any, - chunk_size_bytes: Optional[int] = None, - metadata: Optional[Mapping[str, Any]] = None, - session: Optional[ClientSession] = None, + chunk_size_bytes: int | None = None, + metadata: Mapping[str, Any] | None = None, + session: ClientSession | None = None, ) -> ObjectId: """Uploads a user file to a GridFS bucket. @@ -694,9 +694,9 @@ def upload_from_stream_with_id( file_id: Any, filename: str, source: Any, - chunk_size_bytes: Optional[int] = None, - metadata: Optional[Mapping[str, Any]] = None, - session: Optional[ClientSession] = None, + chunk_size_bytes: int | None = None, + metadata: Mapping[str, Any] | None = None, + session: ClientSession | None = None, ) -> None: """Uploads a user file to a GridFS bucket with a custom file id. @@ -738,9 +738,7 @@ def upload_from_stream_with_id( ) as gin: gin.write(source) - def open_download_stream( - self, file_id: Any, session: Optional[ClientSession] = None - ) -> GridOut: + def open_download_stream(self, file_id: Any, session: ClientSession | None = None) -> GridOut: """Opens a Stream from which the application can read the contents of the stored file specified by file_id. @@ -772,7 +770,7 @@ def open_download_stream( @_csot.apply def download_to_stream( - self, file_id: Any, destination: Any, session: Optional[ClientSession] = None + self, file_id: Any, destination: Any, session: ClientSession | None = None ) -> None: """Downloads the contents of the stored file specified by file_id and writes the contents to `destination`. @@ -807,7 +805,7 @@ def download_to_stream( destination.write(chunk) @_csot.apply - def delete(self, file_id: Any, session: Optional[ClientSession] = None) -> None: + def delete(self, file_id: Any, session: ClientSession | None = None) -> None: """Given an file_id, delete this stored file's files collection document and associated chunks from a GridFS bucket. @@ -835,7 +833,7 @@ def delete(self, file_id: Any, session: Optional[ClientSession] = None) -> None: raise NoFile(f"no file could be deleted because none matched {file_id}") @_csot.apply - def delete_by_name(self, filename: str, session: Optional[ClientSession] = None) -> None: + def delete_by_name(self, filename: str, session: ClientSession | None = None) -> None: """Given a filename, delete this stored file's files collection document(s) and associated chunks from a GridFS bucket. @@ -909,7 +907,7 @@ def find(self, *args: Any, **kwargs: Any) -> GridOutCursor: return GridOutCursor(self._collection, *args, **kwargs) def open_download_stream_by_name( - self, filename: str, revision: int = -1, session: Optional[ClientSession] = None + self, filename: str, revision: int = -1, session: ClientSession | None = None ) -> GridOut: """Opens a Stream from which the application can read the contents of `filename` and optional `revision`. @@ -968,7 +966,7 @@ def download_to_stream_by_name( filename: str, destination: Any, revision: int = -1, - session: Optional[ClientSession] = None, + session: ClientSession | None = None, ) -> None: """Write the contents of `filename` (with optional `revision`) to `destination`. @@ -1013,9 +1011,7 @@ def download_to_stream_by_name( break destination.write(chunk) - def rename( - self, file_id: Any, new_filename: str, session: Optional[ClientSession] = None - ) -> None: + def rename(self, file_id: Any, new_filename: str, session: ClientSession | None = None) -> None: """Renames the stored file with the specified file_id. For example:: @@ -1046,7 +1042,7 @@ def rename( ) def rename_by_name( - self, filename: str, new_filename: str, session: Optional[ClientSession] = None + self, filename: str, new_filename: str, session: ClientSession | None = None ) -> None: """Renames the stored file with the specified filename. @@ -1081,7 +1077,7 @@ class GridIn: def __init__( self, root_collection: Collection[Any], - session: Optional[ClientSession] = None, + session: ClientSession | None = None, **kwargs: Any, ) -> None: """Write a file to GridFS @@ -1200,9 +1196,9 @@ def closed(self) -> bool: return self._closed _id: Any = _grid_in_property("_id", "The ``'_id'`` value for this file.", read_only=True) - filename: Optional[str] = _grid_in_property("filename", "Name of this file.") - name: Optional[str] = _grid_in_property("filename", "Alias for `filename`.") - content_type: Optional[str] = _grid_in_property( + filename: str | None = _grid_in_property("filename", "Name of this file.") + name: str | None = _grid_in_property("filename", "Alias for `filename`.") + content_type: str | None = _grid_in_property( "contentType", "DEPRECATED, will be removed in PyMongo 5.0. Mime-type for this file." ) length: int = _grid_in_property("length", "Length (in bytes) of this file.", closed_only=True) @@ -1210,7 +1206,7 @@ def closed(self) -> bool: upload_date: datetime.datetime = _grid_in_property( "uploadDate", "Date that this file was uploaded.", closed_only=True ) - md5: Optional[str] = _grid_in_property( + md5: str | None = _grid_in_property( "md5", "DEPRECATED, will be removed in PyMongo 5.0. MD5 of the contents of this file if an md5 sum was created.", closed_only=True, @@ -1453,9 +1449,9 @@ class GridOut(GRIDOUT_BASE_CLASS): # type: ignore def __init__( self, root_collection: Collection[Any], - file_id: Optional[int] = None, - file_document: Optional[Any] = None, - session: Optional[ClientSession] = None, + file_id: int | None = None, + file_document: Any | None = None, + session: ClientSession | None = None, ) -> None: """Read a file from GridFS @@ -1515,7 +1511,7 @@ def __init__( _id: Any = _grid_out_property("_id", "The ``'_id'`` value for this file.") filename: str = _grid_out_property("filename", "Name of this file.") name: str = _grid_out_property("filename", "Alias for `filename`.") - content_type: Optional[str] = _grid_out_property( + content_type: str | None = _grid_out_property( "contentType", "DEPRECATED, will be removed in PyMongo 5.0. Mime-type for this file." ) length: int = _grid_out_property("length", "Length (in bytes) of this file.") @@ -1523,13 +1519,13 @@ def __init__( upload_date: datetime.datetime = _grid_out_property( "uploadDate", "Date that this file was first uploaded." ) - aliases: Optional[list[str]] = _grid_out_property( + aliases: list[str] | None = _grid_out_property( "aliases", "DEPRECATED, will be removed in PyMongo 5.0. List of aliases for this file." ) - metadata: Optional[Mapping[str, Any]] = _grid_out_property( + metadata: Mapping[str, Any] | None = _grid_out_property( "metadata", "Metadata attached to this file." ) - md5: Optional[str] = _grid_out_property( + md5: str | None = _grid_out_property( "md5", "DEPRECATED, will be removed in PyMongo 5.0. MD5 of the contents of this file if an md5 sum was created.", ) @@ -1805,7 +1801,7 @@ def flush(self) -> None: def isatty(self) -> bool: return False - def truncate(self, size: Optional[int] = None) -> NoReturn: + def truncate(self, size: int | None = None) -> NoReturn: # See https://docs.python.org/3/library/io.html#io.IOBase.writable # for why truncate has to raise. raise io.UnsupportedOperation("truncate") @@ -1828,7 +1824,7 @@ def __init__( self, grid_out: GridOut, chunks: Collection[Any], - session: Optional[ClientSession], + session: ClientSession | None, next_chunk: Any, ) -> None: self._id = grid_out._id @@ -1840,7 +1836,7 @@ def __init__( self._num_chunks = math.ceil(float(self._length) / self._chunk_size) self._cursor = None - _cursor: Optional[Cursor[Any]] + _cursor: Cursor[Any] | None def expected_chunk_length(self, chunk_n: int) -> int: if chunk_n < self._num_chunks - 1: @@ -1939,13 +1935,13 @@ class GridOutCursor(Cursor): # type: ignore[type-arg] def __init__( self, collection: Collection[Any], - filter: Optional[Mapping[str, Any]] = None, + filter: Mapping[str, Any] | None = None, skip: int = 0, limit: int = 0, no_cursor_timeout: bool = False, - sort: Optional[Any] = None, + sort: Any | None = None, batch_size: int = 0, - session: Optional[ClientSession] = None, + session: ClientSession | None = None, ) -> None: """Create a new cursor, similar to the normal :class:`~pymongo.cursor.Cursor`. @@ -1980,7 +1976,7 @@ def next(self) -> GridOut: next_file = super().next() return GridOut(self._root_collection, file_document=next_file, session=self.session) - def to_list(self, length: Optional[int] = None) -> list[GridOut]: + def to_list(self, length: int | None = None) -> list[GridOut]: """Convert the cursor to a list.""" if length is None: return [x for x in self] # noqa: C416,RUF100 @@ -1999,6 +1995,6 @@ def add_option(self, *args: Any, **kwargs: Any) -> NoReturn: def remove_option(self, *args: Any, **kwargs: Any) -> NoReturn: raise NotImplementedError("Method does not exist for GridOutCursor") - def _clone_base(self, session: Optional[ClientSession]) -> GridOutCursor: + def _clone_base(self, session: ClientSession | None) -> GridOutCursor: """Creates an empty GridOutCursor for information to be copied into.""" return GridOutCursor(self._root_collection, session=session) diff --git a/pymongo/__init__.py b/pymongo/__init__.py index dac07ce184..943f88e34c 100644 --- a/pymongo/__init__.py +++ b/pymongo/__init__.py @@ -17,7 +17,6 @@ from __future__ import annotations from contextlib import AbstractContextManager -from typing import Optional __all__ = [ "ASCENDING", @@ -121,7 +120,7 @@ """Current version of PyMongo.""" -def timeout(seconds: Optional[float]) -> AbstractContextManager[None]: +def timeout(seconds: float | None) -> AbstractContextManager[None]: """**(Provisional)** Apply the given timeout for a block of operations. .. note:: :func:`~pymongo.timeout` is currently provisional. Backwards diff --git a/pymongo/_asyncio_lock.py b/pymongo/_asyncio_lock.py index cde1b0ca51..561bdcae11 100644 --- a/pymongo/_asyncio_lock.py +++ b/pymongo/_asyncio_lock.py @@ -10,7 +10,7 @@ import threading from asyncio import events, exceptions from collections.abc import Coroutine -from typing import Any, Optional +from typing import Any _global_lock = threading.Lock() @@ -94,7 +94,7 @@ class Lock(_ContextManagerMixin, _LoopBoundMixin): """ def __init__(self) -> None: - self._waiters: Optional[collections.deque[Any]] = None + self._waiters: collections.deque[Any] | None = None self._locked = False def __repr__(self) -> str: @@ -187,7 +187,7 @@ class Condition(_ContextManagerMixin, _LoopBoundMixin): A new Lock object is created and used as the underlying lock. """ - def __init__(self, lock: Optional[Lock] = None) -> None: + def __init__(self, lock: Lock | None = None) -> None: if lock is None: lock = Lock() diff --git a/pymongo/_azure_helpers.py b/pymongo/_azure_helpers.py index 231625d8d7..6b96e48fbf 100644 --- a/pymongo/_azure_helpers.py +++ b/pymongo/_azure_helpers.py @@ -17,12 +17,12 @@ from __future__ import annotations import json -from typing import Any, Optional +from typing import Any from urllib.parse import quote def _get_azure_response( - resource: str, client_id: Optional[str] = None, timeout: float = 5 + resource: str, client_id: str | None = None, timeout: float = 5 ) -> dict[str, Any]: # Deferred import to save overall import time. from urllib.request import Request, urlopen diff --git a/pymongo/_client_bulk_shared.py b/pymongo/_client_bulk_shared.py index 0019d0c0bb..bc47e93ff7 100644 --- a/pymongo/_client_bulk_shared.py +++ b/pymongo/_client_bulk_shared.py @@ -18,7 +18,7 @@ from __future__ import annotations from collections.abc import Mapping, MutableMapping -from typing import TYPE_CHECKING, Any, NoReturn, Optional, Union +from typing import TYPE_CHECKING, Any, NoReturn from bson.objectid import ObjectId from bson.raw_bson import RawBSONDocument @@ -98,9 +98,9 @@ def __init__( client: _AgnosticMongoClient, write_concern: WriteConcern, ordered: bool = True, - bypass_document_validation: Optional[bool] = None, - comment: Optional[str] = None, - let: Optional[Any] = None, + bypass_document_validation: bool | None = None, + comment: str | None = None, + let: Any | None = None, verbose_results: bool = False, ) -> None: """Initialize a _AgnosticClientBulkBase instance.""" @@ -143,13 +143,13 @@ def add_update( self, namespace: str, selector: Mapping[str, Any], - update: Union[Mapping[str, Any], _Pipeline], + update: Mapping[str, Any] | _Pipeline, multi: bool, - upsert: Optional[bool] = None, - collation: Optional[Mapping[str, Any]] = None, - array_filters: Optional[list[Mapping[str, Any]]] = None, - hint: Union[str, dict[str, Any], None] = None, - sort: Optional[Mapping[str, Any]] = None, + upsert: bool | None = None, + collation: Mapping[str, Any] | None = None, + array_filters: list[Mapping[str, Any]] | None = None, + hint: str | dict[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, ) -> None: """Create an update document and add it to the list of ops.""" validate_ok_for_update(update) @@ -183,10 +183,10 @@ def add_replace( namespace: str, selector: Mapping[str, Any], replacement: Mapping[str, Any], - upsert: Optional[bool] = None, - collation: Optional[Mapping[str, Any]] = None, - hint: Union[str, dict[str, Any], None] = None, - sort: Optional[Mapping[str, Any]] = None, + upsert: bool | None = None, + collation: Mapping[str, Any] | None = None, + hint: str | dict[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, ) -> None: """Create a replace document and add it to the list of ops.""" validate_ok_for_replace(replacement) @@ -214,8 +214,8 @@ def add_delete( namespace: str, selector: Mapping[str, Any], multi: bool, - collation: Optional[Mapping[str, Any]] = None, - hint: Union[str, dict[str, Any], None] = None, + collation: Mapping[str, Any] | None = None, + hint: str | dict[str, Any] | None = None, ) -> None: """Create a delete document and add it to the list of ops.""" cmd = {"delete": -1, "filter": selector, "multi": multi} diff --git a/pymongo/_csot.py b/pymongo/_csot.py index f586c21a9f..e0023532a1 100644 --- a/pymongo/_csot.py +++ b/pymongo/_csot.py @@ -23,12 +23,12 @@ from collections.abc import MutableMapping from contextlib import AbstractContextManager from contextvars import ContextVar, Token -from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, cast +from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast if TYPE_CHECKING: from pymongo.write_concern import WriteConcern -TIMEOUT: ContextVar[Optional[float]] = ContextVar("TIMEOUT", default=None) +TIMEOUT: ContextVar[float | None] = ContextVar("TIMEOUT", default=None) RTT: ContextVar[float] = ContextVar("RTT", default=0.0) DEADLINE: ContextVar[float] = ContextVar("DEADLINE", default=float("inf")) @@ -39,7 +39,7 @@ def reset_all() -> None: DEADLINE.set(float("inf")) -def get_timeout() -> Optional[float]: +def get_timeout() -> float | None: return TIMEOUT.get(None) @@ -55,7 +55,7 @@ def set_rtt(rtt: float) -> None: RTT.set(rtt) -def remaining() -> Optional[float]: +def remaining() -> float | None: if not get_timeout(): return None return DEADLINE.get() - time.monotonic() @@ -78,9 +78,9 @@ class _TimeoutContext(AbstractContextManager[Any]): client.db.coll.insert_one({}) """ - def __init__(self, timeout: Optional[float]): + def __init__(self, timeout: float | None): self._timeout = timeout - self._tokens: Optional[tuple[Token[Optional[float]], Token[float], Token[float]]] = None + self._tokens: tuple[Token[float | None], Token[float], Token[float]] | None = None def __enter__(self) -> None: timeout_token = TIMEOUT.set(self._timeout) @@ -128,9 +128,7 @@ def csot_wrapper(self: Any, *args: Any, **kwargs: Any) -> Any: return cast(F, csot_wrapper) -def apply_write_concern( - cmd: MutableMapping[str, Any], write_concern: Optional[WriteConcern] -) -> None: +def apply_write_concern(cmd: MutableMapping[str, Any], write_concern: WriteConcern | None) -> None: """Apply the given write concern to a command.""" if not write_concern or write_concern.is_server_default: return diff --git a/pymongo/_kms_connect_shared.py b/pymongo/_kms_connect_shared.py index 47665181ee..4dd9ed4cf9 100644 --- a/pymongo/_kms_connect_shared.py +++ b/pymongo/_kms_connect_shared.py @@ -32,7 +32,7 @@ import urllib.parse from collections.abc import Awaitable, Mapping from dataclasses import dataclass -from typing import Any, Callable, Optional +from typing import Any, Callable from pymongo.errors import ConfigurationError @@ -187,8 +187,8 @@ class HTTPProxyKMSConnect: def __init__( self, proxy_url: str, - ssl_context: Optional[ssl.SSLContext] = None, - headers: Optional[Mapping[str, str]] = None, + ssl_context: ssl.SSLContext | None = None, + headers: Mapping[str, str] | None = None, ): if not isinstance(proxy_url, str): raise TypeError(f"proxy_url must be a string, not {type(proxy_url)}") @@ -226,7 +226,7 @@ def __init__( raise ConfigurationError(f"invalid proxy_url: {_redact_proxy_url(proxy_url)!r}") self.port = port if split.scheme == "https": - self.ssl_context: Optional[ssl.SSLContext] = ( + self.ssl_context: ssl.SSLContext | None = ( ssl.create_default_context() if ssl_context is None else ssl_context ) else: @@ -294,7 +294,7 @@ def _tunnel(self, sock: socket.socket, context: KMSConnectContext, deadline: flo raise OSError(f"proxy refused CONNECT to {target}: {status!r}") def _bridge( - self, proxy: socket.socket, late_sockets: Optional[list[socket.socket]] = None + self, proxy: socket.socket, late_sockets: list[socket.socket] | None = None ) -> socket.socket: """Relay a TLS proxy connection through a socketpair. @@ -368,7 +368,7 @@ def relay(src: socket.socket, dst: socket.socket) -> None: return driver_side def __call__( - self, context: KMSConnectContext, *, deadline: Optional[float] = None + self, context: KMSConnectContext, *, deadline: float | None = None ) -> socket.socket: # One deadline for all three phases; per-phase timeouts would multiply # the caller's budget. @@ -380,7 +380,7 @@ def _connect_via( self, context: KMSConnectContext, deadline: float, - late_sockets: Optional[list[socket.socket]] = None, + late_sockets: list[socket.socket] | None = None, ) -> socket.socket: # A configurable KMS host could inject CR/LF into, or split the # request line of, the CONNECT request. @@ -414,7 +414,7 @@ def _connect_proxy(self, deadline: float) -> socket.socket: # # DNS resolution is not bounded by the deadline; a slow lookup can # exceed the budget, as in the driver's own connect path. - last_error: Optional[OSError] = None + last_error: OSError | None = None for family, socktype, proto, _, sockaddr in socket.getaddrinfo( self.host, self.port, type=socket.SOCK_STREAM ): diff --git a/pymongo/_op_id.py b/pymongo/_op_id.py index 164ceafa62..311aceb5b7 100644 --- a/pymongo/_op_id.py +++ b/pymongo/_op_id.py @@ -24,9 +24,9 @@ from contextlib import AbstractContextManager from contextvars import ContextVar -from typing import Any, Optional +from typing import Any -OP_ID: ContextVar[Optional[int]] = ContextVar("OP_ID", default=None) +OP_ID: ContextVar[int | None] = ContextVar("OP_ID", default=None) def reset() -> None: @@ -36,7 +36,7 @@ def reset() -> None: class _OpIdContext(AbstractContextManager[Any]): """Set OP_ID for the duration of a with block.""" - def __init__(self, op_id: Optional[int]): + def __init__(self, op_id: int | None): self._op_id = op_id def __enter__(self) -> None: diff --git a/pymongo/_psl.py b/pymongo/_psl.py index e0130188ff..181f4c3ed1 100644 --- a/pymongo/_psl.py +++ b/pymongo/_psl.py @@ -17,9 +17,8 @@ from __future__ import annotations from pathlib import Path -from typing import Optional -_PUBLIC_SUFFIXES: Optional[tuple[set[str], set[str], set[str]]] = None +_PUBLIC_SUFFIXES: tuple[set[str], set[str], set[str]] | None = None # Single labels that srvAllowedHostsSuffix may be set to. See the # srvAllowedHostsSuffix section of the Initial DNS Seedlist Discovery spec. diff --git a/pymongo/_telemetry.py b/pymongo/_telemetry.py index 5c83b99dc9..1596f82fb9 100644 --- a/pymongo/_telemetry.py +++ b/pymongo/_telemetry.py @@ -20,7 +20,7 @@ import queue import time from collections.abc import MutableMapping -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from pymongo import _op_id from pymongo.logger import ( @@ -57,7 +57,7 @@ def _monotonic_duration(start: float) -> float: return max(0.0, time.monotonic() - start) -def _generate_op_id_or_none(listeners: Optional[_EventListeners]) -> Optional[int]: +def _generate_op_id_or_none(listeners: _EventListeners | None) -> int | None: """Return a random operation id if it would be consumed by APM events or logging, else None.""" return ( _randint() @@ -100,14 +100,14 @@ class _CommandTelemetry: def __init__( self, - topology_id: Optional[ObjectId], + topology_id: ObjectId | None, conn: _ConnectionTelemetryInfo, - listeners: Optional[_EventListeners], + listeners: _EventListeners | None, cmd: MutableMapping[str, Any], dbname: str, request_id: int, - op_id: Optional[int], - name: Optional[str] = None, + op_id: int | None, + name: str | None = None, ) -> None: # NOTE: the _run_command fast path in command_runner.py inline this gate for performance # They must be kept in sync with any gating changes @@ -249,9 +249,9 @@ class _CmapTelemetry: def __init__( self, - client_id: Optional[ObjectId], + client_id: ObjectId | None, address: _Address, - listeners: Optional[_EventListeners], + listeners: _EventListeners | None, publish: bool, log: bool, ) -> None: @@ -299,7 +299,7 @@ def pool_ready(self) -> None: assert self._listeners is not None self._listeners.publish_pool_ready(self._address) - def pool_cleared(self, service_id: Optional[ObjectId], interrupt_connections: bool) -> None: + def pool_cleared(self, service_id: ObjectId | None, interrupt_connections: bool) -> None: """Emit the pool cleared log entry and APM event.""" # Log before publishing to prevent potential listener preemption in tests. if self._should_log: @@ -439,7 +439,7 @@ def __init__( self, topology_id: ObjectId, address: _Address, - listeners: Optional[_EventListeners], + listeners: _EventListeners | None, ) -> None: self._topology_id = topology_id self._address = address @@ -469,7 +469,7 @@ def started(self, awaited: bool) -> None: assert self._listeners is not None self._listeners.publish_server_heartbeat_started(self._address, awaited) - def emit_started_log(self, conn_id: int, server_conn_id: Optional[int], awaited: bool) -> None: + def emit_started_log(self, conn_id: int, server_conn_id: int | None, awaited: bool) -> None: """Emit the log entry for heartbeat started (after connection checkout).""" if self._should_log: self._emit_log( @@ -484,7 +484,7 @@ def succeeded( round_trip_time: float, response: Hello[Any], conn_id: int, - server_conn_id: Optional[int], + server_conn_id: int | None, ) -> None: """Emit the SUCCEEDED log entry and APM event.""" if self._should_publish: @@ -502,7 +502,7 @@ def succeeded( reply=response.document, ) - def failed(self, error: Exception, conn_id: Optional[int], awaited: bool) -> None: + def failed(self, error: Exception, conn_id: int | None, awaited: bool) -> None: """Emit the FAILED log entry and APM event.""" should_publish = self._should_publish should_log = self._should_log @@ -533,8 +533,8 @@ class _SdamTelemetry: def __init__( self, topology_id: ObjectId, - listeners: Optional[_EventListeners], - events: Optional[queue.Queue[Any]], + listeners: _EventListeners | None, + events: queue.Queue[Any] | None, ) -> None: self._topology_id = topology_id self._listeners = listeners @@ -663,7 +663,7 @@ def __init__( topology_id: ObjectId, selector: Any, operation: str, - operation_id: Optional[int], + operation_id: int | None, topology_description: TopologyDescription, ) -> None: self._topology_id = topology_id @@ -720,10 +720,10 @@ def log_server_selection_succeeded( topology_id: ObjectId, selector: Any, operation: str, - operation_id: Optional[int], + operation_id: int | None, topology_description: TopologyDescription, server_host: str, - server_port: Optional[int], + server_port: int | None, ) -> None: """Emit the server selection SUCCEEDED log entry.""" if _is_debug_enabled(_SERVER_SELECTION_LOGGER): @@ -758,9 +758,9 @@ def log_srv_monitor_invalid_host(host: str, failure: Exception) -> None: def log_command_retry( - topology_id: Optional[ObjectId], + topology_id: ObjectId | None, command_name: str, - operation_id: Optional[int], + operation_id: int | None, attempt_number: int, is_write: bool, ) -> None: diff --git a/pymongo/_version.py b/pymongo/_version.py index caaab856b1..1e0507caa8 100644 --- a/pymongo/_version.py +++ b/pymongo/_version.py @@ -17,16 +17,15 @@ from __future__ import annotations import re -from typing import Union __version__ = "4.20.0.dev0" -def get_version_tuple(version: str) -> tuple[Union[int, str], ...]: +def get_version_tuple(version: str) -> tuple[int | str, ...]: pattern = r"(?P\d+).(?P\d+).(?P\d+)(?P.*)" match = re.match(pattern, version) if match: - parts: list[Union[int, str]] = [int(match[part]) for part in ["major", "minor", "patch"]] + parts: list[int | str] = [int(match[part]) for part in ["major", "minor", "patch"]] if match["rest"]: parts.append(match["rest"]) elif re.match(r"\d+.\d+", version): diff --git a/pymongo/asynchronous/_kms_connect.py b/pymongo/asynchronous/_kms_connect.py index ab74919beb..0b2d8a8086 100644 --- a/pymongo/asynchronous/_kms_connect.py +++ b/pymongo/asynchronous/_kms_connect.py @@ -25,7 +25,7 @@ import inspect import socket import ssl -from typing import TYPE_CHECKING, Any, Optional, Union, cast +from typing import TYPE_CHECKING, Any, cast from pymongo import _csot from pymongo._kms_connect_shared import ( @@ -57,9 +57,9 @@ async def _connect_kms( address: _Address, opts: PoolOptions, - kms_connect_callback: Optional[AsyncKMSConnectCallback], + kms_connect_callback: AsyncKMSConnectCallback | None, timeout: float, -) -> Union[socket.socket, _sslConn]: +) -> socket.socket | _sslConn: """Connect to a KMS host and perform the TLS handshake over the socket. Uses ``kms_connect_callback`` when one is provided, otherwise connects diff --git a/pymongo/asynchronous/aggregation.py b/pymongo/asynchronous/aggregation.py index 8d21ecf46f..766672aee4 100644 --- a/pymongo/asynchronous/aggregation.py +++ b/pymongo/asynchronous/aggregation.py @@ -17,7 +17,7 @@ from __future__ import annotations from collections.abc import Callable, Mapping, MutableMapping -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any from pymongo import common from pymongo.collation import validate_collation_or_none @@ -47,13 +47,13 @@ class _AggregationCommand: def __init__( self, - target: Union[AsyncDatabase[Any], AsyncCollection[Any]], + target: AsyncDatabase[Any] | AsyncCollection[Any], cursor_class: type[AsyncCommandCursor[Any]], pipeline: _Pipeline, options: MutableMapping[str, Any], - let: Optional[Mapping[str, Any]] = None, - user_fields: Optional[MutableMapping[str, Any]] = None, - result_processor: Optional[Callable[[Mapping[str, Any]], None]] = None, + let: Mapping[str, Any] | None = None, + user_fields: MutableMapping[str, Any] | None = None, + result_processor: Callable[[Mapping[str, Any]], None] | None = None, comment: Any = None, ) -> None: if "explain" in options: @@ -103,10 +103,10 @@ def __init__( self._collation = validate_collation_or_none(options.pop("collation", None)) self._max_await_time_ms = options.pop("maxAwaitTimeMS", None) - self._write_preference: Optional[_AggWritePref] = None + self._write_preference: _AggWritePref | None = None @property - def _aggregation_target(self) -> Union[str, int]: + def _aggregation_target(self) -> str | int: """The argument to pass to the aggregate command.""" raise NotImplementedError @@ -125,8 +125,8 @@ def _database(self) -> AsyncDatabase[Any]: raise NotImplementedError def get_read_preference( - self, session: Optional[AsyncClientSession] - ) -> Union[_AggWritePref, _ServerMode]: + self, session: AsyncClientSession | None + ) -> _AggWritePref | _ServerMode: if self._write_preference: return self._write_preference pref = self._target._read_preference_for(session) @@ -136,7 +136,7 @@ def get_read_preference( async def get_cursor( self, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, server: Server, conn: AsyncConnection, read_preference: _ServerMode, diff --git a/pymongo/asynchronous/auth_oidc.py b/pymongo/asynchronous/auth_oidc.py index b0403d372a..090241f6c8 100644 --- a/pymongo/asynchronous/auth_oidc.py +++ b/pymongo/asynchronous/auth_oidc.py @@ -21,7 +21,7 @@ import time from collections.abc import Mapping, MutableMapping from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any import bson from pymongo._csot import remaining @@ -54,7 +54,7 @@ class _OIDCAuthenticator(_OIDCAuthenticatorBase): else: lock: threading.Lock = field(default_factory=_async_create_lock) # type: ignore[assignment, no-redef] - async def reauthenticate(self, conn: AsyncConnection) -> Optional[Mapping[str, Any]]: + async def reauthenticate(self, conn: AsyncConnection) -> Mapping[str, Any] | None: """Handle a reauthenticate from the server.""" # Invalidate the token for the connection. self._invalidate(conn.oidc_token_gen_id) @@ -63,7 +63,7 @@ async def reauthenticate(self, conn: AsyncConnection) -> Optional[Mapping[str, A return await self._authenticate_machine(conn) return await self._authenticate_human(conn) - async def authenticate(self, conn: AsyncConnection) -> Optional[Mapping[str, Any]]: + async def authenticate(self, conn: AsyncConnection) -> Mapping[str, Any] | None: """Handle an initial authenticate request.""" # First handle speculative auth. # If it succeeded, we are done. @@ -95,7 +95,7 @@ async def _authenticate_machine(self, conn: AsyncConnection) -> Mapping[str, Any raise return await self._sasl_start_jwt(conn) - async def _authenticate_human(self, conn: AsyncConnection) -> Optional[Mapping[str, Any]]: + async def _authenticate_human(self, conn: AsyncConnection) -> Mapping[str, Any] | None: # If we have a cached access token, try a JwtStepRequest. # authentication fails with error code 18, invalidate the access token, # and try to authenticate again. If authentication fails for any other @@ -128,9 +128,9 @@ async def _authenticate_human(self, conn: AsyncConnection) -> Optional[Mapping[s # Attempt to authenticate with a JwtStepRequest. return await self._sasl_continue_jwt(conn, start_resp) - async def _get_access_token(self) -> Optional[str]: + async def _get_access_token(self) -> str | None: properties = self.properties - cb: Union[None, OIDCCallback] + cb: None | OIDCCallback resp: OIDCCallbackResult is_human = properties.human_callback is not None @@ -221,7 +221,7 @@ async def _sasl_start_jwt(self, conn: AsyncConnection) -> Mapping[str, Any]: async def _authenticate_oidc( credentials: MongoCredential, conn: AsyncConnection, reauthenticate: bool -) -> Optional[Mapping[str, Any]]: +) -> Mapping[str, Any] | None: """Authenticate using MONGODB-OIDC.""" authenticator = _get_authenticator(credentials, conn.address, _OIDCAuthenticator) if reauthenticate: diff --git a/pymongo/asynchronous/bulk.py b/pymongo/asynchronous/bulk.py index 7b9e8e78af..d26ddb1dd0 100644 --- a/pymongo/asynchronous/bulk.py +++ b/pymongo/asynchronous/bulk.py @@ -25,8 +25,6 @@ from typing import ( TYPE_CHECKING, Any, - Optional, - Union, ) from pymongo import _csot @@ -101,7 +99,7 @@ async def unack_write( max_doc_size: int, docs: list[Mapping[str, Any]], client: AsyncMongoClient[Any], - ) -> Optional[Mapping[str, Any]]: + ) -> Mapping[str, Any] | None: """Send an unacknowledged batch write command.""" # Historically the STARTED log omits the documents while the published # CommandStartedEvent includes them, so log ``cmd`` but publish a copy @@ -122,7 +120,7 @@ async def unack_write( async def _execute_batch_unack( self, - bwc: Union[_BulkWriteContext, _EncryptedBulkWriteContext], + bwc: _BulkWriteContext | _EncryptedBulkWriteContext, cmd: dict[str, Any], ops: list[Mapping[str, Any]], client: AsyncMongoClient[Any], @@ -149,7 +147,7 @@ async def _execute_batch_unack( async def _execute_batch( self, - bwc: Union[_BulkWriteContext, _EncryptedBulkWriteContext], + bwc: _BulkWriteContext | _EncryptedBulkWriteContext, cmd: dict[str, Any], ops: list[Mapping[str, Any]], client: AsyncMongoClient[Any], @@ -173,12 +171,12 @@ async def _execute_command( self, generator: Iterator[Any], write_concern: WriteConcern, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, conn: AsyncConnection, - op_id: Optional[int], + op_id: int | None, retryable: bool, full_result: MutableMapping[str, Any], - final_write_concern: Optional[WriteConcern] = None, + final_write_concern: WriteConcern | None = None, ) -> None: db_name = self.collection.database.name client = self.collection.database.client @@ -276,7 +274,7 @@ async def execute_command( self, generator: Iterator[Any], write_concern: WriteConcern, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, operation: str, ) -> dict[str, Any]: """Execute using write commands.""" @@ -295,7 +293,7 @@ async def execute_command( op_id = _generate_op_id_or_none(client._event_listeners) async def retryable_bulk( - session: Optional[AsyncClientSession], conn: AsyncConnection, retryable: bool + session: AsyncClientSession | None, conn: AsyncConnection, retryable: bool ) -> None: await self._execute_command( generator, @@ -425,7 +423,7 @@ async def execute_no_results( async def execute( self, write_concern: WriteConcern, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, operation: str, ) -> Any: """Execute operations.""" diff --git a/pymongo/asynchronous/change_stream.py b/pymongo/asynchronous/change_stream.py index c95c23e823..daf0cdfd5b 100644 --- a/pymongo/asynchronous/change_stream.py +++ b/pymongo/asynchronous/change_stream.py @@ -16,7 +16,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from bson import _bson_to_dict from pymongo import _csot @@ -60,15 +60,13 @@ class AsyncChangeStream(_AgnosticChangeStream[_DocumentType, "AsyncMongoClient[_ .. seealso:: The MongoDB documentation on `changeStreams `_. """ - _session: Optional[AsyncClientSession] + _session: AsyncClientSession | None async def _initialize_cursor(self) -> None: # Initialize cursor. self._cursor = await self._create_cursor() - async def _run_aggregation_cmd( - self, session: Optional[AsyncClientSession] - ) -> AsyncCommandCursor: # type: ignore[type-arg] + async def _run_aggregation_cmd(self, session: AsyncClientSession | None) -> AsyncCommandCursor: # type: ignore[type-arg] """Run the full aggregation pipeline for this AsyncChangeStream and return the corresponding AsyncCommandCursor. """ @@ -150,7 +148,7 @@ async def next(self) -> _DocumentType: __anext__ = next @_csot.apply - async def try_next(self) -> Optional[_DocumentType]: + async def try_next(self) -> _DocumentType | None: """Advance the cursor without blocking indefinitely. This method returns the next change document without waiting diff --git a/pymongo/asynchronous/client_bulk.py b/pymongo/asynchronous/client_bulk.py index 2cd9a0105b..639eb66e5f 100644 --- a/pymongo/asynchronous/client_bulk.py +++ b/pymongo/asynchronous/client_bulk.py @@ -25,8 +25,6 @@ from typing import ( TYPE_CHECKING, Any, - Optional, - Union, ) from pymongo import _csot @@ -86,7 +84,7 @@ async def write_command( bwc: _ClientBulkWriteContext, cmd: MutableMapping[str, Any], request_id: int, - msg: Union[bytes, dict[str, Any]], + msg: bytes | dict[str, Any], op_docs: list[Mapping[str, Any]], ns_docs: list[Mapping[str, Any]], client: AsyncMongoClient[Any], @@ -117,7 +115,7 @@ async def unack_write( op_docs: list[Mapping[str, Any]], ns_docs: list[Mapping[str, Any]], client: AsyncMongoClient[Any], - ) -> Optional[Mapping[str, Any]]: + ) -> Mapping[str, Any] | None: """Send an unacknowledged client-level batch write command.""" # Historically the STARTED log omits the ops/nsInfo while the published # CommandStartedEvent includes them, so log ``cmd`` but publish a copy @@ -173,7 +171,7 @@ async def _process_results_cursor( full_result: MutableMapping[str, Any], result: MutableMapping[str, Any], conn: AsyncConnection, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, ) -> None: """Internal helper for processing the server reply command cursor.""" if result.get("cursor"): @@ -223,12 +221,12 @@ async def _process_results_cursor( async def _execute_command( self, write_concern: WriteConcern, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, conn: AsyncConnection, - op_id: Optional[int], + op_id: int | None, retryable: bool, full_result: MutableMapping[str, Any], - final_write_concern: Optional[WriteConcern] = None, + final_write_concern: WriteConcern | None = None, ) -> None: """Internal helper for executing batches of bulkWrite commands.""" db_name = "admin" @@ -361,7 +359,7 @@ async def _execute_command( async def execute_command( self, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, operation: str, ) -> MutableMapping[str, Any]: """Execute commands with w=1 WriteConcern.""" @@ -382,7 +380,7 @@ async def execute_command( op_id = _generate_op_id_or_none(self.client._event_listeners) async def retryable_bulk( - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, conn: AsyncConnection, retryable: bool, ) -> None: @@ -473,7 +471,7 @@ async def execute_no_results( async def execute( self, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, operation: str, ) -> Any: """Execute operations.""" diff --git a/pymongo/asynchronous/client_session.py b/pymongo/asynchronous/client_session.py index 61606ead75..97f20fa1ad 100644 --- a/pymongo/asynchronous/client_session.py +++ b/pymongo/asynchronous/client_session.py @@ -145,7 +145,6 @@ TYPE_CHECKING, Any, Callable, - Optional, TypeVar, ) @@ -184,7 +183,7 @@ _IS_SYNC = False -_SESSION: ContextVar[Optional[AsyncClientSession]] = ContextVar("SESSION", default=None) +_SESSION: ContextVar[AsyncClientSession | None] = ContextVar("SESSION", default=None) class _AsyncBoundSessionContext: @@ -192,7 +191,7 @@ class _AsyncBoundSessionContext: def __init__(self, session: AsyncClientSession, end_session: bool) -> None: self._session = session - self._session_token: Optional[Token[AsyncClientSession]] = None + self._session_token: Token[AsyncClientSession] | None = None self._end_session = end_session async def __aenter__(self) -> AsyncClientSession: @@ -218,9 +217,9 @@ async def __aenter__(self) -> _TransactionContext: async def __aexit__( self, - exc_type: Optional[type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: if self.__session.in_transaction: if exc_val is None: @@ -233,7 +232,7 @@ class _Transaction(_TransactionBase["AsyncConnection"]): """Internal class to hold transaction information in a AsyncClientSession.""" _conn_mgr_cls = _ConnectionManager - conn_mgr: Optional[_ConnectionManager] + conn_mgr: _ConnectionManager | None async def unpin(self) -> None: self.pinned_address = None @@ -303,10 +302,10 @@ async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: async def with_transaction( self, callback: Callable[[AsyncClientSession], Awaitable[_T]], - read_concern: Optional[ReadConcern] = None, - write_concern: Optional[WriteConcern] = None, - read_preference: Optional[_ServerMode] = None, - max_commit_time_ms: Optional[int] = None, + read_concern: ReadConcern | None = None, + write_concern: WriteConcern | None = None, + read_preference: _ServerMode | None = None, + max_commit_time_ms: int | None = None, ) -> _T: """Execute a callback in a transaction. @@ -402,7 +401,7 @@ async def callback(session, custom_arg, custom_kwarg=None): """ start_time = time.monotonic() retry = 0 - last_error: Optional[BaseException] = None + last_error: BaseException | None = None while True: if retry: # Implement exponential backoff on retry. jitter = random.random() # noqa: S311 @@ -460,10 +459,10 @@ async def callback(session, custom_arg, custom_kwarg=None): async def start_transaction( self, - read_concern: Optional[ReadConcern] = None, - write_concern: Optional[WriteConcern] = None, - read_preference: Optional[_ServerMode] = None, - max_commit_time_ms: Optional[int] = None, + read_concern: ReadConcern | None = None, + write_concern: WriteConcern | None = None, + read_preference: _ServerMode | None = None, + max_commit_time_ms: int | None = None, ) -> AbstractAsyncContextManager[Any]: """Start a multi-statement transaction. @@ -576,7 +575,7 @@ async def _finish_transaction_with_retry(self, command_name: str) -> dict[str, A """ async def func( - _session: Optional[AsyncClientSession], conn: AsyncConnection, _retryable: bool + _session: AsyncClientSession | None, conn: AsyncConnection, _retryable: bool ) -> dict[str, Any]: return await self._finish_transaction(conn, command_name) diff --git a/pymongo/asynchronous/collection.py b/pymongo/asynchronous/collection.py index 924635d7dc..89ba436f4c 100644 --- a/pymongo/asynchronous/collection.py +++ b/pymongo/asynchronous/collection.py @@ -25,9 +25,7 @@ Callable, Generic, NoReturn, - Optional, TypeVar, - Union, cast, overload, ) @@ -94,14 +92,14 @@ _FIND_AND_MODIFY_DOC_FIELDS = {"value": 1} -_WriteOp = Union[ - InsertOne[_DocumentType], - DeleteOne, - DeleteMany, - ReplaceOne[_DocumentType], - UpdateOne, - UpdateMany, -] +_WriteOp = ( + InsertOne[_DocumentType] + | DeleteOne + | DeleteMany + | ReplaceOne[_DocumentType] + | UpdateOne + | UpdateMany +) class ReturnDocument: @@ -136,12 +134,12 @@ def __init__( self, database: AsyncDatabase[_DocumentType], name: str, - create: Optional[bool] = False, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, - session: Optional[AsyncClientSession] = None, + create: bool | None = False, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, + session: AsyncClientSession | None = None, **kwargs: Any, ) -> None: """Get / create an asynchronous Mongo collection. @@ -331,26 +329,26 @@ def database(self) -> AsyncDatabase[_DocumentType]: def with_options( self, codec_options: None = None, - read_preference: Optional[_ServerMode] = ..., - write_concern: Optional[WriteConcern] = ..., - read_concern: Optional[ReadConcern] = ..., + read_preference: _ServerMode | None = ..., + write_concern: WriteConcern | None = ..., + read_concern: ReadConcern | None = ..., ) -> AsyncCollection[_DocumentType]: ... @overload def with_options( self, codec_options: bson.CodecOptions[_DocumentTypeArg], - read_preference: Optional[_ServerMode] = ..., - write_concern: Optional[WriteConcern] = ..., - read_concern: Optional[ReadConcern] = ..., + read_preference: _ServerMode | None = ..., + write_concern: WriteConcern | None = ..., + read_concern: ReadConcern | None = ..., ) -> 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, + codec_options: bson.CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> AsyncCollection[_DocumentType] | AsyncCollection[_DocumentTypeArg]: """Get a clone of this collection changing the specified settings. @@ -415,18 +413,18 @@ def __call__(self, *args: Any, **kwargs: Any) -> NoReturn: async def watch( self, - pipeline: Optional[_Pipeline] = None, - full_document: Optional[str] = None, - resume_after: Optional[Mapping[str, Any]] = None, - max_await_time_ms: Optional[int] = None, - batch_size: Optional[int] = None, - collation: Optional[_CollationIn] = None, - start_at_operation_time: Optional[Timestamp] = None, - session: Optional[AsyncClientSession] = None, - start_after: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, - full_document_before_change: Optional[str] = None, - show_expanded_events: Optional[bool] = None, + pipeline: _Pipeline | None = None, + full_document: str | None = None, + resume_after: Mapping[str, Any] | None = None, + max_await_time_ms: int | None = None, + batch_size: int | None = None, + collation: _CollationIn | None = None, + start_at_operation_time: Timestamp | None = None, + session: AsyncClientSession | None = None, + start_after: Mapping[str, Any] | None = None, + comment: Any | None = None, + full_document_before_change: str | None = None, + show_expanded_events: bool | None = None, ) -> AsyncCollectionChangeStream[_DocumentType]: """Watch changes on this collection. @@ -558,16 +556,16 @@ async def _command( self, conn: AsyncConnection, command: MutableMapping[str, Any], - read_preference: Optional[_ServerMode] = None, - codec_options: Optional[CodecOptions[Mapping[str, Any]]] = None, + read_preference: _ServerMode | None = None, + codec_options: CodecOptions[Mapping[str, Any]] | None = None, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - read_concern: Optional[ReadConcern] = None, - write_concern: Optional[WriteConcern] = None, - collation: Optional[_CollationIn] = None, - session: Optional[AsyncClientSession] = None, + allowable_errors: Sequence[str | int] | None = None, + read_concern: ReadConcern | None = None, + write_concern: WriteConcern | None = None, + collation: _CollationIn | None = None, + session: AsyncClientSession | None = None, retryable_write: bool = False, - user_fields: Optional[Any] = None, + user_fields: Any | None = None, ) -> Mapping[str, Any]: """Internal command helper. @@ -616,9 +614,9 @@ async def _create_helper( self, name: str, options: MutableMapping[str, Any], - collation: Optional[_CollationIn], - session: Optional[AsyncClientSession], - encrypted_fields: Optional[Mapping[str, Any]] = None, + collation: _CollationIn | None, + session: AsyncClientSession | None, + encrypted_fields: Mapping[str, Any] | None = None, qev2_required: bool = False, ) -> None: """Sends a create command with the given options.""" @@ -632,7 +630,7 @@ async def _create_helper( cmd.update(options) async def inner( - session: Optional[AsyncClientSession], conn: AsyncConnection, _retryable_write: bool + session: AsyncClientSession | None, conn: AsyncConnection, _retryable_write: bool ) -> None: if qev2_required and conn.max_wire_version < 21: raise ConfigurationError( @@ -655,7 +653,7 @@ async def inner( async def _create( self, options: MutableMapping[str, Any], - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, ) -> None: collation = validate_collation_or_none(options.pop("collation", None)) encrypted_fields = options.pop("encryptedFields", None) @@ -684,10 +682,10 @@ async def bulk_write( self, requests: Sequence[_WriteOp[_DocumentType]], ordered: bool = True, - bypass_document_validation: Optional[bool] = None, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, - let: Optional[Mapping[str, Any]] = None, + bypass_document_validation: bool | None = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, + let: Mapping[str, Any] | None = None, ) -> BulkWriteResult: """Send a batch of write operations to the server. @@ -782,10 +780,10 @@ async def _insert_one( doc: Mapping[str, Any], ordered: bool, write_concern: WriteConcern, - op_id: Optional[int], - bypass_doc_val: Optional[bool], - session: Optional[AsyncClientSession], - comment: Optional[Any] = None, + op_id: int | None, + bypass_doc_val: bool | None, + session: AsyncClientSession | None, + comment: Any | None = None, ) -> Any: """Internal helper for inserting a single document.""" write_concern = write_concern or self.write_concern @@ -795,7 +793,7 @@ async def _insert_one( command["comment"] = comment async def _insert_command( - session: Optional[AsyncClientSession], conn: AsyncConnection, retryable_write: bool + session: AsyncClientSession | None, conn: AsyncConnection, retryable_write: bool ) -> None: if bypass_doc_val is not None: command["bypassDocumentValidation"] = bypass_doc_val @@ -822,10 +820,10 @@ async def _insert_command( async def insert_one( self, - document: Union[_DocumentType, RawBSONDocument], - bypass_document_validation: Optional[bool] = None, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + document: _DocumentType | RawBSONDocument, + bypass_document_validation: bool | None = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, ) -> InsertOneResult: """Insert a single document. @@ -887,11 +885,11 @@ async def insert_one( @_csot.apply async def insert_many( self, - documents: Iterable[Union[_DocumentType, RawBSONDocument]], + documents: Iterable[_DocumentType | RawBSONDocument], ordered: bool = True, - bypass_document_validation: Optional[bool] = None, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + bypass_document_validation: bool | None = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, ) -> InsertManyResult: """Insert an iterable of documents. @@ -963,22 +961,22 @@ async def _update( self, conn: AsyncConnection, criteria: Mapping[str, Any], - document: Union[Mapping[str, Any], _Pipeline], + document: Mapping[str, Any] | _Pipeline, upsert: bool = False, multi: bool = False, - write_concern: Optional[WriteConcern] = None, - op_id: Optional[int] = None, + write_concern: WriteConcern | None = None, + op_id: int | None = None, ordered: bool = True, - bypass_doc_val: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, + bypass_doc_val: bool | None = None, + collation: _CollationIn | None = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, retryable_write: bool = False, - let: Optional[Mapping[str, Any]] = None, - sort: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, - ) -> Optional[Mapping[str, Any]]: + let: Mapping[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, + comment: Any | None = None, + ) -> Mapping[str, Any] | None: """Internal update / replace helper.""" validate_boolean("upsert", upsert) collation = validate_collation_or_none(collation) @@ -1054,27 +1052,27 @@ async def _update( async def _update_retryable( self, criteria: Mapping[str, Any], - document: Union[Mapping[str, Any], _Pipeline], + document: Mapping[str, Any] | _Pipeline, operation: str, upsert: bool = False, multi: bool = False, - write_concern: Optional[WriteConcern] = None, - op_id: Optional[int] = None, + write_concern: WriteConcern | None = None, + op_id: int | None = None, ordered: bool = True, - bypass_doc_val: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - sort: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, - ) -> Optional[Mapping[str, Any]]: + bypass_doc_val: bool | None = None, + collation: _CollationIn | None = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, + comment: Any | None = None, + ) -> Mapping[str, Any] | None: """Internal update / replace helper.""" async def _update( - session: Optional[AsyncClientSession], conn: AsyncConnection, retryable_write: bool - ) -> Optional[Mapping[str, Any]]: + session: AsyncClientSession | None, conn: AsyncConnection, retryable_write: bool + ) -> Mapping[str, Any] | None: return await self._update( conn, criteria, @@ -1107,13 +1105,13 @@ async def replace_one( filter: Mapping[str, Any], replacement: Mapping[str, Any], upsert: bool = False, - bypass_document_validation: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - sort: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + bypass_document_validation: bool | None = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> UpdateResult: """Replace a single document matching the filter. @@ -1213,16 +1211,16 @@ async def replace_one( async def update_one( self, filter: Mapping[str, Any], - update: Union[Mapping[str, Any], _Pipeline], + update: Mapping[str, Any] | _Pipeline, upsert: bool = False, - bypass_document_validation: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - sort: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + bypass_document_validation: bool | None = None, + collation: _CollationIn | None = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> UpdateResult: """Update a single document matching the filter. @@ -1332,15 +1330,15 @@ async def update_one( async def update_many( self, filter: Mapping[str, Any], - update: Union[Mapping[str, Any], _Pipeline], + update: Mapping[str, Any] | _Pipeline, upsert: bool = False, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - bypass_document_validation: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + bypass_document_validation: bool | None = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> UpdateResult: """Update one or more documents that match the filter. @@ -1431,9 +1429,9 @@ async def update_many( async def drop( self, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, - encrypted_fields: Optional[Mapping[str, Any]] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, + encrypted_fields: Mapping[str, Any] | None = None, ) -> None: """Alias for :meth:`~pymongo.asynchronous.database.AsyncDatabase.drop_collection`. @@ -1477,15 +1475,15 @@ async def _delete( conn: AsyncConnection, criteria: Mapping[str, Any], multi: bool, - write_concern: Optional[WriteConcern] = None, - op_id: Optional[int] = None, + write_concern: WriteConcern | None = None, + op_id: int | None = None, ordered: bool = True, - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, retryable_write: bool = False, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> Mapping[str, Any]: """Internal delete helper.""" common.validate_is_mapping("filter", criteria) @@ -1528,19 +1526,19 @@ async def _delete_retryable( self, criteria: Mapping[str, Any], multi: bool, - write_concern: Optional[WriteConcern] = None, - op_id: Optional[int] = None, + write_concern: WriteConcern | None = None, + op_id: int | None = None, ordered: bool = True, - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> Mapping[str, Any]: """Internal delete helper.""" async def _delete( - session: Optional[AsyncClientSession], conn: AsyncConnection, retryable_write: bool + session: AsyncClientSession | None, conn: AsyncConnection, retryable_write: bool ) -> Mapping[str, Any]: return await self._delete( conn, @@ -1567,11 +1565,11 @@ async def _delete( async def delete_one( self, filter: Mapping[str, Any], - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> DeleteResult: """Delete a single document matching the filter. @@ -1632,11 +1630,11 @@ async def delete_one( async def delete_many( self, filter: Mapping[str, Any], - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> DeleteResult: """Delete one or more documents matching the filter. @@ -1695,8 +1693,8 @@ async def delete_many( ) async def find_one( - self, filter: Optional[Any] = None, *args: Any, **kwargs: Any - ) -> Optional[_DocumentType]: + self, filter: Any | None = None, *args: Any, **kwargs: Any + ) -> _DocumentType | None: """Get a single document from the database. All arguments to :meth:`find` are also valid arguments for @@ -1971,11 +1969,11 @@ def find_raw_batches(self, *args: Any, **kwargs: Any) -> AsyncRawBatchCursor[_Do async def _count_cmd( self, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, conn: AsyncConnection, - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, cmd: dict[str, Any], - collation: Optional[Collation], + collation: Collation | None, ) -> int: """Internal count command helper.""" res = await self._command( @@ -1992,11 +1990,11 @@ async def _count_cmd( async def _aggregate_one_result( self, conn: AsyncConnection, - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, cmd: dict[str, Any], - collation: Optional[_CollationIn], - session: Optional[AsyncClientSession], - ) -> Optional[Mapping[str, Any]]: + collation: _CollationIn | None, + session: AsyncClientSession | None, + ) -> Mapping[str, Any] | None: """Internal helper to run an aggregate that returns a single result.""" result = await self._command( conn, @@ -2014,7 +2012,7 @@ async def _aggregate_one_result( batch = result["cursor"]["firstBatch"] return batch[0] if batch else None - async def estimated_document_count(self, comment: Optional[Any] = None, **kwargs: Any) -> int: + async def estimated_document_count(self, comment: Any | None = None, **kwargs: Any) -> int: """Get an estimate of the number of documents in this collection using collection metadata. @@ -2047,10 +2045,10 @@ async def estimated_document_count(self, comment: Optional[Any] = None, **kwargs kwargs["comment"] = comment async def _cmd( - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, _server: Server, conn: AsyncConnection, - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, ) -> int: cmd: dict[str, Any] = {"count": self._name} cmd.update(kwargs) @@ -2061,8 +2059,8 @@ async def _cmd( async def count_documents( self, filter: Mapping[str, Any], - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> int: """Count the number of documents in this collection. @@ -2133,10 +2131,10 @@ async def count_documents( collation = validate_collation_or_none(kwargs.pop("collation", None)) async def _cmd( - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, _server: Server, conn: AsyncConnection, - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, ) -> int: cmd: dict[str, Any] = {"aggregate": self._name, "pipeline": pipeline, "cursor": {}} cmd.update(kwargs) @@ -2152,10 +2150,10 @@ async def _cmd( async def _retryable_non_cursor_read( self, func: Callable[ - [Optional[AsyncClientSession], Server, AsyncConnection, Optional[_ServerMode]], + [AsyncClientSession | None, Server, AsyncConnection, _ServerMode | None], Coroutine[Any, Any, T], ], - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, operation: str, ) -> T: """Non-cursor read helper to handle implicit session creation.""" @@ -2166,8 +2164,8 @@ async def _retryable_non_cursor_read( async def create_indexes( self, indexes: Sequence[IndexModel], - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> list[str]: """Create one or more indexes on this collection. @@ -2212,7 +2210,7 @@ async def create_indexes( @_csot.apply async def _create_indexes( - self, indexes: Sequence[IndexModel], session: Optional[AsyncClientSession], **kwargs: Any + self, indexes: Sequence[IndexModel], session: AsyncClientSession | None, **kwargs: Any ) -> list[str]: """Internal createIndexes helper. @@ -2226,7 +2224,7 @@ async def _create_indexes( names = [] async def inner( - session: Optional[AsyncClientSession], conn: AsyncConnection, _retryable_write: bool + session: AsyncClientSession | None, conn: AsyncConnection, _retryable_write: bool ) -> list[str]: def gen_indexes() -> Iterator[Mapping[str, Any]]: for index in indexes: @@ -2257,8 +2255,8 @@ def gen_indexes() -> Iterator[Mapping[str, Any]]: async def create_index( self, keys: _IndexKeyHint, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> str: """Creates an index on this collection. @@ -2374,8 +2372,8 @@ async def create_index( async def drop_indexes( self, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> None: """Drops all indexes on this collection. @@ -2408,8 +2406,8 @@ async def drop_indexes( async def drop_index( self, index_or_name: _IndexKeyHint, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> None: """Drops the specified index on this collection. @@ -2457,8 +2455,8 @@ async def drop_index( async def _drop_index( self, index_or_name: _IndexKeyHint, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> None: name = index_or_name @@ -2474,7 +2472,7 @@ async def _drop_index( cmd["comment"] = comment async def inner( - session: Optional[AsyncClientSession], conn: AsyncConnection, _retryable_write: bool + session: AsyncClientSession | None, conn: AsyncConnection, _retryable_write: bool ) -> None: await self._command( conn, @@ -2489,8 +2487,8 @@ async def inner( async def list_indexes( self, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, ) -> AsyncCommandCursor[MutableMapping[str, Any]]: """Get a cursor over the index documents for this collection. @@ -2527,8 +2525,8 @@ async def list_indexes( async def _list_indexes( self, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, ) -> AsyncCommandCursor[MutableMapping[str, Any]]: codec_options: CodecOptions[Mapping[str, Any]] = CodecOptions(SON) coll = cast( @@ -2538,7 +2536,7 @@ async def _list_indexes( read_pref = (session and session._txn_read_preference()) or ReadPreference.PRIMARY async def _cmd( - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, _server: Server, conn: AsyncConnection, read_preference: _ServerMode, @@ -2574,8 +2572,8 @@ async def _cmd( async def index_information( self, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, ) -> MutableMapping[str, Any]: """Get information on this collection's indexes. @@ -2616,9 +2614,9 @@ async def index_information( async def list_search_indexes( self, - name: Optional[str] = None, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + name: str | None = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> AsyncCommandCursor[Mapping[str, Any]]: """Return a cursor over search indexes for the current collection. @@ -2677,8 +2675,8 @@ async def list_search_indexes( async def create_search_index( self, - model: Union[Mapping[str, Any], SearchIndexModel], - session: Optional[AsyncClientSession] = None, + model: Mapping[str, Any] | SearchIndexModel, + session: AsyncClientSession | None = None, comment: Any = None, **kwargs: Any, ) -> str: @@ -2708,8 +2706,8 @@ async def create_search_index( async def create_search_indexes( self, models: list[SearchIndexModel], - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> list[str]: """Create multiple search indexes for the current collection. @@ -2732,8 +2730,8 @@ async def create_search_indexes( async def _create_search_indexes( self, models: list[SearchIndexModel], - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> list[str]: if comment is not None: @@ -2751,7 +2749,7 @@ def gen_indexes() -> Iterator[Mapping[str, Any]]: cmd.update(kwargs) async def inner( - session: Optional[AsyncClientSession], conn: AsyncConnection, _retryable_write: bool + session: AsyncClientSession | None, conn: AsyncConnection, _retryable_write: bool ) -> list[str]: resp = await self._command( conn, @@ -2769,8 +2767,8 @@ async def inner( async def drop_search_index( self, name: str, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> None: """Delete a search index by index name. @@ -2793,7 +2791,7 @@ async def drop_search_index( cmd["comment"] = comment async def inner( - session: Optional[AsyncClientSession], conn: AsyncConnection, _retryable_write: bool + session: AsyncClientSession | None, conn: AsyncConnection, _retryable_write: bool ) -> None: await self._command( conn, @@ -2810,8 +2808,8 @@ async def update_search_index( self, name: str, definition: Mapping[str, Any], - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> None: """Update a search index by replacing the existing index definition with the provided definition. @@ -2835,7 +2833,7 @@ async def update_search_index( cmd["comment"] = comment async def inner( - session: Optional[AsyncClientSession], conn: AsyncConnection, _retryable_write: bool + session: AsyncClientSession | None, conn: AsyncConnection, _retryable_write: bool ) -> None: await self._command( conn, @@ -2850,8 +2848,8 @@ async def inner( async def options( self, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, ) -> MutableMapping[str, Any]: """Get the options set on this collection. @@ -2900,9 +2898,9 @@ async def _aggregate( aggregation_command: type[_AggregationCommand], pipeline: _Pipeline, cursor_class: type[AsyncCommandCursor], # type: ignore[type-arg] - session: Optional[AsyncClientSession], - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> AsyncCommandCursor[_DocumentType]: if comment is not None: @@ -2928,9 +2926,9 @@ async def _aggregate( async def aggregate( self, pipeline: _Pipeline, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> AsyncCommandCursor[_DocumentType]: """Perform an aggregation using the aggregation framework on this @@ -3032,8 +3030,8 @@ async def aggregate( async def aggregate_raw_batches( self, pipeline: _Pipeline, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> AsyncRawBatchCursor[_DocumentType]: """Perform an aggregation and retrieve batches of raw BSON. @@ -3080,8 +3078,8 @@ async def aggregate_raw_batches( async def rename( self, new_name: str, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> MutableMapping[str, Any]: """Rename this collection. @@ -3131,7 +3129,7 @@ async def rename( client = self._database.client async def inner( - session: Optional[AsyncClientSession], conn: AsyncConnection, _retryable_write: bool + session: AsyncClientSession | None, conn: AsyncConnection, _retryable_write: bool ) -> MutableMapping[str, Any]: return await conn.command( "admin", @@ -3147,10 +3145,10 @@ async def inner( async def distinct( self, key: str, - filter: Optional[Mapping[str, Any]] = None, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, - hint: Optional[_IndexKeyHint] = None, + filter: Mapping[str, Any] | None = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, + hint: _IndexKeyHint | None = None, **kwargs: Any, ) -> list[Any]: """Get a list of distinct values for `key` among all documents @@ -3206,10 +3204,10 @@ async def distinct( hint = helpers_shared._index_document(hint) async def _cmd( - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, _server: Server, conn: AsyncConnection, - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, ) -> list: # type: ignore[type-arg] cmd = {"distinct": self._name, "key": key} cmd.update(kwargs) @@ -3234,14 +3232,14 @@ async def _cmd( async def _find_and_modify( self, filter: Mapping[str, Any], - projection: Optional[Union[Mapping[str, Any], Iterable[str]]], - sort: Optional[_IndexList], - upsert: Optional[bool] = None, + projection: Mapping[str, Any] | Iterable[str] | None, + sort: _IndexList | None, + upsert: bool | None = None, return_document: bool = ReturnDocument.BEFORE, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, **kwargs: Any, ) -> Any: """Internal findAndModify helper.""" @@ -3257,7 +3255,7 @@ async def _find_and_modify( write_concern = self._write_concern_for_cmd(kwargs, session) async def _find_and_modify_helper( - session: Optional[AsyncClientSession], conn: AsyncConnection, retryable_write: bool + session: AsyncClientSession | None, conn: AsyncConnection, retryable_write: bool ) -> Any: cmd = {"findAndModify": self._name, "query": filter, "new": return_document} if let is not None: @@ -3304,14 +3302,14 @@ async def _find_and_modify_helper( async def find_one_and_delete( self, filter: Mapping[str, Any], - projection: Optional[Union[Mapping[str, Any], Iterable[str]]] = None, - sort: Optional[_IndexList] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + projection: Mapping[str, Any] | Iterable[str] | None = None, + sort: _IndexList | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, - ) -> Optional[_DocumentType]: + ) -> _DocumentType | None: """Finds a single document and deletes it, returning the document. >>> await db.test.count_documents({'x': 1}) @@ -3398,16 +3396,16 @@ async def find_one_and_replace( self, filter: Mapping[str, Any], replacement: Mapping[str, Any], - projection: Optional[Union[Mapping[str, Any], Iterable[str]]] = None, - sort: Optional[_IndexList] = None, + projection: Mapping[str, Any] | Iterable[str] | None = None, + sort: _IndexList | None = None, upsert: bool = False, return_document: bool = ReturnDocument.BEFORE, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, - ) -> Optional[_DocumentType]: + ) -> _DocumentType | None: """Finds a single document and replaces it, returning either the original or the replaced document. @@ -3516,18 +3514,18 @@ async def find_one_and_replace( async def find_one_and_update( self, filter: Mapping[str, Any], - update: Union[Mapping[str, Any], _Pipeline], - projection: Optional[Union[Mapping[str, Any], Iterable[str]]] = None, - sort: Optional[_IndexList] = None, + update: Mapping[str, Any] | _Pipeline, + projection: Mapping[str, Any] | Iterable[str] | None = None, + sort: _IndexList | None = None, upsert: bool = False, return_document: bool = ReturnDocument.BEFORE, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[AsyncClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + session: AsyncClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, - ) -> Optional[_DocumentType]: + ) -> _DocumentType | None: """Finds a single document and updates it, returning either the original or the updated document. diff --git a/pymongo/asynchronous/command_cursor.py b/pymongo/asynchronous/command_cursor.py index f2e86ddcfb..cedc4f8f2f 100644 --- a/pymongo/asynchronous/command_cursor.py +++ b/pymongo/asynchronous/command_cursor.py @@ -22,7 +22,6 @@ TYPE_CHECKING, Any, NoReturn, - Optional, ) from bson import CodecOptions, _convert_raw_document_lists_to_streams @@ -165,7 +164,7 @@ async def next(self) -> _DocumentType: async def __anext__(self) -> _DocumentType: return await self.next() - async def _try_next(self, get_more_allowed: bool) -> Optional[_DocumentType]: + async def _try_next(self, get_more_allowed: bool) -> _DocumentType | None: """Advance the cursor blocking for at most one getMore command.""" if not len(self._data) and not self._killed and get_more_allowed: await self._refresh() @@ -174,7 +173,7 @@ async def _try_next(self, get_more_allowed: bool) -> Optional[_DocumentType]: else: return None - async def _next_batch(self, result: list, total: Optional[int] = None) -> bool: # type: ignore[type-arg] + async def _next_batch(self, result: list, total: int | None = None) -> bool: # type: ignore[type-arg] """Get all or some available documents from the cursor.""" if not len(self._data) and not self._killed: await self._refresh() @@ -189,7 +188,7 @@ async def _next_batch(self, result: list, total: Optional[int] = None) -> bool: else: return False - async def try_next(self) -> Optional[_DocumentType]: + async def try_next(self) -> _DocumentType | None: """Advance the cursor without blocking indefinitely. This method returns the next document without waiting @@ -215,10 +214,10 @@ def __init__( self, collection: AsyncCollection[_DocumentType], cursor_info: Mapping[str, Any], - address: Optional[_Address], + address: _Address | None, batch_size: int = 0, - max_await_time_ms: Optional[int] = None, - session: Optional[AsyncClientSession] = None, + max_await_time_ms: int | None = None, + session: AsyncClientSession | None = None, comment: Any = None, ) -> None: """Create a new cursor / iterator over raw batches of BSON data. @@ -243,9 +242,9 @@ def __init__( def _unpack_response( # type: ignore[override] self, response: _OpMsg, - cursor_id: Optional[int], + cursor_id: int | None, codec_options: CodecOptions[dict[str, Any]], - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, legacy_response: bool = False, ) -> list[Mapping[str, Any]]: raw_response = response.raw_response(cursor_id, user_fields=user_fields) diff --git a/pymongo/asynchronous/command_runner.py b/pymongo/asynchronous/command_runner.py index 48801f2059..ff98897fc2 100644 --- a/pymongo/asynchronous/command_runner.py +++ b/pymongo/asynchronous/command_runner.py @@ -42,8 +42,6 @@ TYPE_CHECKING, Any, Callable, - Optional, - Union, cast, ) @@ -80,19 +78,19 @@ async def _run_command( request_id: int, msg: bytes, *, - client: Optional[AsyncMongoClient[Any]], - session: Optional[AsyncClientSession], - listeners: Optional[_EventListeners], - topology_id: Optional[ObjectId], + client: AsyncMongoClient[Any] | None, + session: AsyncClientSession | None, + listeners: _EventListeners | None, + topology_id: ObjectId | None, codec_options: CodecOptions[_DocumentType], - user_fields: Optional[Mapping[str, Any]] = None, - orig: Optional[MutableMapping[str, Any]] = None, - op_id: Optional[int] = None, - command_name: Optional[str] = None, + user_fields: Mapping[str, Any] | None = None, + orig: MutableMapping[str, Any] | None = None, + op_id: int | None = None, + command_name: str | None = None, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, + allowable_errors: Sequence[str | int] | None = None, parse_write_concern_error: bool = False, - pool_opts: Optional[PoolOptions] = None, + pool_opts: PoolOptions | None = None, unacknowledged: bool = False, speculative_hello: bool = False, ensure_db: bool = False, @@ -100,9 +98,9 @@ async def _run_command( max_doc_size: int = 0, more_to_come: bool = False, set_conn_more_to_come: bool = False, - unpack_res: Optional[Callable[..., Any]] = None, - cursor_id: Optional[int] = None, -) -> tuple[list[dict[str, Any]], Optional[_OpMsg], float]: + unpack_res: Callable[..., Any] | None = None, + cursor_id: int | None = None, +) -> tuple[list[dict[str, Any]], _OpMsg | None, float]: """Send ``msg`` over ``conn`` and return ``(docs, reply, duration_s)``, where ``duration_s`` is the round-trip duration in seconds. @@ -165,7 +163,7 @@ async def _run_command( # Fast path: skip telemetry construction when logging and APM are disabled # Inline enabled check here for performance - telemetry: Optional[_CommandTelemetry] = None + telemetry: _CommandTelemetry | None = None if (topology_id is not None and _is_debug_enabled(_COMMAND_LOGGER)) or ( listeners is not None and listeners.enabled_for_commands ): @@ -177,7 +175,7 @@ async def _run_command( else: start = time.monotonic() - reply: Optional[_OpMsg] = None + reply: _OpMsg | None = None docs: list[dict[str, Any]] = [{"ok": 1}] try: if more_to_come: @@ -248,11 +246,11 @@ async def run_bulk_write_command( request_id: int, msg: bytes, *, - client: Optional[AsyncMongoClient[Any]], - orig: Optional[MutableMapping[str, Any]] = None, + client: AsyncMongoClient[Any] | None, + orig: MutableMapping[str, Any] | None = None, max_doc_size: int = 0, unacknowledged: bool = False, -) -> tuple[list[dict[str, Any]], Optional[_OpMsg], float]: +) -> tuple[list[dict[str, Any]], _OpMsg | None, float]: """Send a bulk write batch and return ``(docs, reply, duration_s)``. :param bwc: Bulk write context supplying the connection, session, listeners, etc. @@ -293,18 +291,18 @@ async def run_cursor_command( request_id: int, msg: bytes, *, - client: Optional[AsyncMongoClient[Any]], - session: Optional[AsyncClientSession], - listeners: Optional[_EventListeners], + client: AsyncMongoClient[Any] | None, + session: AsyncClientSession | None, + listeners: _EventListeners | None, codec_options: CodecOptions[_DocumentType], command_name: str, - user_fields: Optional[Mapping[str, Any]] = None, - pool_opts: Optional[PoolOptions] = None, + user_fields: Mapping[str, Any] | None = None, + pool_opts: PoolOptions | None = None, max_doc_size: int = 0, more_to_come: bool = False, - unpack_res: Optional[Callable[..., Any]] = None, - cursor_id: Optional[int] = None, -) -> tuple[list[dict[str, Any]], Optional[_OpMsg], datetime.timedelta]: + unpack_res: Callable[..., Any] | None = None, + cursor_id: int | None = None, +) -> tuple[list[dict[str, Any]], _OpMsg | None, datetime.timedelta]: """Run a cursor ``find``/``getMore`` operation over ``conn``. :param conn: The AsyncConnection to send on. @@ -354,22 +352,22 @@ async def run_command( conn: AsyncConnection, dbname: str, spec: MutableMapping[str, Any], - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, codec_options: CodecOptions[_DocumentType], - session: Optional[AsyncClientSession], - client: Optional[AsyncMongoClient[Any]], + session: AsyncClientSession | None, + client: AsyncMongoClient[Any] | None, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - listeners: Optional[_EventListeners] = None, - max_bson_size: Optional[int] = None, - read_concern: Optional[ReadConcern] = None, + allowable_errors: Sequence[str | int] | None = None, + listeners: _EventListeners | None = None, + max_bson_size: int | None = None, + read_concern: ReadConcern | None = None, parse_write_concern_error: bool = False, - collation: Optional[_CollationIn] = None, - compression_ctx: Union[SnappyContext, ZlibContext, ZstdContext, None] = None, + collation: _CollationIn | None = None, + compression_ctx: SnappyContext | ZlibContext | ZstdContext | None = None, unacknowledged: bool = False, - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, exhaust_allowed: bool = False, - write_concern: Optional[WriteConcern] = None, + write_concern: WriteConcern | None = None, ) -> _DocumentType: """Encode and execute a command over ``conn``, or raise socket.error. diff --git a/pymongo/asynchronous/cursor.py b/pymongo/asynchronous/cursor.py index 14b4faed2c..34c0aa1305 100644 --- a/pymongo/asynchronous/cursor.py +++ b/pymongo/asynchronous/cursor.py @@ -22,8 +22,6 @@ TYPE_CHECKING, Any, NoReturn, - Optional, - Union, cast, overload, ) @@ -87,9 +85,7 @@ def __getitem__(self, index: int) -> _DocumentType: ... @overload def __getitem__(self, index: slice) -> AsyncCursor[_DocumentType]: ... - def __getitem__( - self, index: Union[int, slice] - ) -> Union[_DocumentType, AsyncCursor[_DocumentType]]: + def __getitem__(self, index: int | slice) -> _DocumentType | AsyncCursor[_DocumentType]: """Get a single document or a slice of documents from this cursor. .. warning:: A :class:`~AsyncCursor` is not a Python :class:`list`. Each @@ -221,7 +217,7 @@ async def distinct(self, key: str) -> list[Any]: return await self._collection.distinct(key, session=self._session, **options) - async def _send_message(self, operation: Union[_Query, _GetMore]) -> None: + async def _send_message(self, operation: _Query | _GetMore) -> None: """Send a query or getmore operation and handles the response. If operation is ``None`` this is an exhaust cursor, which reads @@ -389,7 +385,7 @@ async def next(self) -> _DocumentType: else: raise StopAsyncIteration - async def _next_batch(self, result: list, total: Optional[int] = None) -> bool: # type: ignore[type-arg] + async def _next_batch(self, result: list, total: int | None = None) -> bool: # type: ignore[type-arg] """Get all or some documents from the cursor.""" if self._empty: return False @@ -436,9 +432,9 @@ def __init__( def _unpack_response( self, response: _OpMsg, - cursor_id: Optional[int], + cursor_id: int | None, codec_options: CodecOptions[Mapping[str, Any]], - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, legacy_response: bool = False, ) -> list[_DocumentOut]: raw_response = response.raw_response(cursor_id, user_fields=user_fields) diff --git a/pymongo/asynchronous/cursor_base.py b/pymongo/asynchronous/cursor_base.py index 424a2bbd83..3d6c879660 100644 --- a/pymongo/asynchronous/cursor_base.py +++ b/pymongo/asynchronous/cursor_base.py @@ -18,7 +18,7 @@ from abc import abstractmethod from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any, Optional, Union, cast +from typing import TYPE_CHECKING, Any, cast from pymongo import _csot from pymongo.asynchronous.command_runner import run_cursor_command @@ -38,7 +38,7 @@ async def _operation_to_command( - operation: Union[_Query, _GetMore], + operation: _Query | _GetMore, conn: AsyncConnection, apply_timeout: bool, ) -> tuple[dict[str, Any], str]: @@ -55,7 +55,7 @@ class _ConnectionManager: """Used with exhaust cursors to ensure the connection is returned.""" def __init__(self, conn: AsyncConnection, more_to_come: bool): - self.conn: Optional[AsyncConnection] = conn + self.conn: AsyncConnection | None = conn self.more_to_come = more_to_come self._lock = _async_create_lock() @@ -73,24 +73,24 @@ class _AsyncCursorBase(_AgnosticCursorBase[_DocumentType]): """Asynchronous cursor base class.""" @property - def session(self) -> Optional[AsyncClientSession]: + def session(self) -> AsyncClientSession | None: """The cursor's :class:`~pymongo.asynchronous.client_session.AsyncClientSession`, or None. .. versionadded:: 3.6 """ - return cast("Optional[AsyncClientSession]", super().session) + return cast("AsyncClientSession | None", super().session) @abstractmethod - async def _next_batch(self, result: list, total: Optional[int] = None) -> bool: # type: ignore[type-arg] + async def _next_batch(self, result: list, total: int | None = None) -> bool: # type: ignore[type-arg] ... @abstractmethod def _unpack_response( self, response: _OpMsg, - cursor_id: Optional[int], + cursor_id: int | None, codec_options: Any, - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, legacy_response: bool = False, ) -> Sequence[_DocumentOut]: ... @@ -98,7 +98,7 @@ def _unpack_response( async def _run_with_conn( self, conn: AsyncConnection, - operation: Union[_Query, _GetMore], + operation: _Query | _GetMore, read_preference: _ServerMode, ) -> Response: """Execute a cursor operation on the given connection and return a Response. @@ -191,7 +191,7 @@ async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: await self.close() @_csot.apply - async def to_list(self, length: Optional[int] = None) -> list[_DocumentType]: + async def to_list(self, length: int | None = None) -> list[_DocumentType]: """Converts the contents of this cursor to a list more efficiently than ``[doc async for doc in cursor]``. To use:: diff --git a/pymongo/asynchronous/database.py b/pymongo/asynchronous/database.py index 88ca9cab5b..94d3f2193c 100644 --- a/pymongo/asynchronous/database.py +++ b/pymongo/asynchronous/database.py @@ -23,8 +23,6 @@ Any, Generic, NoReturn, - Optional, - Union, cast, overload, ) @@ -62,10 +60,10 @@ def __init__( self, client: AsyncMongoClient[_DocumentType], name: str, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> None: """Get a database by client and name. @@ -149,26 +147,26 @@ def name(self) -> str: def with_options( self, codec_options: None = None, - read_preference: Optional[_ServerMode] = ..., - write_concern: Optional[WriteConcern] = ..., - read_concern: Optional[ReadConcern] = ..., + read_preference: _ServerMode | None = ..., + write_concern: WriteConcern | None = ..., + read_concern: ReadConcern | None = ..., ) -> AsyncDatabase[_DocumentType]: ... @overload def with_options( self, codec_options: bson.CodecOptions[_DocumentTypeArg], - read_preference: Optional[_ServerMode] = ..., - write_concern: Optional[WriteConcern] = ..., - read_concern: Optional[ReadConcern] = ..., + read_preference: _ServerMode | None = ..., + write_concern: WriteConcern | None = ..., + read_concern: ReadConcern | None = ..., ) -> 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, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> AsyncDatabase[_DocumentType] | AsyncDatabase[_DocumentTypeArg]: """Get a clone of this database changing the specified settings. @@ -249,10 +247,10 @@ def __getitem__(self, name: str) -> AsyncCollection[_DocumentType]: def get_collection( self, name: str, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> AsyncCollection[_DocumentType]: """Get a :class:`~pymongo.asynchronous.collection.AsyncCollection` with the given name and options. @@ -302,7 +300,7 @@ def get_collection( async def _get_encrypted_fields( self, kwargs: Mapping[str, Any], coll_name: str, ask_db: bool - ) -> Optional[Mapping[str, Any]]: + ) -> Mapping[str, Any] | None: encrypted_fields = kwargs.get("encryptedFields") if encrypted_fields: return cast(Mapping[str, Any], deepcopy(encrypted_fields)) @@ -344,18 +342,18 @@ def __bool__(self) -> NoReturn: async def watch( self, - pipeline: Optional[_Pipeline] = None, - full_document: Optional[str] = None, - resume_after: Optional[Mapping[str, Any]] = None, - max_await_time_ms: Optional[int] = None, - batch_size: Optional[int] = None, - collation: Optional[_CollationIn] = None, - start_at_operation_time: Optional[Timestamp] = None, - session: Optional[AsyncClientSession] = None, - start_after: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, - full_document_before_change: Optional[str] = None, - show_expanded_events: Optional[bool] = None, + pipeline: _Pipeline | None = None, + full_document: str | None = None, + resume_after: Mapping[str, Any] | None = None, + max_await_time_ms: int | None = None, + batch_size: int | None = None, + collation: _CollationIn | None = None, + start_at_operation_time: Timestamp | None = None, + session: AsyncClientSession | None = None, + start_after: Mapping[str, Any] | None = None, + comment: Any | None = None, + full_document_before_change: str | None = None, + show_expanded_events: bool | None = None, ) -> AsyncDatabaseChangeStream[_DocumentType]: """Watch changes on this database. @@ -477,12 +475,12 @@ async def watch( async def create_collection( self, name: str, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, - session: Optional[AsyncClientSession] = None, - check_exists: Optional[bool] = True, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, + session: AsyncClientSession | None = None, + check_exists: bool | None = True, **kwargs: Any, ) -> AsyncCollection[_DocumentType]: """Create a new :class:`~pymongo.asynchronous.collection.AsyncCollection` in this @@ -634,7 +632,7 @@ async def create_collection( return coll async def aggregate( - self, pipeline: _Pipeline, session: Optional[AsyncClientSession] = None, **kwargs: Any + self, pipeline: _Pipeline, session: AsyncClientSession | None = None, **kwargs: Any ) -> AsyncCommandCursor[_DocumentType]: """Perform a database-level aggregation. @@ -720,15 +718,15 @@ async def aggregate( async def _command( self, conn: AsyncConnection, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: int = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, + allowable_errors: Sequence[str | int] | None = None, read_preference: _ServerMode = ReadPreference.PRIMARY, codec_options: CodecOptions[dict[str, Any]] = DEFAULT_CODEC_OPTIONS, - write_concern: Optional[WriteConcern] = None, + write_concern: WriteConcern | None = None, parse_write_concern_error: bool = False, - session: Optional[AsyncClientSession] = None, + session: AsyncClientSession | None = None, **kwargs: Any, ) -> dict[str, Any]: ... @@ -736,34 +734,33 @@ async def _command( async def _command( self, conn: AsyncConnection, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: int = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, + allowable_errors: Sequence[str | int] | None = None, read_preference: _ServerMode = ReadPreference.PRIMARY, codec_options: CodecOptions[_CodecDocumentType] = ..., - write_concern: Optional[WriteConcern] = None, + write_concern: WriteConcern | None = None, parse_write_concern_error: bool = False, - session: Optional[AsyncClientSession] = None, + session: AsyncClientSession | None = None, **kwargs: Any, ) -> _CodecDocumentType: ... async def _command( self, conn: AsyncConnection, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: int = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, + allowable_errors: Sequence[str | int] | None = None, read_preference: _ServerMode = ReadPreference.PRIMARY, - codec_options: Union[ - CodecOptions[dict[str, Any]], CodecOptions[_CodecDocumentType] - ] = DEFAULT_CODEC_OPTIONS, - write_concern: Optional[WriteConcern] = None, + codec_options: CodecOptions[dict[str, Any]] + | CodecOptions[_CodecDocumentType] = DEFAULT_CODEC_OPTIONS, + write_concern: WriteConcern | None = None, parse_write_concern_error: bool = False, - session: Optional[AsyncClientSession] = None, + session: AsyncClientSession | None = None, **kwargs: Any, - ) -> Union[dict[str, Any], _CodecDocumentType]: + ) -> dict[str, Any] | _CodecDocumentType: """Internal command helper.""" if isinstance(command, str): command = {command: value} @@ -786,44 +783,44 @@ async def _command( @overload async def command( self, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: Any = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - read_preference: Optional[_ServerMode] = None, + allowable_errors: Sequence[str | int] | None = None, + read_preference: _ServerMode | None = None, codec_options: None = None, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> dict[str, Any]: ... @overload async def command( self, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: Any = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - read_preference: Optional[_ServerMode] = None, + allowable_errors: Sequence[str | int] | None = None, + read_preference: _ServerMode | None = None, codec_options: CodecOptions[_CodecDocumentType] = ..., - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> _CodecDocumentType: ... @_csot.apply async def command( self, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: Any = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - read_preference: Optional[_ServerMode] = None, - codec_options: Optional[bson.codec_options.CodecOptions[_CodecDocumentType]] = None, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + allowable_errors: Sequence[str | int] | None = None, + read_preference: _ServerMode | None = None, + codec_options: bson.codec_options.CodecOptions[_CodecDocumentType] | None = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, - ) -> Union[dict[str, Any], _CodecDocumentType]: + ) -> dict[str, Any] | _CodecDocumentType: """Issue a MongoDB command. Send command `command` to the database and return the @@ -926,11 +923,11 @@ async def command( read_preference = (session and session._txn_read_preference()) or ReadPreference.PRIMARY async def inner( - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, _server: Server, conn: AsyncConnection, read_preference: _ServerMode, - ) -> Union[dict[str, Any], _CodecDocumentType]: + ) -> dict[str, Any] | _CodecDocumentType: return await self._command( conn, command, @@ -950,13 +947,13 @@ async def inner( @_csot.apply async def cursor_command( self, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: Any = 1, - read_preference: Optional[_ServerMode] = None, - codec_options: Optional[CodecOptions[_CodecDocumentType]] = None, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, - max_await_time_ms: Optional[int] = None, + read_preference: _ServerMode | None = None, + codec_options: CodecOptions[_CodecDocumentType] | None = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, + max_await_time_ms: int | None = None, **kwargs: Any, ) -> AsyncCommandCursor[_DocumentType]: """Issue a MongoDB command and parse the response as a cursor. @@ -1020,7 +1017,7 @@ async def cursor_command( ) or ReadPreference.PRIMARY async def inner( - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, _server: Server, conn: AsyncConnection, read_preference: _ServerMode, @@ -1057,15 +1054,15 @@ async def inner( async def _retryable_read_command( self, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], operation: str, - session: Optional[AsyncClientSession] = None, + session: AsyncClientSession | None = None, ) -> dict[str, Any]: """Same as command but used for retryable read commands.""" read_preference = (session and session._txn_read_preference()) or ReadPreference.PRIMARY async def _cmd( - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, _server: Server, conn: AsyncConnection, read_preference: _ServerMode, @@ -1082,7 +1079,7 @@ async def _cmd( async def _list_collections( self, conn: AsyncConnection, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, read_preference: _ServerMode, **kwargs: Any, ) -> AsyncCommandCursor[MutableMapping[str, Any]]: @@ -1109,9 +1106,9 @@ async def _list_collections( async def _list_collections_helper( self, - session: Optional[AsyncClientSession] = None, - filter: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + filter: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> AsyncCommandCursor[MutableMapping[str, Any]]: """Get a cursor over the collections of this database. @@ -1140,7 +1137,7 @@ async def _list_collections_helper( kwargs["comment"] = comment async def _cmd( - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, _server: Server, conn: AsyncConnection, read_preference: _ServerMode, @@ -1155,9 +1152,9 @@ async def _cmd( async def list_collections( self, - session: Optional[AsyncClientSession] = None, - filter: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + filter: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> AsyncCommandCursor[MutableMapping[str, Any]]: """Get a cursor over the collections of this database. @@ -1192,9 +1189,9 @@ async def list_collections( async def _list_collection_names( self, - session: Optional[AsyncClientSession] = None, - filter: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + filter: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> list[str]: if comment is not None: @@ -1217,9 +1214,9 @@ async def _list_collection_names( async def list_collection_names( self, - session: Optional[AsyncClientSession] = None, - filter: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + filter: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> list[str]: """Get a list of all the collection names in this database. @@ -1250,14 +1247,14 @@ async def list_collection_names( return await self._list_collection_names(session, filter, comment, **kwargs) async def _drop_helper( - self, name: str, session: Optional[AsyncClientSession] = None, comment: Optional[Any] = None + self, name: str, session: AsyncClientSession | None = None, comment: Any | None = None ) -> dict[str, Any]: command = {"drop": name} if comment is not None: command["comment"] = comment async def inner( - session: Optional[AsyncClientSession], conn: AsyncConnection, _retryable_write: bool + session: AsyncClientSession | None, conn: AsyncConnection, _retryable_write: bool ) -> dict[str, Any]: return await self._command( conn, @@ -1273,10 +1270,10 @@ async def inner( @_csot.apply async def drop_collection( self, - name_or_collection: Union[str, AsyncCollection[_DocumentTypeArg]], - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, - encrypted_fields: Optional[Mapping[str, Any]] = None, + name_or_collection: str | AsyncCollection[_DocumentTypeArg], + session: AsyncClientSession | None = None, + comment: Any | None = None, + encrypted_fields: Mapping[str, Any] | None = None, ) -> dict[str, Any]: """Drop a collection. @@ -1350,12 +1347,12 @@ async def drop_collection( async def validate_collection( self, - name_or_collection: Union[str, AsyncCollection[_DocumentTypeArg]], + name_or_collection: str | AsyncCollection[_DocumentTypeArg], scandata: bool = False, full: bool = False, - session: Optional[AsyncClientSession] = None, - background: Optional[bool] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + background: bool | None = None, + comment: Any | None = None, ) -> dict[str, Any]: """Validate a collection. @@ -1435,10 +1432,10 @@ async def validate_collection( async def dereference( self, dbref: DBRef, - session: Optional[AsyncClientSession] = None, - comment: Optional[Any] = None, + session: AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, - ) -> Optional[_DocumentType]: + ) -> _DocumentType | None: """Dereference a :class:`~bson.dbref.DBRef`, getting the document it points to. diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index e4f91294a4..d0e9f17b25 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -28,8 +28,6 @@ TYPE_CHECKING, Any, Generic, - Optional, - Union, cast, ) @@ -119,9 +117,9 @@ class _EncryptionIO(AsyncMongoCryptCallback): # type: ignore[misc] def __init__( self, - client: Optional[AsyncMongoClient[_DocumentTypeArg]], + client: AsyncMongoClient[_DocumentTypeArg] | None, key_vault_coll: AsyncCollection[_DocumentTypeArg], - mongocryptd_client: Optional[AsyncMongoClient[_DocumentTypeArg]], + mongocryptd_client: AsyncMongoClient[_DocumentTypeArg] | None, opts: AutoEncryptionOpts, ): """Internal class to perform I/O on behalf of pymongocrypt.""" @@ -131,7 +129,7 @@ def __init__( self.client_ref = weakref.ref(client) else: self.client_ref = None - self.key_vault_coll: Optional[AsyncCollection[RawBSONDocument]] = cast( + self.key_vault_coll: AsyncCollection[RawBSONDocument] | None = cast( AsyncCollection[RawBSONDocument], key_vault_coll.with_options( codec_options=_KEY_VAULT_OPTS, @@ -240,7 +238,7 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: ) raise exc from final_err - async def collection_info(self, database: str, filter: bytes) -> Optional[list[bytes]]: + async def collection_info(self, database: str, filter: bytes) -> list[bytes] | None: """Get the collection info for a namespace. The returned collection info is passed to libmongocrypt which reads @@ -445,7 +443,7 @@ async def encrypt( # TODO: PYTHON-1922 avoid decoding the encrypted_cmd. return _inflate_bson(encrypted_cmd, DEFAULT_RAW_BSON_OPTIONS) - async def decrypt(self, response: bytes | memoryview) -> Optional[bytes]: + async def decrypt(self, response: bytes | memoryview) -> bytes | None: """Decrypt a MongoDB command response. :param response: A MongoDB command response as BSON. @@ -488,8 +486,8 @@ def _string_opts_kwarg() -> str: def _resolve_string_opts( - string_opts: Optional[StringOpts], text_opts: Optional[StringOpts] -) -> Optional[StringOpts]: + string_opts: StringOpts | None, text_opts: StringOpts | None +) -> StringOpts | None: """Resolve string_opts and its former name, text_opts. pymongocrypt accepts only one of the two names per release, so passing the @@ -521,9 +519,9 @@ def __init__( key_vault_namespace: str, key_vault_client: AsyncMongoClient[_DocumentTypeArg], codec_options: CodecOptions[_DocumentTypeArg], - kms_tls_options: Optional[Mapping[str, Any]] = None, - key_expiration_ms: Optional[int] = None, - kms_connect_callback: Optional[AsyncKMSConnectCallback] = None, + kms_tls_options: Mapping[str, Any] | None = None, + key_expiration_ms: int | None = None, + kms_connect_callback: AsyncKMSConnectCallback | None = None, ) -> None: """Explicit client-side field level encryption. @@ -655,9 +653,7 @@ def __init__( kms_connect_callback=kms_connect_callback, ) self._kms_ssl_contexts = _parse_kms_tls_options(opts._kms_tls_options, _IS_SYNC) - self._io_callbacks: Optional[_EncryptionIO] = _EncryptionIO( - None, key_vault_coll, None, opts - ) + self._io_callbacks: _EncryptionIO | None = _EncryptionIO(None, key_vault_coll, None, opts) self._encryption = AsyncExplicitEncrypter( self._io_callbacks, _create_mongocrypt_options( @@ -676,8 +672,8 @@ async def create_encrypted_collection( database: AsyncDatabase[_DocumentTypeArg], name: str, encrypted_fields: Mapping[str, Any], - kms_provider: Optional[str] = None, - master_key: Optional[Mapping[str, Any]] = None, + kms_provider: str | None = None, + master_key: Mapping[str, Any] | None = None, **kwargs: Any, ) -> tuple[AsyncCollection[_DocumentTypeArg], Mapping[str, Any]]: """Create a collection with encryptedFields. @@ -763,9 +759,9 @@ async def create_encrypted_collection( async def create_data_key( self, kms_provider: str, - master_key: Optional[Mapping[str, Any]] = None, - key_alt_names: Optional[Sequence[str]] = None, - key_material: Optional[bytes] = None, + master_key: Mapping[str, Any] | None = None, + key_alt_names: Sequence[str] | None = None, + key_material: bytes | None = None, ) -> Binary: """Create and insert a new data key into the key vault collection. @@ -855,13 +851,13 @@ async def _encrypt_helper( self, value: Any, algorithm: str, - key_id: Optional[Union[Binary, uuid.UUID]] = None, - key_alt_name: Optional[str] = None, - query_type: Optional[str] = None, - contention_factor: Optional[int] = None, - range_opts: Optional[RangeOpts] = None, + key_id: Binary | uuid.UUID | None = None, + key_alt_name: str | None = None, + query_type: str | None = None, + contention_factor: int | None = None, + range_opts: RangeOpts | None = None, is_expression: bool = False, - string_opts: Optional[StringOpts] = None, + string_opts: StringOpts | None = None, ) -> Any: self._check_closed() if isinstance(key_id, uuid.UUID): @@ -905,13 +901,13 @@ async def encrypt( self, value: Any, algorithm: str, - key_id: Optional[Union[Binary, uuid.UUID]] = None, - key_alt_name: Optional[str] = None, - query_type: Optional[str] = None, - contention_factor: Optional[int] = None, - range_opts: Optional[RangeOpts] = None, - string_opts: Optional[StringOpts] = None, - text_opts: Optional[StringOpts] = None, + key_id: Binary | uuid.UUID | None = None, + key_alt_name: str | None = None, + query_type: str | None = None, + contention_factor: int | None = None, + range_opts: RangeOpts | None = None, + string_opts: StringOpts | None = None, + text_opts: StringOpts | None = None, ) -> Binary: """Encrypt a BSON value with a given key and algorithm. @@ -976,11 +972,11 @@ async def encrypt_expression( self, expression: Mapping[str, Any], algorithm: str, - key_id: Optional[Union[Binary, uuid.UUID]] = None, - key_alt_name: Optional[str] = None, - query_type: Optional[str] = None, - contention_factor: Optional[int] = None, - range_opts: Optional[RangeOpts] = None, + key_id: Binary | uuid.UUID | None = None, + key_alt_name: str | None = None, + query_type: str | None = None, + contention_factor: int | None = None, + range_opts: RangeOpts | None = None, ) -> RawBSONDocument: """Encrypt a BSON expression with a given key and algorithm. @@ -1044,7 +1040,7 @@ async def decrypt(self, value: Binary) -> Any: decrypted_doc = await self._encryption.decrypt(doc) return decode(decrypted_doc, codec_options=self._codec_options)["v"] - async def get_key(self, id: Binary) -> Optional[RawBSONDocument]: + async def get_key(self, id: Binary) -> RawBSONDocument | None: """Get a data key by id. :param id` (Binary): The UUID of a key a which must be a @@ -1103,7 +1099,7 @@ async def add_key_alt_name(self, id: Binary, key_alt_name: str) -> Any: assert self._key_vault_coll is not None return await self._key_vault_coll.find_one_and_update({"_id": id}, update) - async def get_key_by_alt_name(self, key_alt_name: str) -> Optional[RawBSONDocument]: + async def get_key_by_alt_name(self, key_alt_name: str) -> RawBSONDocument | None: """Get a key document in the key vault collection that has the given ``key_alt_name``. :param key_alt_name: (str): The key alternate name of the key to get. @@ -1116,7 +1112,7 @@ async def get_key_by_alt_name(self, key_alt_name: str) -> Optional[RawBSONDocume assert self._key_vault_coll is not None return await self._key_vault_coll.find_one({"keyAltNames": key_alt_name}) - async def remove_key_alt_name(self, id: Binary, key_alt_name: str) -> Optional[RawBSONDocument]: + async def remove_key_alt_name(self, id: Binary, key_alt_name: str) -> RawBSONDocument | None: """Remove ``key_alt_name`` from the set of keyAltNames in the key document with UUID ``id``. Also removes the ``keyAltNames`` field from the key document if it would otherwise be empty. @@ -1155,8 +1151,8 @@ async def remove_key_alt_name(self, id: Binary, key_alt_name: str) -> Optional[R async def rewrap_many_data_key( self, filter: Mapping[str, Any], - provider: Optional[str] = None, - master_key: Optional[Mapping[str, Any]] = None, + provider: str | None = None, + master_key: Mapping[str, Any] | None = None, ) -> RewrapManyDataKeyResult: """Decrypts and encrypts all matching data keys in the key vault with a possibly new `master_key` value. diff --git a/pymongo/asynchronous/mongo_client.py b/pymongo/asynchronous/mongo_client.py index fcfe94fd2f..308370e095 100644 --- a/pymongo/asynchronous/mongo_client.py +++ b/pymongo/asynchronous/mongo_client.py @@ -49,7 +49,6 @@ NoReturn, Optional, TypeVar, - Union, cast, ) @@ -154,14 +153,14 @@ _IS_SYNC = False -_WriteOp = Union[ - InsertOne, # type: ignore[type-arg] - DeleteOne, - DeleteMany, - ReplaceOne, # type: ignore[type-arg] - UpdateOne, - UpdateMany, -] +_WriteOp = ( + InsertOne # type: ignore[type-arg] + | DeleteOne + | DeleteMany + | ReplaceOne # type: ignore[type-arg] + | UpdateOne + | UpdateMany +) class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]): @@ -174,12 +173,12 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]): def __init__( self, - host: Optional[Union[str, Sequence[str]]] = None, - port: Optional[int] = None, - document_class: Optional[type[_DocumentType]] = None, - tz_aware: Optional[bool] = None, - connect: Optional[bool] = None, - type_registry: Optional[TypeRegistry] = None, + host: str | Sequence[str] | None = None, + port: int | None = None, + document_class: type[_DocumentType] | None = None, + tz_aware: bool | None = None, + connect: bool | None = None, + type_registry: TypeRegistry | None = None, **kwargs: Any, ) -> None: """Client for a MongoDB instance, a replica set, or a set of mongoses. @@ -920,7 +919,7 @@ def validator(host: str) -> bool: self._lock = _async_create_lock() self._kill_cursors_queue: list = [] # type: ignore[type-arg] - self._encrypter: Optional[_Encrypter] = None + self._encrypter: _Encrypter | None = None self._resolve_srv_info.update( { @@ -958,7 +957,7 @@ def validator(host: str) -> bool: self._opened = False self._closed = False - self._loop: Optional[asyncio.AbstractEventLoop] = None + self._loop: asyncio.AbstractEventLoop | None = None if not is_srv: self._init_background() @@ -1121,7 +1120,7 @@ async def aconnect(self) -> None: """Explicitly connect to MongoDB asynchronously instead of on the first operation.""" await self._get_topology() - def _init_background(self, old_pid: Optional[int] = None) -> None: + def _init_background(self, old_pid: int | None = None) -> None: self._topology = Topology(self._topology_settings) if _HAS_REGISTER_AT_FORK: # Add this client to the list of weakly referenced items. @@ -1165,7 +1164,7 @@ def append_metadata(self, driver_info: DriverInfo) -> None: ) self._options.pool_options._update_metadata(driver_info) - def _should_pin_cursor(self, session: Optional[AsyncClientSession]) -> Optional[bool]: + def _should_pin_cursor(self, session: AsyncClientSession | None) -> bool | None: return self._options.load_balanced and not (session and session.in_transaction) def _after_fork(self) -> None: @@ -1181,18 +1180,18 @@ def _duplicate(self, **kwargs: Any) -> AsyncMongoClient: # type: ignore[type-ar async def watch( self, - pipeline: Optional[_Pipeline] = None, - full_document: Optional[str] = None, - resume_after: Optional[Mapping[str, Any]] = None, - max_await_time_ms: Optional[int] = None, - batch_size: Optional[int] = None, - collation: Optional[_CollationIn] = None, - start_at_operation_time: Optional[Timestamp] = None, - session: Optional[client_session.AsyncClientSession] = None, - start_after: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, - full_document_before_change: Optional[str] = None, - show_expanded_events: Optional[bool] = None, + pipeline: _Pipeline | None = None, + full_document: str | None = None, + resume_after: Mapping[str, Any] | None = None, + max_await_time_ms: int | None = None, + batch_size: int | None = None, + collation: _CollationIn | None = None, + start_at_operation_time: Timestamp | None = None, + session: client_session.AsyncClientSession | None = None, + start_after: Mapping[str, Any] | None = None, + comment: Any | None = None, + full_document_before_change: str | None = None, + show_expanded_events: bool | None = None, ) -> AsyncChangeStream[_DocumentType]: """Watch changes on this cluster. @@ -1342,7 +1341,7 @@ def topology_description(self) -> TopologyDescription: return self._topology.description @property - def _topology_id(self) -> Optional[ObjectId]: + def _topology_id(self) -> ObjectId | None: return self._topology_settings._topology_id @property @@ -1371,7 +1370,7 @@ def options(self) -> ClientOptions: """ return self._options - def eq_props(self) -> tuple[tuple[_Address, ...], Optional[str], Optional[str], str]: + def eq_props(self) -> tuple[tuple[_Address, ...], str | None, str | None, str]: return ( tuple(sorted(self._resolve_srv_info["seeds"])), self._options.replica_set_name, @@ -1476,8 +1475,8 @@ def __del__(self) -> None: def _close_cursor_soon( self, cursor_id: int, - address: Optional[_CursorAddress], - conn_mgr: Optional[_ConnectionManager] = None, + address: _CursorAddress | None, + conn_mgr: _ConnectionManager | None = None, ) -> None: """Request that a cursor and/or connection be cleaned up soon.""" self._kill_cursors_queue.append((address, cursor_id, conn_mgr)) @@ -1489,9 +1488,9 @@ def _start_session(self, implicit: bool, **kwargs: Any) -> AsyncClientSession: def start_session( self, - causal_consistency: Optional[bool] = None, - default_transaction_options: Optional[TransactionOptions] = None, - snapshot: Optional[bool] = False, + causal_consistency: bool | None = None, + default_transaction_options: TransactionOptions | None = None, + snapshot: bool | None = False, ) -> client_session.AsyncClientSession: """Start a logical session. @@ -1517,8 +1516,8 @@ def start_session( ) def _ensure_session( - self, session: Optional[AsyncClientSession] = None - ) -> Optional[AsyncClientSession]: + self, session: AsyncClientSession | None = None + ) -> AsyncClientSession | None: """If provided session and bound session are None, lend a temporary session.""" session = session or self._get_bound_session() if session: @@ -1533,13 +1532,13 @@ def _ensure_session( return None def _send_cluster_time( - self, command: MutableMapping[str, Any], session: Optional[AsyncClientSession] + self, command: MutableMapping[str, Any], session: AsyncClientSession | None ) -> None: topology_time = self._topology.max_cluster_time() session_time = session.cluster_time if session else None if topology_time and session_time: if topology_time["clusterTime"] > session_time["clusterTime"]: - cluster_time: Optional[ClusterTime] = topology_time + cluster_time: ClusterTime | None = topology_time else: cluster_time = session_time else: @@ -1549,11 +1548,11 @@ def _send_cluster_time( def get_default_database( self, - default: Optional[str] = None, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, + default: str | None = None, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> database.AsyncDatabase[_DocumentType]: """Get the database named in the MongoDB connection URI. @@ -1609,11 +1608,11 @@ def get_default_database( def get_database( self, - name: Optional[str] = None, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, + name: str | None = None, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> database.AsyncDatabase[_DocumentType]: """Get a :class:`~pymongo.asynchronous.database.AsyncDatabase` with the given name and options. @@ -1707,7 +1706,7 @@ async def _server_property(self, attr_name: str) -> Any: return getattr(server.description, attr_name) @property - async def address(self) -> Optional[tuple[str, int]]: + async def address(self) -> tuple[str, int] | None: """(host, port) of the current standalone, primary, or mongos, or None. Accessing :attr:`address` raises :exc:`~.errors.InvalidOperation` if @@ -1734,7 +1733,7 @@ async def address(self) -> Optional[tuple[str, int]]: return await self._server_property("address") @property - async def primary(self) -> Optional[tuple[str, int]]: + async def primary(self) -> tuple[str, int] | None: """The (host, port) of the current primary of the replica set. Returns ``None`` if this client is not connected to a replica set, @@ -1879,17 +1878,17 @@ async def _get_topology(self) -> Topology: self._opened = True return self._topology - def _checkout(self, server: Server, session: Optional[AsyncClientSession]) -> _ClientCheckout: + def _checkout(self, server: Server, session: AsyncClientSession | None) -> _ClientCheckout: return _ClientCheckout(self, server, session) async def _select_server( self, server_selector: Callable[[Selection], Selection], - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, operation: str, - address: Optional[_Address] = None, - deprioritized_servers: Optional[list[Server]] = None, - operation_id: Optional[int] = None, + address: _Address | None = None, + deprioritized_servers: list[Server] | None = None, + operation_id: int | None = None, ) -> Server: """Select a server to run an operation on this client. @@ -1932,13 +1931,13 @@ async def _select_server( raise async def _conn_for_writes( - self, session: Optional[AsyncClientSession], operation: str + self, session: AsyncClientSession | None, operation: str ) -> _ClientCheckout: server = await self._select_server(writable_server_selector, session, operation) return self._checkout(server, session) def _conn_from_server( - self, read_preference: _ServerMode, server: Server, session: Optional[AsyncClientSession] + self, read_preference: _ServerMode, server: Server, session: AsyncClientSession | None ) -> _ClientReadCheckout: assert read_preference is not None, "read_preference must not be None" return _ClientReadCheckout(self, server, session, read_preference) @@ -1946,7 +1945,7 @@ def _conn_from_server( async def _conn_for_reads( self, read_preference: _ServerMode, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, operation: str, ) -> _ClientReadCheckout: assert read_preference is not None, "read_preference must not be None" @@ -1956,9 +1955,9 @@ async def _conn_for_reads( @_csot.apply async def _run_operation( self, - operation: Union[_Query, _GetMore], + operation: _Query | _GetMore, run_with_conn: Callable, # type: ignore[type-arg] - address: Optional[_Address] = None, + address: _Address | None = None, ) -> Response: """Run a _Query/_GetMore operation and return a Response. @@ -1988,7 +1987,7 @@ async def _run_operation( ) async def _cmd( - _session: Optional[AsyncClientSession], + _session: AsyncClientSession | None, _server: Server, conn: AsyncConnection, read_preference: _ServerMode, @@ -2009,10 +2008,10 @@ async def _retry_with_session( self, retryable: bool, func: _WriteCall[T], - session: Optional[AsyncClientSession], - bulk: Optional[Union[_AsyncBulk, _AsyncClientBulk]], + session: AsyncClientSession | None, + bulk: _AsyncBulk | _AsyncClientBulk | None, operation: str, - operation_id: Optional[int] = None, + operation_id: int | None = None, ) -> T: """Execute an operation with at most one consecutive retries @@ -2039,14 +2038,14 @@ async def _retry_with_session( async def _retry_internal( self, func: _WriteCall[T] | _ReadCall[T], - session: Optional[AsyncClientSession], - bulk: Optional[Union[_AsyncBulk, _AsyncClientBulk]], + session: AsyncClientSession | None, + bulk: _AsyncBulk | _AsyncClientBulk | None, operation: str, is_read: bool = False, - address: Optional[_Address] = None, - read_pref: Optional[_ServerMode] = None, + address: _Address | None = None, + read_pref: _ServerMode | None = None, retryable: bool = False, - operation_id: Optional[int] = None, + operation_id: int | None = None, is_run_command: bool = False, is_aggregate_write: bool = False, ) -> T: @@ -2085,11 +2084,11 @@ async def _retryable_read( self, func: _ReadCall[T], read_pref: _ServerMode, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, operation: str, - address: Optional[_Address] = None, + address: _Address | None = None, retryable: bool = True, - operation_id: Optional[int] = None, + operation_id: int | None = None, is_run_command: bool = False, is_aggregate_write: bool = False, ) -> T: @@ -2136,10 +2135,10 @@ async def _retryable_write( self, retryable: bool, func: _WriteCall[T], - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, operation: str, - bulk: Optional[Union[_AsyncBulk, _AsyncClientBulk]] = None, - operation_id: Optional[int] = None, + bulk: _AsyncBulk | _AsyncClientBulk | None = None, + operation_id: int | None = None, ) -> T: """Execute an operation with consecutive retries if possible @@ -2161,9 +2160,9 @@ async def _retryable_write( def _cleanup_cursor_no_lock( self, cursor_id: int, - address: Optional[_CursorAddress], + address: _CursorAddress | None, conn_mgr: _ConnectionManager, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, ) -> None: """Cleanup a cursor from __del__ without locking. @@ -2184,9 +2183,9 @@ def _cleanup_cursor_no_lock( async def _cleanup_cursor_lock( self, cursor_id: int, - address: Optional[_CursorAddress], + address: _CursorAddress | None, conn_mgr: _ConnectionManager, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, ) -> None: """Cleanup a cursor from cursor.close() using a lock. @@ -2216,9 +2215,9 @@ async def _cleanup_cursor_lock( async def _close_cursor_now( self, cursor_id: int, - address: Optional[_CursorAddress], - session: Optional[AsyncClientSession] = None, - conn_mgr: Optional[_ConnectionManager] = None, + address: _CursorAddress | None, + session: AsyncClientSession | None = None, + conn_mgr: _ConnectionManager | None = None, ) -> None: """Send a kill cursors message with the given id. @@ -2243,9 +2242,9 @@ async def _close_cursor_now( async def _kill_cursors( self, cursor_ids: Sequence[int], - address: Optional[_CursorAddress], + address: _CursorAddress | None, topology: Topology, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, ) -> None: """Send a kill cursors message with the given ids.""" if address: @@ -2264,7 +2263,7 @@ async def _kill_cursor_impl( self, cursor_ids: Sequence[int], address: _CursorAddress, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, conn: AsyncConnection, ) -> None: namespace = address.namespace @@ -2326,9 +2325,7 @@ async def _process_periodic_tasks(self) -> None: else: _log_client_error() - def _return_server_session( - self, server_session: Union[_ServerSession, _EmptyServerSession] - ) -> None: + def _return_server_session(self, server_session: _ServerSession | _EmptyServerSession) -> None: """Internal: return a _ServerSession to the pool.""" if isinstance(server_session, _EmptyServerSession): return None @@ -2336,8 +2333,8 @@ def _return_server_session( @contextlib.asynccontextmanager async def _tmp_session( - self, session: Optional[client_session.AsyncClientSession] - ) -> AsyncGenerator[Optional[client_session.AsyncClientSession], None]: + self, session: client_session.AsyncClientSession | None + ) -> AsyncGenerator[client_session.AsyncClientSession | None, None]: """If provided session is None, lend a temporary session.""" if session is not None and not isinstance(session, client_session.AsyncClientSession): raise ValueError( @@ -2370,13 +2367,13 @@ async def _tmp_session( yield None async def _process_response( - self, reply: Mapping[str, Any], session: Optional[AsyncClientSession] + self, reply: Mapping[str, Any], session: AsyncClientSession | None ) -> None: await self._topology.receive_cluster_time(reply.get("$clusterTime")) if session is not None: session._process_response(reply) - def _get_bound_session(self) -> Optional[AsyncClientSession]: + def _get_bound_session(self) -> AsyncClientSession | None: bound_session = _SESSION.get() if bound_session: if bound_session.client is self: @@ -2389,7 +2386,7 @@ def _get_bound_session(self) -> Optional[AsyncClientSession]: return None async def server_info( - self, session: Optional[client_session.AsyncClientSession] = None + self, session: client_session.AsyncClientSession | None = None ) -> dict[str, Any]: """Get information about the MongoDB server we're connected to. @@ -2408,8 +2405,8 @@ async def server_info( async def _list_databases( self, - session: Optional[client_session.AsyncClientSession] = None, - comment: Optional[Any] = None, + session: client_session.AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> AsyncCommandCursor[dict[str, Any]]: cmd = {"listDatabases": 1} @@ -2430,8 +2427,8 @@ async def _list_databases( async def list_databases( self, - session: Optional[client_session.AsyncClientSession] = None, - comment: Optional[Any] = None, + session: client_session.AsyncClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> AsyncCommandCursor[dict[str, Any]]: """Get a cursor over the databases of the connected server. @@ -2464,8 +2461,8 @@ async def list_databases( async def list_database_names( self, - session: Optional[client_session.AsyncClientSession] = None, - comment: Optional[Any] = None, + session: client_session.AsyncClientSession | None = None, + comment: Any | None = None, ) -> list[str]: """Get a list of the names of all databases on the connected server. @@ -2485,9 +2482,9 @@ async def list_database_names( @_csot.apply async def drop_database( self, - name_or_database: Union[str, database.AsyncDatabase[_DocumentTypeArg]], - session: Optional[client_session.AsyncClientSession] = None, - comment: Optional[Any] = None, + name_or_database: str | database.AsyncDatabase[_DocumentTypeArg], + session: client_session.AsyncClientSession | None = None, + comment: Any | None = None, ) -> None: """Drop a database. @@ -2537,13 +2534,13 @@ async def drop_database( async def bulk_write( self, models: Sequence[_WriteOp], - session: Optional[AsyncClientSession] = None, + session: AsyncClientSession | None = None, ordered: bool = True, verbose_results: bool = False, - bypass_document_validation: Optional[bool] = None, - comment: Optional[Any] = None, - let: Optional[Mapping[str, Any]] = None, - write_concern: Optional[WriteConcern] = None, + bypass_document_validation: bool | None = None, + comment: Any | None = None, + let: Mapping[str, Any] | None = None, + write_concern: WriteConcern | None = None, ) -> ClientBulkWriteResult: """Send a batch of write operations, potentially across multiple namespaces, to the server. @@ -2691,7 +2688,7 @@ def __init__( self, client: AsyncMongoClient, # type: ignore[type-arg] server: Server, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, ): if not isinstance(client, AsyncMongoClient): # This is for compatibility with mocked and subclassed types, such as in Motor. @@ -2708,10 +2705,10 @@ def __init__( # of the pool at the time the connection attempt was started." self.sock_generation = server.pool.gen.get_overall() self.completed_handshake = False - self.service_id: Optional[ObjectId] = None + self.service_id: ObjectId | None = None self.handled = False - self._existing_conn: Optional[AsyncConnection] = None - self._pool_checkout: Optional[_PoolCheckout] = None + self._existing_conn: AsyncConnection | None = None + self._pool_checkout: _PoolCheckout | None = None self._server = server def contribute_socket(self, conn: AsyncConnection, completed_handshake: bool = True) -> None: @@ -2722,7 +2719,7 @@ def contribute_socket(self, conn: AsyncConnection, completed_handshake: bool = T self.completed_handshake = completed_handshake async def handle( - self, exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException] + self, exc_type: type[BaseException] | None, exc_val: BaseException | None ) -> None: if self.handled or exc_val is None: return @@ -2799,9 +2796,9 @@ async def __aenter__(self) -> AsyncConnection: async def __aexit__( self, - exc_type: Optional[type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: # Perform SDAM error handling while the connection is still checked out. try: @@ -2817,7 +2814,7 @@ def for_existing_conn( cls, client: AsyncMongoClient, # type: ignore[type-arg] server: Server, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, conn: AsyncConnection, ) -> _ClientCheckout: """Return a _ClientCheckout for an already-checked-out connection. @@ -2844,7 +2841,7 @@ def __init__( self, client: AsyncMongoClient, # type: ignore[type-arg] server: Server, - session: Optional[AsyncClientSession], + session: AsyncClientSession | None, read_preference: _ServerMode, ) -> None: super().__init__(client, server, session) @@ -2874,7 +2871,7 @@ class _ClientConnectionRetryable( __slots__ = () _client: AsyncMongoClient[Any] - _session: Optional[AsyncClientSession] + _session: AsyncClientSession | None async def run(self) -> T: """Runs the supplied func() and attempts a retry diff --git a/pymongo/asynchronous/monitor.py b/pymongo/asynchronous/monitor.py index 6885fa090a..245d5de131 100644 --- a/pymongo/asynchronous/monitor.py +++ b/pymongo/asynchronous/monitor.py @@ -21,7 +21,7 @@ import threading import time import weakref -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from pymongo import common, periodic_executor from pymongo._csot import MovingMinimum @@ -77,7 +77,7 @@ async def target() -> bool: self._executor = executor - def _on_topology_gc(dummy: Optional[Topology] = None) -> None: + def _on_topology_gc(dummy: Topology | None = None) -> None: # This prevents GC from waiting 10 seconds for hello to complete # See test_cleanup_executors_on_client_del. monitor = self_ref() @@ -142,9 +142,9 @@ def __init__( self._pool = pool self._settings = topology_settings self._listeners = self._settings._pool_options._event_listeners - self._cancel_context: Optional[_CancellationContext] = None - self._conn_id: Optional[int] = None - self._current_hb: Optional[_HeartbeatTelemetry] = None + self._cancel_context: _CancellationContext | None = None + self._conn_id: int | None = None + self._current_hb: _HeartbeatTelemetry | None = None self._awaited: bool = False self._rtt_monitor = _RttMonitor( topology, @@ -359,7 +359,7 @@ async def _run(self) -> None: # Topology was garbage-collected. await self.close() - async def _get_seedlist(self) -> Optional[list[tuple[str, Any]]]: + async def _get_seedlist(self) -> list[tuple[str, Any]] | None: """Poll SRV records for a seedlist. Returns a list of ServerDescriptions. @@ -419,7 +419,7 @@ async def add_sample(self, sample: float) -> None: self._moving_average.add_sample(sample) self._moving_min.add_sample(sample) - async def get(self) -> tuple[Optional[float], float]: + async def get(self) -> tuple[float | None, float]: """Get the calculated average, or None if no samples yet and the min.""" async with self._lock: return self._moving_average.get(), self._moving_min.get() diff --git a/pymongo/asynchronous/pool.py b/pymongo/asynchronous/pool.py index ee80a7b88a..2f17aa2b2b 100644 --- a/pymongo/asynchronous/pool.py +++ b/pymongo/asynchronous/pool.py @@ -23,8 +23,6 @@ TYPE_CHECKING, Any, NoReturn, - Optional, - Union, ) from bson import DEFAULT_CODEC_OPTIONS @@ -126,8 +124,8 @@ async def hello(self) -> Hello[dict[str, Any]]: async def _hello( self, - topology_version: Optional[Any], - heartbeat_frequency: Optional[int], + topology_version: Any | None, + heartbeat_frequency: int | None, ) -> Hello[dict[str, Any]]: cmd = self.hello_cmd() performing_handshake = not self.performed_handshake @@ -179,7 +177,7 @@ async def _hello( self.supports_sessions = ( hello.logical_session_timeout_minutes is not None and hello.is_readable ) - self.logical_session_timeout_minutes: Optional[int] = hello.logical_session_timeout_minutes + self.logical_session_timeout_minutes: int | None = hello.logical_session_timeout_minutes # hello_ok is set from helloOk, which is only returned from ismaster # don't overwrite this when connection switches to hello self.hello_ok = self.hello_ok or hello.hello_ok @@ -229,16 +227,16 @@ async def command( read_preference: _ServerMode = ReadPreference.PRIMARY, codec_options: CodecOptions[Mapping[str, Any]] = DEFAULT_CODEC_OPTIONS, # type: ignore[assignment] check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - read_concern: Optional[ReadConcern] = None, - write_concern: Optional[WriteConcern] = None, + allowable_errors: Sequence[str | int] | None = None, + read_concern: ReadConcern | None = None, + write_concern: WriteConcern | None = None, parse_write_concern_error: bool = False, - collation: Optional[_CollationIn] = None, - session: Optional[AsyncClientSession] = None, - client: Optional[AsyncMongoClient[Any]] = None, + collation: _CollationIn | None = None, + session: AsyncClientSession | None = None, + client: AsyncMongoClient[Any] | None = None, retryable_write: bool = False, publish_events: bool = True, - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, exhaust_allowed: bool = False, ) -> dict[str, Any]: """Execute a command or raise an error. @@ -327,7 +325,7 @@ async def send_message(self, message: bytes, max_doc_size: int) -> None: except BaseException as error: await self._raise_connection_failure(error) - async def receive_message(self, request_id: Optional[int]) -> _OpMsg: + async def receive_message(self, request_id: int | None) -> _OpMsg: """Receive a raw BSON message or raise ConnectionFailure. If any exception is raised, the socket is closed. @@ -359,7 +357,7 @@ async def authenticate(self, reauthenticate: bool = False) -> None: self.ready = True self._telemetry.connection_ready(self.id, self.creation_time) - async def close_conn(self, reason: Optional[str]) -> None: + async def close_conn(self, reason: str | None) -> None: """Close this connection with a reason.""" if self.closed: return @@ -429,7 +427,7 @@ def __init__( address: _Address, options: PoolOptions, is_sdam: bool = False, - client_id: Optional[ObjectId] = None, + client_id: ObjectId | None = None, ): """ :param address: a (hostname, port) tuple @@ -459,7 +457,7 @@ async def _reset( self, close: bool, pause: bool = True, - service_id: Optional[ObjectId] = None, + service_id: ObjectId | None = None, interrupt_connections: bool = False, ) -> None: old_state = self.state @@ -530,7 +528,7 @@ async def _reset( for conn in sockets: await conn.close_conn(ConnectionClosedReason.STALE) - async def update_is_writable(self, is_writable: Optional[bool]) -> None: + async def update_is_writable(self, is_writable: bool | None) -> None: """Updates the is_writable attribute on all sockets currently in the Pool. """ @@ -540,7 +538,7 @@ async def update_is_writable(self, is_writable: Optional[bool]) -> None: _socket.update_is_writable(self.is_writable) # type: ignore[arg-type] async def reset( - self, service_id: Optional[ObjectId] = None, interrupt_connections: bool = False + self, service_id: ObjectId | None = None, interrupt_connections: bool = False ) -> None: await self._reset( close=False, service_id=service_id, interrupt_connections=interrupt_connections @@ -621,7 +619,7 @@ async def remove_stale_sockets(self, reference_generation: int) -> None: self.requests -= 1 self.size_cond.notify() - async def connect(self, handler: Optional[_ClientCheckout] = None) -> AsyncConnection: + async def connect(self, handler: _ClientCheckout | None = None) -> AsyncConnection: """Connect to Mongo and return a new AsyncConnection. Can raise ConnectionFailure. @@ -689,7 +687,7 @@ async def connect(self, handler: Optional[_ClientCheckout] = None) -> AsyncConne return conn - def checkout(self, handler: Optional[_ClientCheckout] = None) -> _PoolCheckout: + def checkout(self, handler: _ClientCheckout | None = None) -> _PoolCheckout: """Get a connection from the pool. Always use in an async with statement:: async with pool.checkout() as connection: @@ -703,7 +701,7 @@ def checkout(self, handler: Optional[_ClientCheckout] = None) -> _PoolCheckout: return _PoolCheckout(self, handler) async def _get_conn( - self, checkout_started_time: float, handler: Optional[_ClientCheckout] = None + self, checkout_started_time: float, handler: _ClientCheckout | None = None ) -> AsyncConnection: """Get or create a AsyncConnection. Can raise ConnectionFailure.""" # We use the pid here to avoid issues with fork / multiprocessing. @@ -881,7 +879,7 @@ async def checkin(self, conn: AsyncConnection) -> None: self._pinned_sockets.discard(conn) forked = self.pid != os.getpid() # Re-apply the accounting if a BaseException interrupts here (PYTHON-6074). - close_conn_reason: Optional[str] = None + close_conn_reason: str | None = None emit_closed = False accounted = False try: @@ -967,11 +965,11 @@ class _PoolCheckout: def __init__( self, pool: Pool, - handler: Optional[_ClientCheckout] = None, + handler: _ClientCheckout | None = None, ) -> None: self._pool = pool self._handler = handler - self._conn: Optional[AsyncConnection] = None + self._conn: AsyncConnection | None = None async def __aenter__(self) -> AsyncConnection: pool = self._pool @@ -997,9 +995,9 @@ async def __aenter__(self) -> AsyncConnection: async def __aexit__( self, - exc_type: Optional[type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: conn = self._conn if conn is None: diff --git a/pymongo/asynchronous/server.py b/pymongo/asynchronous/server.py index 8764bfcb28..f711741a53 100644 --- a/pymongo/asynchronous/server.py +++ b/pymongo/asynchronous/server.py @@ -19,7 +19,6 @@ from typing import ( TYPE_CHECKING, Any, - Optional, ) from pymongo._telemetry import _SdamTelemetry @@ -43,9 +42,9 @@ def __init__( server_description: ServerDescription, pool: Pool, monitor: Monitor, - topology_id: Optional[ObjectId] = None, - listeners: Optional[_EventListeners] = None, - events: Optional[ReferenceType[Queue[Any]]] = None, + topology_id: ObjectId | None = None, + listeners: _EventListeners | None = None, + events: ReferenceType[Queue[Any]] | None = None, ) -> None: """Represent one MongoDB server.""" self._description = server_description @@ -62,7 +61,7 @@ async def open(self) -> None: if not self._pool.opts.load_balanced: self._monitor.open() - async def reset(self, service_id: Optional[ObjectId] = None) -> None: + async def reset(self, service_id: ObjectId | None = None) -> None: """Clear the connection pool.""" await self.pool.reset(service_id) diff --git a/pymongo/asynchronous/settings.py b/pymongo/asynchronous/settings.py index 4bc5f05f28..864766348a 100644 --- a/pymongo/asynchronous/settings.py +++ b/pymongo/asynchronous/settings.py @@ -18,7 +18,7 @@ import threading from collections.abc import Collection -from typing import Callable, Optional +from typing import Callable from bson.objectid import ObjectId from pymongo import common @@ -35,25 +35,25 @@ class TopologySettings(_BaseTopologySettings[type[Pool], type[monitor.Monitor]]): def __init__( self, - seeds: Optional[Collection[tuple[str, int]]] = None, - replica_set_name: Optional[str] = None, - pool_class: Optional[type[Pool]] = None, - pool_options: Optional[PoolOptions] = None, - monitor_class: Optional[type[monitor.Monitor]] = None, - condition_class: Optional[type[threading.Condition]] = None, + seeds: Collection[tuple[str, int]] | None = None, + replica_set_name: str | None = None, + pool_class: type[Pool] | None = None, + pool_options: PoolOptions | None = None, + monitor_class: type[monitor.Monitor] | None = None, + condition_class: type[threading.Condition] | None = None, local_threshold_ms: int = LOCAL_THRESHOLD_MS, server_selection_timeout: int = SERVER_SELECTION_TIMEOUT, heartbeat_frequency: int = common.HEARTBEAT_FREQUENCY, - server_selector: Optional[_ServerSelector] = None, - fqdn: Optional[str] = None, - direct_connection: Optional[bool] = False, - load_balanced: Optional[bool] = None, + server_selector: _ServerSelector | None = None, + fqdn: str | None = None, + direct_connection: bool | None = False, + load_balanced: bool | None = None, srv_service_name: str = common.SRV_SERVICE_NAME, srv_max_hosts: int = 0, - srv_allowed_hosts_suffix: Optional[str] = None, - srv_host_validator: Optional[Callable[[str], bool]] = None, + srv_allowed_hosts_suffix: str | None = None, + srv_host_validator: Callable[[str], bool] | None = None, server_monitoring_mode: str = common.SERVER_MONITORING_MODE, - topology_id: Optional[ObjectId] = None, + topology_id: ObjectId | None = None, ): """Represent MongoClient's configuration. diff --git a/pymongo/asynchronous/srv_resolver.py b/pymongo/asynchronous/srv_resolver.py index 262e9107ce..60d9ab0345 100644 --- a/pymongo/asynchronous/srv_resolver.py +++ b/pymongo/asynchronous/srv_resolver.py @@ -18,7 +18,7 @@ import ipaddress import random -from typing import TYPE_CHECKING, Any, Callable, Optional, Union +from typing import TYPE_CHECKING, Any, Callable from pymongo._psl import SPECIAL_USE_LABELS, _to_punycode, is_public_suffix from pymongo._telemetry import log_srv_monitor_invalid_host @@ -33,7 +33,7 @@ # dnspython can return bytes or str from various parts # of its API depending on version. We always want str. -def maybe_decode(text: Union[str, bytes]) -> str: +def maybe_decode(text: str | bytes) -> str: if isinstance(text, bytes): return text.decode() return text @@ -61,11 +61,11 @@ class _SrvResolver: def __init__( self, fqdn: str, - connect_timeout: Optional[float], + connect_timeout: float | None, srv_service_name: str, srv_max_hosts: int = 0, - srv_allowed_hosts_suffix: Optional[str] = None, - srv_host_validator: Optional[Callable[[str], bool]] = None, + srv_allowed_hosts_suffix: str | None = None, + srv_host_validator: Callable[[str], bool] | None = None, ): self.__fqdn = fqdn.lower() self.__srv = srv_service_name @@ -112,7 +112,7 @@ def __init__( self.__slen = len(self.__plist) self.nparts = len(split_fqdn) - async def get_options(self) -> Optional[str]: + async def get_options(self) -> str | None: from dns import resolver try: diff --git a/pymongo/asynchronous/topology.py b/pymongo/asynchronous/topology.py index 8f1b4b1a21..43a86cb476 100644 --- a/pymongo/asynchronous/topology.py +++ b/pymongo/asynchronous/topology.py @@ -26,7 +26,7 @@ import weakref from collections.abc import Mapping from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Optional, cast +from typing import TYPE_CHECKING, Any, Callable, cast from pymongo import _csot, common, helpers_shared, periodic_executor from pymongo._telemetry import ( @@ -86,7 +86,7 @@ class Topology(_AgnosticTopologyBase[Pool, Server]): _settings: TopologySettings _events_executor: periodic_executor.AsyncPeriodicExecutor - _srv_monitor: Optional[SrvMonitor] + _srv_monitor: SrvMonitor | None _monitor_tasks: list[MonitorBase] def __init__(self, topology_settings: TopologySettings): @@ -162,10 +162,10 @@ async def select_servers( self, selector: Callable[[Selection], Selection], operation: str, - server_selection_timeout: Optional[float] = None, - address: Optional[_Address] = None, - operation_id: Optional[int] = None, - deprioritized_servers: Optional[list[Server]] = None, + server_selection_timeout: float | None = None, + address: _Address | None = None, + operation_id: int | None = None, + deprioritized_servers: list[Server] | None = None, ) -> list[Server]: """Return a list of Servers matching selector, or time out. @@ -210,16 +210,16 @@ async def _select_servers_loop( selector: Callable[[Selection], Selection], timeout: float, operation: str, - operation_id: Optional[int], - address: Optional[_Address], - deprioritized_servers: Optional[list[Server]] = None, + operation_id: int | None, + address: _Address | None, + deprioritized_servers: list[Server] | None = None, ) -> list[ServerDescription]: """select_servers() guts. Hold the lock when calling this.""" now = time.monotonic() end_time = now + timeout logged_waiting = False # Server selection does not have APM events, gate only on logging - ss: Optional[_ServerSelectionTelemetry] = None + ss: _ServerSelectionTelemetry | None = None if _is_debug_enabled(_SERVER_SELECTION_LOGGER): ss = _ServerSelectionTelemetry( self._topology_id, selector, operation, operation_id, self.description @@ -269,10 +269,10 @@ async def _select_server( self, selector: Callable[[Selection], Selection], operation: str, - server_selection_timeout: Optional[float] = None, - address: Optional[_Address] = None, - deprioritized_servers: Optional[list[Server]] = None, - operation_id: Optional[int] = None, + server_selection_timeout: float | None = None, + address: _Address | None = None, + deprioritized_servers: list[Server] | None = None, + operation_id: int | None = None, ) -> Server: servers = await self.select_servers( selector, @@ -294,10 +294,10 @@ async def select_server( self, selector: Callable[[Selection], Selection], operation: str, - server_selection_timeout: Optional[float] = None, - address: Optional[_Address] = None, - deprioritized_servers: Optional[list[Server]] = None, - operation_id: Optional[int] = None, + server_selection_timeout: float | None = None, + address: _Address | None = None, + deprioritized_servers: list[Server] | None = None, + operation_id: int | None = None, ) -> Server: """Like select_servers, but choose a random server if several match.""" server = await self._select_server( @@ -326,8 +326,8 @@ async def select_server_by_address( self, address: _Address, operation: str, - server_selection_timeout: Optional[int] = None, - operation_id: Optional[int] = None, + server_selection_timeout: int | None = None, + operation_id: int | None = None, ) -> Server: """Return a Server for "address", reconnecting if necessary. @@ -450,7 +450,7 @@ async def on_srv_update(self, seedlist: list[tuple[str, Any]]) -> None: if self._opened: await self._process_srv_update(seedlist) - async def get_primary(self) -> Optional[_Address]: + async def get_primary(self) -> _Address | None: """Return primary's address or None.""" # Implemented here in Topology instead of AsyncMongoClient, so it can lock. async with self._lock: @@ -483,7 +483,7 @@ async def get_arbiters(self) -> set[_Address]: """Return set of arbiter addresses.""" return await self._get_replica_set_members(arbiter_server_selector) - async def receive_cluster_time(self, cluster_time: Optional[Mapping[str, Any]]) -> None: + async def receive_cluster_time(self, cluster_time: Mapping[str, Any] | None) -> None: async with self._lock: self._receive_cluster_time_no_lock(cluster_time) diff --git a/pymongo/asynchronous/uri_parser.py b/pymongo/asynchronous/uri_parser.py index c42add6ae8..01efdfe502 100644 --- a/pymongo/asynchronous/uri_parser.py +++ b/pymongo/asynchronous/uri_parser.py @@ -17,7 +17,7 @@ from __future__ import annotations -from typing import Any, Callable, Optional +from typing import Any, Callable from pymongo.asynchronous.srv_resolver import _SrvResolver from pymongo.common import SRV_SERVICE_NAME, _CaseInsensitiveDictionary @@ -40,15 +40,15 @@ async def parse_uri( uri: str, - default_port: Optional[int] = DEFAULT_PORT, + default_port: int | None = DEFAULT_PORT, validate: bool = True, warn: bool = False, normalize: bool = True, - connect_timeout: Optional[float] = None, - srv_service_name: Optional[str] = None, - srv_max_hosts: Optional[int] = None, - srv_allowed_hosts_suffix: Optional[str] = None, - srv_host_validator: Optional[Callable[[str], bool]] = None, + connect_timeout: float | None = None, + srv_service_name: str | None = None, + srv_max_hosts: int | None = None, + srv_allowed_hosts_suffix: str | None = None, + srv_host_validator: Callable[[str], bool] | None = None, ) -> dict[str, Any]: """Parse and validate a MongoDB URI. @@ -127,15 +127,15 @@ async def parse_uri( async def _parse_srv( uri: str, - default_port: Optional[int] = DEFAULT_PORT, + default_port: int | None = DEFAULT_PORT, validate: bool = True, warn: bool = False, normalize: bool = True, - connect_timeout: Optional[float] = None, - srv_service_name: Optional[str] = None, - srv_max_hosts: Optional[int] = None, - srv_allowed_hosts_suffix: Optional[str] = None, - srv_host_validator: Optional[Callable[[str], bool]] = None, + connect_timeout: float | None = None, + srv_service_name: str | None = None, + srv_max_hosts: int | None = None, + srv_allowed_hosts_suffix: str | None = None, + srv_host_validator: Callable[[str], bool] | None = None, ) -> dict[str, Any]: if uri.startswith(SCHEME): is_srv = False diff --git a/pymongo/auth_oidc_shared.py b/pymongo/auth_oidc_shared.py index a774c85c0e..1c6c5db49d 100644 --- a/pymongo/auth_oidc_shared.py +++ b/pymongo/auth_oidc_shared.py @@ -21,7 +21,7 @@ import os from collections.abc import Mapping, MutableMapping from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any, Callable, Optional +from typing import TYPE_CHECKING, Any, Callable from urllib.parse import quote import bson @@ -38,8 +38,8 @@ @dataclass class OIDCIdPInfo: issuer: str - clientId: Optional[str] = field(default=None) - requestScopes: Optional[list[str]] = field(default=None) + clientId: str | None = field(default=None) + requestScopes: list[str] | None = field(default=None) @dataclass @@ -47,15 +47,15 @@ class OIDCCallbackContext: timeout_seconds: float username: str version: int - refresh_token: Optional[str] = field(default=None) - idp_info: Optional[OIDCIdPInfo] = field(default=None) + refresh_token: str | None = field(default=None) + idp_info: OIDCIdPInfo | None = field(default=None) @dataclass class OIDCCallbackResult: access_token: str - expires_in_seconds: Optional[float] = field(default=None) - refresh_token: Optional[str] = field(default=None) + expires_in_seconds: float | None = field(default=None) + refresh_token: str | None = field(default=None) class OIDCCallback(abc.ABC): @@ -68,11 +68,11 @@ def fetch(self, context: OIDCCallbackContext) -> OIDCCallbackResult: @dataclass class _OIDCProperties: - callback: Optional[OIDCCallback] = field(default=None) - human_callback: Optional[OIDCCallback] = field(default=None) - environment: Optional[str] = field(default=None) + callback: OIDCCallback | None = field(default=None) + human_callback: OIDCCallback | None = field(default=None) + environment: str | None = field(default=None) allowed_hosts: list[str] = field(default_factory=list) - token_resource: Optional[str] = field(default=None) + token_resource: str | None = field(default=None) username: str = "" @@ -150,13 +150,13 @@ class _OIDCAuthenticatorBase: username: str properties: _OIDCProperties - refresh_token: Optional[str] = field(default=None) - access_token: Optional[str] = field(default=None) - idp_info: Optional[OIDCIdPInfo] = field(default=None) + refresh_token: str | None = field(default=None) + access_token: str | None = field(default=None) + idp_info: OIDCIdPInfo | None = field(default=None) token_gen_id: int = field(default=0) last_call_time: float = field(default=0) - def get_spec_auth_cmd(self) -> Optional[MutableMapping[str, Any]]: + def get_spec_auth_cmd(self) -> MutableMapping[str, Any] | None: """Get the appropriate speculative auth command.""" if not self.access_token: return None @@ -167,14 +167,14 @@ def _is_auth_error(self, err: Exception) -> bool: return False return err.code == _AUTHENTICATION_FAILURE_CODE - def _invalidate(self, oidc_token_gen_id: Optional[int]) -> None: + def _invalidate(self, oidc_token_gen_id: int | None) -> None: # Ignore the invalidation if a token gen id is given and is less than our # current token gen id. if (oidc_token_gen_id or 0) < self.token_gen_id: return self.access_token = None - def _get_start_command(self, payload: Optional[Mapping[str, Any]]) -> MutableMapping[str, Any]: + def _get_start_command(self, payload: Mapping[str, Any] | None) -> MutableMapping[str, Any]: if payload is None: principal_name = self.username if principal_name: diff --git a/pymongo/auth_shared.py b/pymongo/auth_shared.py index 1d624bd8c4..e43da1fd0c 100644 --- a/pymongo/auth_shared.py +++ b/pymongo/auth_shared.py @@ -23,7 +23,7 @@ from base64 import standard_b64encode from collections import namedtuple from collections.abc import Mapping, MutableMapping -from typing import TYPE_CHECKING, Any, Callable, Optional, cast +from typing import TYPE_CHECKING, Any, Callable, cast from bson import Binary from pymongo.auth_oidc_shared import ( @@ -106,11 +106,11 @@ def _validate_canonicalize_host_name(value: str | bool) -> str | bool: def _build_credentials_tuple( mech: str, - source: Optional[str], - user: Optional[str], - passwd: Optional[str], + source: str | None, + user: str | None, + passwd: str | None, extra: Mapping[str, Any], - database: Optional[str], + database: str | None, ) -> MongoCredential: """Build and return a mechanism specific credentials tuple.""" if mech not in ("MONGODB-X509", "MONGODB-AWS", "MONGODB-OIDC") and user is None: @@ -280,19 +280,19 @@ def _password_digest(username: str, password: str) -> str: class _AuthContext: def __init__(self, credentials: MongoCredential, address: tuple[str, int]) -> None: self.credentials = credentials - self.speculative_authenticate: Optional[Mapping[str, Any]] = None + self.speculative_authenticate: Mapping[str, Any] | None = None self.address = address @staticmethod def from_credentials( creds: MongoCredential, address: tuple[str, int], spec_auth_map: Mapping[str, Any] - ) -> Optional[_AuthContext]: + ) -> _AuthContext | None: spec_cls = spec_auth_map.get(creds.mechanism) if spec_cls: return cast(_AuthContext, spec_cls(creds, address)) return None - def speculate_command(self) -> Optional[MutableMapping[str, Any]]: + def speculate_command(self) -> MutableMapping[str, Any] | None: raise NotImplementedError def parse_response(self, hello: Hello[Mapping[str, Any]]) -> None: @@ -307,10 +307,10 @@ def __init__( self, credentials: MongoCredential, address: tuple[str, int], mechanism: str ) -> None: super().__init__(credentials, address) - self.scram_data: Optional[tuple[bytes, bytes]] = None + self.scram_data: tuple[bytes, bytes] | None = None self.mechanism = mechanism - def speculate_command(self) -> Optional[MutableMapping[str, Any]]: + def speculate_command(self) -> MutableMapping[str, Any] | None: nonce, first_bare, cmd = _authenticate_scram_start(self.credentials, self.mechanism) # The 'db' field is included only on the speculative command. cmd["db"] = self.credentials.source @@ -337,7 +337,7 @@ def __init__( super().__init__(credentials, address) self.authenticator_cls = authenticator_cls - def speculate_command(self) -> Optional[MutableMapping[str, Any]]: + def speculate_command(self) -> MutableMapping[str, Any] | None: authenticator = _get_authenticator(self.credentials, self.address, self.authenticator_cls) cmd = authenticator.get_spec_auth_cmd() if cmd is None: diff --git a/pymongo/bulk_shared.py b/pymongo/bulk_shared.py index 54f0d4cd22..0f7d7a4b04 100644 --- a/pymongo/bulk_shared.py +++ b/pymongo/bulk_shared.py @@ -18,7 +18,7 @@ from __future__ import annotations from collections.abc import Iterator, Mapping, MutableMapping -from typing import TYPE_CHECKING, Any, NoReturn, Optional, Union +from typing import TYPE_CHECKING, Any, NoReturn from bson.objectid import ObjectId from bson.raw_bson import RawBSONDocument @@ -149,9 +149,9 @@ def __init__( self, collection: _AgnosticCollection[_DocumentType], ordered: bool, - bypass_document_validation: Optional[bool], - comment: Optional[str] = None, - let: Optional[Any] = None, + bypass_document_validation: bool | None, + comment: str | None = None, + let: Any | None = None, ) -> None: """Initialize an _AgnosticBulkBase instance.""" self.collection = collection.with_options( @@ -162,7 +162,7 @@ def __init__( self.let = let if self.let is not None: validate_is_document_type("let", self.let) - self.comment: Optional[str] = comment + self.comment: str | None = comment self.ordered = ordered self.ops: list[tuple[int, Mapping[str, Any]]] = [] self.executed = False @@ -199,13 +199,13 @@ def add_insert(self, document: _DocumentOut) -> None: def add_update( self, selector: Mapping[str, Any], - update: Union[Mapping[str, Any], _Pipeline], + update: Mapping[str, Any] | _Pipeline, 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, - sort: Optional[Mapping[str, Any]] = None, + upsert: bool | None, + collation: Mapping[str, Any] | None = None, + array_filters: list[Mapping[str, Any]] | None = None, + hint: str | dict[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, ) -> None: """Create an update document and add it to the list of ops.""" validate_ok_for_update(update) @@ -232,10 +232,10 @@ def add_replace( self, selector: Mapping[str, Any], replacement: Mapping[str, Any], - upsert: Optional[bool], - collation: Optional[Mapping[str, Any]] = None, - hint: Union[str, dict[str, Any], None] = None, - sort: Optional[Mapping[str, Any]] = None, + upsert: bool | None, + collation: Mapping[str, Any] | None = None, + hint: str | dict[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, ) -> None: """Create a replace document and add it to the list of ops.""" validate_ok_for_replace(replacement) @@ -256,8 +256,8 @@ def add_delete( self, selector: Mapping[str, Any], limit: int, - collation: Optional[Mapping[str, Any]] = None, - hint: Union[str, dict[str, Any], None] = None, + collation: Mapping[str, Any] | None = None, + hint: str | dict[str, Any] | None = None, ) -> None: """Create a delete document and add it to the list of ops.""" cmd: dict[str, Any] = {"q": selector, "limit": limit} @@ -271,7 +271,7 @@ def add_delete( self.is_retryable = False self.ops.append((_DELETE, cmd)) - def gen_ordered(self) -> Iterator[Optional[_Run]]: + def gen_ordered(self) -> Iterator[_Run | None]: """Generate batches of operations, batched by type of operation, in the order **provided**. """ diff --git a/pymongo/change_stream_shared.py b/pymongo/change_stream_shared.py index 1622304cfa..5ab2938385 100644 --- a/pymongo/change_stream_shared.py +++ b/pymongo/change_stream_shared.py @@ -18,7 +18,7 @@ import copy from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast from bson import CodecOptions from bson.raw_bson import RawBSONDocument @@ -60,23 +60,21 @@ class _AgnosticChangeStream(Generic[_DocumentType, _ClientT]): def __init__( self, - target: Union[ - _AgnosticMongoClient, - _AgnosticDatabase[_DocumentType], - _AgnosticCollection[_DocumentType], - ], - pipeline: Optional[_Pipeline], - full_document: Optional[str], - resume_after: Optional[Mapping[str, Any]], - max_await_time_ms: Optional[int], - batch_size: Optional[int], - collation: Optional[_CollationIn], - start_at_operation_time: Optional[Timestamp], - session: Optional[_AgnosticClientSession], - start_after: Optional[Mapping[str, Any]], - comment: Optional[Any] = None, - full_document_before_change: Optional[str] = None, - show_expanded_events: Optional[bool] = None, + target: _AgnosticMongoClient + | _AgnosticDatabase[_DocumentType] + | _AgnosticCollection[_DocumentType], + pipeline: _Pipeline | None, + full_document: str | None, + resume_after: Mapping[str, Any] | None, + max_await_time_ms: int | None, + batch_size: int | None, + collation: _CollationIn | None, + start_at_operation_time: Timestamp | None, + session: _AgnosticClientSession | None, + start_after: Mapping[str, Any] | None, + comment: Any | None = None, + full_document_before_change: str | None = None, + show_expanded_events: bool | None = None, ) -> None: if pipeline is None: pipeline = [] @@ -187,7 +185,7 @@ def _process_result(self, result: Mapping[str, Any]) -> None: ) @property - def resume_token(self) -> Optional[Mapping[str, Any]]: + def resume_token(self) -> Mapping[str, Any] | None: """The cached resume token that will be used to resume after the most recently returned change. diff --git a/pymongo/client_options.py b/pymongo/client_options.py index b42635ef2c..7ef34c578a 100644 --- a/pymongo/client_options.py +++ b/pymongo/client_options.py @@ -20,7 +20,7 @@ from __future__ import annotations from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any, Optional, cast +from typing import TYPE_CHECKING, Any, cast from bson.codec_options import _parse_codec_options from pymongo import common @@ -47,8 +47,8 @@ def _parse_credentials( - username: str, password: str, database: Optional[str], options: Mapping[str, Any] -) -> Optional[MongoCredential]: + username: str, password: str, database: str | None, options: Mapping[str, Any] +) -> MongoCredential | None: """Parse authentication credentials.""" mechanism = options.get("authmechanism", "DEFAULT" if username else None) source = options.get("authsource") @@ -86,9 +86,7 @@ def _parse_read_concern(options: Mapping[str, Any]) -> ReadConcern: return ReadConcern(concern) -def _parse_ssl_options( - options: Mapping[str, Any], is_sync: bool -) -> tuple[Optional[SSLContext], bool]: +def _parse_ssl_options(options: Mapping[str, Any], is_sync: bool) -> tuple[SSLContext | None, bool]: """Parse ssl options.""" use_tls = options.get("tls") if use_tls is not None: @@ -151,7 +149,7 @@ def _parse_ssl_options( def _parse_pool_options( username: str, password: str, - database: Optional[str], + database: str | None, options: Mapping[str, Any], is_sync: bool, ) -> PoolOptions: @@ -166,7 +164,7 @@ def _parse_pool_options( connect_timeout = options.get("connecttimeoutms", common.CONNECT_TIMEOUT) socket_timeout = options.get("sockettimeoutms") wait_queue_timeout = options.get("waitqueuetimeoutms", common.WAIT_QUEUE_TIMEOUT) - event_listeners = cast(Optional[Sequence[_EventListener]], options.get("event_listeners")) + event_listeners = cast(Sequence[_EventListener] | None, options.get("event_listeners")) appname = options.get("appname") driver = options.get("driver") server_api = options.get("server_api") @@ -209,7 +207,7 @@ def __init__( self, username: str, password: str, - database: Optional[str], + database: str | None, options: Mapping[str, Any], is_sync: bool = True, ): @@ -255,7 +253,7 @@ def _options(self) -> Mapping[str, Any]: return self.__options @property - def connect(self) -> Optional[bool]: + def connect(self) -> bool | None: """Whether to begin discovering a MongoDB topology automatically.""" return self.__connect @@ -265,7 +263,7 @@ def codec_options(self) -> CodecOptions[Any]: return self.__codec_options @property - def direct_connection(self) -> Optional[bool]: + def direct_connection(self) -> bool | None: """Whether to connect to the deployment in 'Single' topology.""" return self.__direct_connection @@ -299,7 +297,7 @@ def read_preference(self) -> _ServerMode: return self.__read_preference @property - def replica_set_name(self) -> Optional[str]: + def replica_set_name(self) -> str | None: """Replica set name or None.""" return self.__replica_set_name @@ -314,7 +312,7 @@ def read_concern(self) -> ReadConcern: return self.__read_concern @property - def timeout(self) -> Optional[float]: + def timeout(self) -> float | None: """The configured timeoutMS converted to seconds, or None. .. versionadded:: 4.2 @@ -332,12 +330,12 @@ def retry_reads(self) -> bool: return self.__retry_reads @property - def auto_encryption_opts(self) -> Optional[AutoEncryptionOpts]: + def auto_encryption_opts(self) -> AutoEncryptionOpts | None: """A :class:`~pymongo.encryption.AutoEncryptionOpts` or None.""" return self.__auto_encryption_opts @property - def load_balanced(self) -> Optional[bool]: + def load_balanced(self) -> bool | None: """True if the client was configured to connect to a load balancer.""" return self.__load_balanced diff --git a/pymongo/client_session_shared.py b/pymongo/client_session_shared.py index a5b057298d..9a78122189 100644 --- a/pymongo/client_session_shared.py +++ b/pymongo/client_session_shared.py @@ -26,7 +26,6 @@ Callable, Generic, NoReturn, - Optional, Protocol, TypeVar, cast, @@ -87,9 +86,9 @@ class SessionOptions: def __init__( self, - causal_consistency: Optional[bool] = None, - default_transaction_options: Optional[TransactionOptions] = None, - snapshot: Optional[bool] = False, + causal_consistency: bool | None = None, + default_transaction_options: TransactionOptions | None = None, + snapshot: bool | None = False, ) -> None: if snapshot: if causal_consistency: @@ -113,7 +112,7 @@ def causal_consistency(self) -> bool: return self._causal_consistency @property - def default_transaction_options(self) -> Optional[TransactionOptions]: + def default_transaction_options(self) -> TransactionOptions | None: """The default TransactionOptions to use for transactions started on this session. @@ -122,7 +121,7 @@ def default_transaction_options(self) -> Optional[TransactionOptions]: return self._default_transaction_options @property - def snapshot(self) -> Optional[bool]: + def snapshot(self) -> bool | None: """Whether snapshot reads are configured. .. versionadded:: 3.12 @@ -160,10 +159,10 @@ class TransactionOptions: def __init__( self, - read_concern: Optional[ReadConcern] = None, - write_concern: Optional[WriteConcern] = None, - read_preference: Optional[_ServerMode] = None, - max_commit_time_ms: Optional[int] = None, + read_concern: ReadConcern | None = None, + write_concern: WriteConcern | None = None, + read_preference: _ServerMode | None = None, + max_commit_time_ms: int | None = None, ) -> None: self._read_concern = read_concern self._write_concern = write_concern @@ -199,22 +198,22 @@ def __init__( ) @property - def read_concern(self) -> Optional[ReadConcern]: + def read_concern(self) -> ReadConcern | None: """This transaction's :class:`~pymongo.read_concern.ReadConcern`.""" return self._read_concern @property - def write_concern(self) -> Optional[WriteConcern]: + def write_concern(self) -> WriteConcern | None: """This transaction's :class:`~pymongo.write_concern.WriteConcern`.""" return self._write_concern @property - def read_preference(self) -> Optional[_ServerMode]: + def read_preference(self) -> _ServerMode | None: """This transaction's :class:`~pymongo.read_preferences.ReadPreference`.""" return self._read_preference @property - def max_commit_time_ms(self) -> Optional[int]: + def max_commit_time_ms(self) -> int | None: """The maxTimeMS to use when running a commitTransaction command. .. versionadded:: 3.9 @@ -223,8 +222,8 @@ def max_commit_time_ms(self) -> Optional[int]: def _validate_session_write_concern( - session: Optional[_ClientSessionT], write_concern: Optional[WriteConcern] -) -> Optional[_ClientSessionT]: + session: _ClientSessionT | None, write_concern: WriteConcern | None +) -> _ClientSessionT | None: """Validate that an explicit session is not used with an unack'ed write. Returns the session to use for the next operation. @@ -336,7 +335,7 @@ def mark_dirty(self) -> None: """ self.dirty = True - def timed_out(self, session_timeout_minutes: Optional[int]) -> bool: + def timed_out(self, session_timeout_minutes: int | None) -> bool: if session_timeout_minutes is None: return False @@ -377,7 +376,7 @@ def pop_all(self) -> list[_ServerSession]: break return ids - def get_server_session(self, session_timeout_minutes: Optional[int]) -> _ServerSession: + def get_server_session(self, session_timeout_minutes: int | None) -> _ServerSession: # Although the Driver Sessions Spec says we only clear stale sessions # in return_server_session, PyMongo can't take a lock when returning # sessions from a __del__ method (like in Cursor.__die), so it can't @@ -402,7 +401,7 @@ def return_server_session(self, server_session: _ServerSession) -> None: if server_session.generation == self.generation and not server_session.dirty: self.appendleft(server_session) - def _clear_stale(self, session_timeout_minutes: Optional[int]) -> None: + def _clear_stale(self, session_timeout_minutes: int | None) -> None: # Clear stale sessions. The least recently used are on the right. while True: try: @@ -441,8 +440,8 @@ def __init__( self._client: _ClientT = client self._server_session = server_session self._options = options - self._cluster_time: Optional[Mapping[str, Any]] = None - self._operation_time: Optional[Timestamp] = None + self._cluster_time: Mapping[str, Any] | None = None + self._operation_time: Timestamp | None = None self._snapshot_time = None # Is this an implicitly created session? self._implicit = implicit @@ -515,14 +514,14 @@ def _transaction_id(self) -> Int64: return self._server_session.transaction_id @property - def cluster_time(self) -> Optional[ClusterTime]: + def cluster_time(self) -> ClusterTime | None: """The cluster time returned by the last operation executed in this session. """ return self._cluster_time @property - def operation_time(self) -> Optional[Timestamp]: + def operation_time(self) -> Timestamp | None: """The operation time returned by the last operation executed in this session. """ @@ -538,7 +537,7 @@ def _inherit_option(self, name: str, val: _T) -> _T: return parent_val return getattr(self.client, name) - def _advance_cluster_time(self, cluster_time: Optional[Mapping[str, Any]]) -> None: + def _advance_cluster_time(self, cluster_time: Mapping[str, Any] | None) -> None: """Internal cluster time helper.""" if self._cluster_time is None: self._cluster_time = cluster_time @@ -562,7 +561,7 @@ def advance_cluster_time(self, cluster_time: Mapping[str, Any]) -> None: raise ValueError("Invalid cluster_time") self._advance_cluster_time(cluster_time) - def _advance_operation_time(self, operation_time: Optional[Timestamp]) -> None: + def _advance_operation_time(self, operation_time: Timestamp | None) -> None: """Internal operation time helper.""" if self._operation_time is None: self._operation_time = operation_time @@ -618,14 +617,14 @@ def _starting_transaction(self) -> bool: return self._transaction.starting() @property - def _pinned_address(self) -> Optional[_Address]: + def _pinned_address(self) -> _Address | None: """The mongos address this transaction was created on.""" if self._transaction.active(): return self._transaction.pinned_address return None @property - def _pinned_connection(self) -> Optional[_ConnectionT]: + def _pinned_connection(self) -> _ConnectionT | None: """The connection this transaction was started on.""" return self._transaction.pinned_conn @@ -633,14 +632,14 @@ def _pin(self, server: _AgnosticServer, conn: _ConnectionT) -> None: """Pin this session to the given Server or to the given connection.""" self._transaction.pin(server, conn) - def _txn_read_preference(self) -> Optional[_ServerMode]: + def _txn_read_preference(self) -> _ServerMode | None: """Return read preference of this transaction or None.""" if self.in_transaction: assert self._transaction.opts return self._transaction.opts.read_preference return None - def _materialize(self, logical_session_timeout_minutes: Optional[int] = None) -> None: + def _materialize(self, logical_session_timeout_minutes: int | None = None) -> None: if isinstance(self._server_session, _EmptyServerSession): old = self._server_session self._server_session = self._client._topology.get_server_session( @@ -725,7 +724,7 @@ def __copy__(self) -> NoReturn: class _ConnectionManagerProtocol(Protocol[_ConnectionT]): """Protocol for the connection manager used to pin a connection to a transaction.""" - conn: Optional[_ConnectionT] + conn: _ConnectionT | None class _TransactionBase(Generic[_ConnectionT]): @@ -734,12 +733,12 @@ class _TransactionBase(Generic[_ConnectionT]): # Set by the async/sync subclasses to the correct _ConnectionManager class. _conn_mgr_cls: Callable[[_ConnectionT, bool], _ConnectionManagerProtocol[_ConnectionT]] - def __init__(self, opts: Optional[TransactionOptions], client: _AgnosticMongoClient): + def __init__(self, opts: TransactionOptions | None, client: _AgnosticMongoClient): self.opts = opts self.state = _TxnState.NONE self.sharded = False - self.pinned_address: Optional[_Address] = None - self.conn_mgr: Optional[_ConnectionManagerProtocol[_ConnectionT]] = None + self.pinned_address: _Address | None = None + self.conn_mgr: _ConnectionManagerProtocol[_ConnectionT] | None = None self.recovery_token = None self.attempt = 0 self.client = client @@ -759,7 +758,7 @@ def set_in_progress(self) -> None: self.state = _TxnState.IN_PROGRESS @property - def pinned_conn(self) -> Optional[_ConnectionT]: + def pinned_conn(self) -> _ConnectionT | None: if self.active() and self.conn_mgr: return self.conn_mgr.conn return None diff --git a/pymongo/collation.py b/pymongo/collation.py index 9f108c447c..dac664a474 100644 --- a/pymongo/collation.py +++ b/pymongo/collation.py @@ -22,7 +22,7 @@ from __future__ import annotations from collections.abc import Mapping -from typing import Any, Optional, Union +from typing import Any from pymongo import common from pymongo.write_concern import validate_boolean @@ -161,14 +161,14 @@ class Collation: def __init__( self, locale: str, - caseLevel: Optional[bool] = None, - caseFirst: Optional[str] = None, - strength: Optional[int] = None, - numericOrdering: Optional[bool] = None, - alternate: Optional[str] = None, - maxVariable: Optional[str] = None, - normalization: Optional[bool] = None, - backwards: Optional[bool] = None, + caseLevel: bool | None = None, + caseFirst: str | None = None, + strength: int | None = None, + numericOrdering: bool | None = None, + alternate: str | None = None, + maxVariable: str | None = None, + normalization: bool | None = None, + backwards: bool | None = None, **kwargs: Any, ) -> None: locale = common.validate_string("locale", locale) @@ -219,8 +219,8 @@ def __ne__(self, other: Any) -> bool: def validate_collation_or_none( - value: Optional[Union[Mapping[str, Any], Collation]], -) -> Optional[dict[str, Any]]: + value: Mapping[str, Any] | Collation | None, +) -> dict[str, Any] | None: if value is None: return None if isinstance(value, Collation): diff --git a/pymongo/common.py b/pymongo/common.py index ce4379b507..d543b209cb 100644 --- a/pymongo/common.py +++ b/pymongo/common.py @@ -28,8 +28,6 @@ Any, Callable, NoReturn, - Optional, - Union, overload, ) from urllib.parse import unquote_plus @@ -107,13 +105,13 @@ MAX_CONNECTING = 2 # Default value for maxIdleTimeMS. -MAX_IDLE_TIME_MS: Optional[int] = None +MAX_IDLE_TIME_MS: int | None = None # Default value for maxIdleTimeMS in seconds. -MAX_IDLE_TIME_SEC: Optional[int] = None +MAX_IDLE_TIME_SEC: int | None = None # Default value for waitQueueTimeoutMS in seconds. -WAIT_QUEUE_TIMEOUT: Optional[int] = None +WAIT_QUEUE_TIMEOUT: int | None = None # Default value for localThresholdMS. LOCAL_THRESHOLD_MS = 15 @@ -173,7 +171,7 @@ def clean_node(node: str) -> tuple[str, int]: return host.lower(), port -def raise_config_error(key: str, suggestions: Optional[list[str]] = None) -> NoReturn: +def raise_config_error(key: str, suggestions: list[str] | None = None) -> NoReturn: """Raise ConfigurationError with the given key name.""" msg = f"Unknown option: {key}." if suggestions: @@ -228,7 +226,7 @@ def validate_non_negative_integer(option: str, value: Any) -> int: return val -def validate_readable(option: str, value: Any) -> Optional[str]: +def validate_readable(option: str, value: Any) -> str | None: """Validates that 'value' is file-like and readable.""" if value is None: return value @@ -239,7 +237,7 @@ def validate_readable(option: str, value: Any) -> Optional[str]: return value -def validate_non_negative_integer_or_none(option: str, value: Any) -> Optional[int]: +def validate_non_negative_integer_or_none(option: str, value: Any) -> int | None: """Validate that 'value' is a positive integer or 0 or None.""" if value is None: return value @@ -253,14 +251,14 @@ def validate_string(option: str, value: Any) -> str: raise TypeError(f"Wrong type for {option}, value must be an instance of str, not {type(value)}") -def validate_string_or_none(option: str, value: Any) -> Optional[str]: +def validate_string_or_none(option: str, value: Any) -> str | None: """Validates that 'value' is an instance of `basestring` or `None`.""" if value is None: return value return validate_string(option, value) -def validate_non_negative_int_or_basestring(option: Any, value: Any) -> Union[int, str]: +def validate_non_negative_int_or_basestring(option: Any, value: Any) -> int | str: """Validates that 'value' is an integer or string.""" if isinstance(value, int): return value @@ -303,7 +301,7 @@ def validate_positive_float_or_zero(option: str, value: Any) -> float: return validate_positive_float(option, value) -def validate_timeout_or_none(option: str, value: Any) -> Optional[float]: +def validate_timeout_or_none(option: str, value: Any) -> float | None: """Validates a timeout specified in milliseconds returning a value in floating point seconds. """ @@ -325,7 +323,7 @@ def validate_timeout_or_zero(option: str, value: Any) -> float: return validate_positive_float(option, value) / 1000.0 -def validate_timeout_or_none_or_zero(option: Any, value: Any) -> Optional[float]: +def validate_timeout_or_none_or_zero(option: Any, value: Any) -> float | None: """Validates a timeout specified in milliseconds returning a value in floating point seconds. value=0 and value="0" are treated the same as value=None which means unlimited timeout. @@ -335,7 +333,7 @@ def validate_timeout_or_none_or_zero(option: Any, value: Any) -> Optional[float] return validate_positive_float(option, value) / 1000.0 -def validate_timeoutms(option: Any, value: Any) -> Optional[float]: +def validate_timeoutms(option: Any, value: Any) -> float | None: """Validates a timeout specified in milliseconds returning a value in floating point seconds. """ @@ -451,7 +449,7 @@ def redact_auth_mechanism_properties_for_repr(value: Any) -> Any: return redacted -def validate_auth_mechanism_properties(option: str, value: Any) -> dict[str, Union[bool, str]]: +def validate_auth_mechanism_properties(option: str, value: Any) -> dict[str, bool | str]: """Validate authMechanismProperties.""" props: dict[str, Any] = {} if not isinstance(value, str): @@ -508,7 +506,7 @@ def validate_auth_mechanism_properties(option: str, value: Any) -> dict[str, Uni def validate_document_class( option: str, value: Any -) -> Union[type[MutableMapping[str, Any]], type[RawBSONDocument]]: +) -> type[MutableMapping[str, Any]] | type[RawBSONDocument]: """Validate the document_class option.""" # Generic aliases like SON[str, Any] or dict[str, Any] aren't classes, so # resolve to their origin before the subclass check. issubclass() raises @@ -528,7 +526,7 @@ def validate_document_class( return value -def validate_type_registry(option: Any, value: Any) -> Optional[TypeRegistry]: +def validate_type_registry(option: Any, value: Any) -> TypeRegistry | None: """Validate the type_registry option.""" if value is not None and not isinstance(value, TypeRegistry): raise TypeError(f"{option} must be an instance of {TypeRegistry}") @@ -542,7 +540,7 @@ def validate_list(option: str, value: Any) -> list[Any]: return value -def validate_list_or_none(option: Any, value: Any) -> Optional[list[Any]]: +def validate_list_or_none(option: Any, value: Any) -> list[Any] | None: """Validates that 'value' is a list or None.""" if value is None: return value @@ -580,7 +578,7 @@ def validate_is_document_type(option: str, value: Any) -> None: ) -def validate_appname_or_none(option: str, value: Any) -> Optional[str]: +def validate_appname_or_none(option: str, value: Any) -> str | None: """Validate the appname option.""" if value is None: return value @@ -591,7 +589,7 @@ def validate_appname_or_none(option: str, value: Any) -> Optional[str]: return value -def validate_driver_or_none(option: Any, value: Any) -> Optional[DriverInfo]: +def validate_driver_or_none(option: Any, value: Any) -> DriverInfo | None: """Validate the driver keyword arg.""" if value is None: return value @@ -600,7 +598,7 @@ def validate_driver_or_none(option: Any, value: Any) -> Optional[DriverInfo]: return value -def validate_server_api_or_none(option: Any, value: Any) -> Optional[ServerApi]: +def validate_server_api_or_none(option: Any, value: Any) -> ServerApi | None: """Validate the server_api keyword arg.""" if value is None: return value @@ -609,7 +607,7 @@ def validate_server_api_or_none(option: Any, value: Any) -> Optional[ServerApi]: return value -def validate_is_callable_or_none(option: Any, value: Any) -> Optional[Callable[..., Any]]: +def validate_is_callable_or_none(option: Any, value: Any) -> Callable[..., Any] | None: """Validates that 'value' is a callable.""" if value is None: return value @@ -655,14 +653,14 @@ def validate_unicode_decode_error_handler(dummy: Any, value: str) -> str: return value -def validate_tzinfo(dummy: Any, value: Any) -> Optional[datetime.tzinfo]: +def validate_tzinfo(dummy: Any, value: Any) -> datetime.tzinfo | None: """Validate the tzinfo option""" if value is not None and not isinstance(value, datetime.tzinfo): raise TypeError(f"{value} must be an instance of datetime.tzinfo") return value -def validate_auto_encryption_opts_or_none(option: Any, value: Any) -> Optional[Any]: +def validate_auto_encryption_opts_or_none(option: Any, value: Any) -> Any | None: """Validate the driver keyword arg.""" if value is None: return value @@ -674,7 +672,7 @@ def validate_auto_encryption_opts_or_none(option: Any, value: Any) -> Optional[A return value -def validate_datetime_conversion(option: Any, value: Any) -> Optional[DatetimeConversion]: +def validate_datetime_conversion(option: Any, value: Any) -> DatetimeConversion | None: """Validate a DatetimeConversion string.""" if value is None: return DatetimeConversion.DATETIME @@ -836,7 +834,7 @@ def validate_server_monitoring_mode(option: str, value: str) -> str: def _get_validator( - key: str, validators: dict[str, Callable[[Any, Any], Any]], normed_key: Optional[str] = None + key: str, validators: dict[str, Callable[[Any, Any], Any]], normed_key: str | None = None ) -> Callable[[Any, Any], Any]: normed_key = normed_key or key try: @@ -971,7 +969,7 @@ def write_concern(self) -> WriteConcern: """ return self._write_concern - def _write_concern_for(self, session: Optional[_AgnosticClientSession]) -> WriteConcern: + def _write_concern_for(self, session: _AgnosticClientSession | None) -> WriteConcern: """Read only access to the write concern of this instance or session.""" # Override this operation's write concern with the transaction's. if session and session.in_transaction: @@ -979,7 +977,7 @@ def _write_concern_for(self, session: Optional[_AgnosticClientSession]) -> Write return self.write_concern def _write_concern_for_cmd( - self, cmd: Mapping[str, Any], session: Optional[_AgnosticClientSession] + self, cmd: Mapping[str, Any], session: _AgnosticClientSession | None ) -> WriteConcern: raw_wc = cmd.get("writeConcern") if raw_wc is not None: @@ -996,7 +994,7 @@ def read_preference(self) -> _ServerMode: """ return self._read_preference - def _read_preference_for(self, session: Optional[_AgnosticClientSession]) -> _ServerMode: + def _read_preference_for(self, session: _AgnosticClientSession | None) -> _ServerMode: """Read only access to the read preference of this instance or session.""" # Override this operation's read preference with the transaction's. if session: @@ -1057,7 +1055,7 @@ def __eq__(self, other: Any) -> bool: __hash__ = None # type: ignore[assignment] - def get(self, key: str, default: Optional[Any] = None) -> Any: + def get(self, key: str, default: Any | None = None) -> Any: return self.__data.get(key.lower(), default) def pop(self, key: str, *args: Any, **kwargs: Any) -> Any: @@ -1075,12 +1073,12 @@ def clear(self) -> None: self.__data.clear() @overload - def setdefault(self, key: str, default: None = None) -> Optional[Any]: ... + def setdefault(self, key: str, default: None = None) -> Any | None: ... @overload def setdefault(self, key: str, default: Any) -> Any: ... - def setdefault(self, key: str, default: Optional[Any] = None) -> Optional[Any]: + def setdefault(self, key: str, default: Any | None = None) -> Any | None: lc_key = key.lower() if key in self: return self.__data[lc_key] diff --git a/pymongo/compression_support.py b/pymongo/compression_support.py index 39f0834a8c..4cf730334c 100644 --- a/pymongo/compression_support.py +++ b/pymongo/compression_support.py @@ -16,7 +16,7 @@ import sys import warnings from collections.abc import Iterable -from typing import Any, Optional, Union +from typing import Any from pymongo.errors import ProtocolError from pymongo.hello import HelloCompat @@ -57,7 +57,7 @@ def _have_zstd() -> bool: return False -def validate_compressors(dummy: Any, value: Union[str, Iterable[str]]) -> list[str]: +def validate_compressors(dummy: Any, value: str | Iterable[str]) -> list[str]: try: # `value` is string. compressors = value.split(",") # type: ignore[union-attr] @@ -116,8 +116,8 @@ def __init__(self, compressors: list[str], zlib_compression_level: int): self.zlib_compression_level = zlib_compression_level def get_compression_context( - self, compressors: Optional[list[str]] - ) -> Union[SnappyContext, ZlibContext, ZstdContext, None]: + self, compressors: list[str] | None + ) -> SnappyContext | ZlibContext | ZstdContext | None: if compressors: chosen = compressors[0] if chosen == "snappy": diff --git a/pymongo/cursor_shared.py b/pymongo/cursor_shared.py index 3a0acbe932..a766e2a19c 100644 --- a/pymongo/cursor_shared.py +++ b/pymongo/cursor_shared.py @@ -22,7 +22,7 @@ from abc import ABC, abstractmethod from collections import deque from collections.abc import Iterable, Mapping, Sequence -from typing import TYPE_CHECKING, Any, Generic, Optional, Union, cast, overload +from typing import TYPE_CHECKING, Any, Generic, cast, overload from bson import RE_TYPE from bson.code import Code @@ -55,7 +55,7 @@ def _split_message( - message: Union[tuple[int, bytes], tuple[int, bytes, int]], + message: tuple[int, bytes] | tuple[int, bytes, int], ) -> tuple[int, bytes, int]: """Return request_id, data, max_doc_size. @@ -76,11 +76,11 @@ class _AgnosticCursorBase(Generic[_DocumentType], ABC): # These are all typed more accurately in subclasses. _collection: Any - _id: Optional[int] + _id: int | None _data: Any - _address: Optional[_Address] + _address: _Address | None _sock_mgr: Any - _session: Optional[Any] + _session: Any | None _killed: bool @abstractmethod @@ -89,7 +89,7 @@ def _get_namespace(self) -> str: ... @property - def session(self) -> Optional[_AgnosticClientSession]: + def session(self) -> _AgnosticClientSession | None: """The cursor's session, or None if the session is implicit. Typed more accurately in subclasses. @@ -131,7 +131,7 @@ def alive(self) -> bool: return bool(len(self._data) or (not self._killed)) @property - def cursor_id(self) -> Optional[int]: + def cursor_id(self) -> int | None: """Returns the id of the cursor. .. versionadded:: 2.2 @@ -139,7 +139,7 @@ def cursor_id(self) -> Optional[int]: return self._id @property - def address(self) -> Optional[_Address]: + def address(self) -> _Address | None: """The (host, port) of the server used, or None. .. versionchanged:: 3.0 @@ -147,7 +147,7 @@ def address(self) -> Optional[_Address]: """ return self._address - def _prepare_to_die(self, already_killed: bool) -> tuple[int, Optional[_CursorAddress]]: + def _prepare_to_die(self, already_killed: bool) -> tuple[int, _CursorAddress | None]: self._killed = True if self._id and not already_killed: cursor_id = self._id @@ -192,10 +192,10 @@ def __init__( self, collection: _AgnosticCollection[_DocumentType], cursor_info: Mapping[str, Any], - address: Optional[_Address], + address: _Address | None, batch_size: int = 0, - max_await_time_ms: Optional[int] = None, - session: Optional[_AgnosticClientSession] = None, + max_await_time_ms: int | None = None, + session: _AgnosticClientSession | None = None, comment: Any = None, ) -> None: """Create a new command cursor.""" @@ -203,7 +203,7 @@ def __init__( self._collection = collection self._id = cursor_info["id"] self._data = deque(cursor_info["firstBatch"]) - self._postbatchresumetoken: Optional[Mapping[str, Any]] = cursor_info.get( + self._postbatchresumetoken: Mapping[str, Any] | None = cursor_info.get( "postBatchResumeToken" ) self._address = address @@ -263,7 +263,7 @@ def _has_next(self) -> bool: return len(self._data) > 0 @property - def _post_batch_resume_token(self) -> Optional[Mapping[str, Any]]: + def _post_batch_resume_token(self) -> Mapping[str, Any] | None: """Retrieve the postBatchResumeToken from the response to a changeStream aggregate or getMore. """ @@ -272,9 +272,9 @@ def _post_batch_resume_token(self) -> Optional[Mapping[str, Any]]: def _unpack_response( self, response: _OpMsg, - cursor_id: Optional[int], + cursor_id: int | None, codec_options: CodecOptions[Mapping[str, Any]], - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, legacy_response: bool = False, ) -> Sequence[_DocumentOut]: return response.unpack_response(cursor_id, codec_options, user_fields, legacy_response) @@ -356,10 +356,8 @@ class CursorType: """ -_Sort = Union[ - Sequence[Union[str, tuple[str, Union[int, str, Mapping[str, Any]]]]], Mapping[str, Any] -] -_Hint = Union[str, _Sort] +_Sort = Sequence[str | tuple[str, int | str | Mapping[str, Any]]] | Mapping[str, Any] +_Hint = str | _Sort class _AgnosticCursor(_AgnosticCursorBase[_DocumentType]): @@ -374,29 +372,29 @@ class _AgnosticCursor(_AgnosticCursorBase[_DocumentType]): def __init__( self, collection: _AgnosticCollection[_DocumentType], - filter: Optional[Mapping[str, Any]] = None, - projection: Optional[Union[Mapping[str, Any], Iterable[str]]] = None, + filter: Mapping[str, Any] | None = None, + projection: Mapping[str, Any] | Iterable[str] | None = None, skip: int = 0, limit: int = 0, no_cursor_timeout: bool = False, cursor_type: int = CursorType.NON_TAILABLE, - sort: Optional[_Sort] = None, + sort: _Sort | None = None, allow_partial_results: bool = False, oplog_replay: bool = False, batch_size: int = 0, - collation: Optional[_CollationIn] = None, - hint: Optional[_Hint] = None, - max_scan: Optional[int] = None, - max_time_ms: Optional[int] = None, - max: Optional[_Sort] = None, - min: Optional[_Sort] = None, - return_key: Optional[bool] = None, - show_record_id: Optional[bool] = None, - snapshot: Optional[bool] = None, - comment: Optional[Any] = None, - session: Optional[_AgnosticClientSession] = None, - allow_disk_use: Optional[bool] = None, - let: Optional[Mapping[str, Any]] = None, + collation: _CollationIn | None = None, + hint: _Hint | None = None, + max_scan: int | None = None, + max_time_ms: int | None = None, + max: _Sort | None = None, + min: _Sort | None = None, + return_key: bool | None = None, + show_record_id: bool | None = None, + snapshot: bool | None = None, + comment: Any | None = None, + session: _AgnosticClientSession | None = None, + allow_disk_use: bool | None = None, + let: Mapping[str, Any] | None = None, ) -> None: """Create a new cursor. Used by a collection's ``find()`` method to iterate over MongoDB query results. @@ -473,15 +471,15 @@ def __init__( self._comment = comment self._max_time_ms = max_time_ms self._timeout = self._collection.database.client.options.timeout - self._max_await_time_ms: Optional[int] = None - self._max: Optional[Union[dict[Any, Any], _Sort]] = max - self._min: Optional[Union[dict[Any, Any], _Sort]] = min + self._max_await_time_ms: int | None = None + self._max: dict[Any, Any] | _Sort | None = max + self._min: dict[Any, Any] | _Sort | None = min self._collation = validate_collation_or_none(collation) self._return_key = return_key self._show_record_id = show_record_id self._allow_disk_use = allow_disk_use self._snapshot = snapshot - self._hint: Union[str, dict[str, Any], None] + self._hint: str | dict[str, Any] | None self._set_hint(hint) # This is ugly. People want to be able to do cursor[5:5] and @@ -493,12 +491,12 @@ def __init__( self._empty = False self._data: deque = deque() # type: ignore[type-arg] - self._address: Optional[_Address] = None + self._address: _Address | None = None self._retrieved = 0 self._codec_options = collection.codec_options # Read preference is set when the initial find is sent. - self._read_preference: Optional[_ServerMode] = None + self._read_preference: _ServerMode | None = None self._read_concern = collection.read_concern self._query_flags = cursor_type @@ -545,7 +543,7 @@ def clone(self) -> Self: """ return self._clone(True) - def _clone(self, deepcopy: bool = True, base: Optional[Any] = None) -> Self: + def _clone(self, deepcopy: bool = True, base: Any | None = None) -> Self: """Internal clone helper.""" if not base: if self._session and not self._session._implicit: @@ -587,7 +585,7 @@ def _clone(self, deepcopy: bool = True, base: Optional[Any] = None) -> Self: base.__dict__.update(data) return base - def _clone_base(self, session: Optional[Any]) -> Self: + def _clone_base(self, session: Any | None) -> Self: """Creates an empty cursor object for information to be copied into.""" return self.__class__(self._collection, session=session) @@ -752,7 +750,7 @@ def skip(self, skip: int) -> Self: self._skip = skip return self - def max_time_ms(self, max_time_ms: Optional[int]) -> Self: + def max_time_ms(self, max_time_ms: int | None) -> Self: """Specifies a time limit for a query operation. If the specified time is exceeded, the operation will be aborted and :exc:`~pymongo.errors.ExecutionTimeout` is raised. If `max_time_ms` @@ -771,7 +769,7 @@ def max_time_ms(self, max_time_ms: Optional[int]) -> Self: self._max_time_ms = max_time_ms return self - def max_await_time_ms(self, max_await_time_ms: Optional[int]) -> Self: + def max_await_time_ms(self, max_await_time_ms: int | None) -> Self: """Specifies a time limit for a getMore operation on a :attr:`~pymongo.cursor.CursorType.TAILABLE_AWAIT` cursor. For all other types of cursor max_await_time_ms is ignored. @@ -799,7 +797,7 @@ def max_await_time_ms(self, max_await_time_ms: Optional[int]) -> Self: return self - def max_scan(self, max_scan: Optional[int]) -> Self: + def max_scan(self, max_scan: int | None) -> Self: """**DEPRECATED** - Limit the number of documents to scan when performing the query. @@ -860,7 +858,7 @@ def min(self, spec: _Sort) -> Self: self._min = dict(spec) return self - def sort(self, key_or_list: _Hint, direction: Optional[Union[int, str]] = None) -> Self: + def sort(self, key_or_list: _Hint, direction: int | str | None = None) -> Self: """Sorts this cursor's results. Pass a field name and a direction, either @@ -923,7 +921,7 @@ def sort(self, key_or_list: _Hint, direction: Optional[Union[int, str]] = None) self._ordering = helpers_shared._index_document(keys) return self - def _set_hint(self, index: Optional[_Hint]) -> None: + def _set_hint(self, index: _Hint | None) -> None: if index is None: self._hint = None return @@ -933,7 +931,7 @@ def _set_hint(self, index: Optional[_Hint]) -> None: else: self._hint = helpers_shared._index_document(index) - def hint(self, index: Optional[_Hint]) -> Self: + def hint(self, index: _Hint | None) -> Self: """Adds a 'hint', telling Mongo the proper index to use for the query. Judicious use of hints can greatly improve query @@ -970,7 +968,7 @@ def comment(self, comment: Any) -> Self: self._comment = comment return self - def where(self, code: Union[str, Code]) -> Self: + def where(self, code: str | Code) -> Self: """Adds a `$where`_ clause to this query. The `code` argument must be an instance of :class:`str` or @@ -1017,7 +1015,7 @@ def where(self, code: Union[str, Code]) -> Self: self._spec = spec return self - def collation(self, collation: Optional[_CollationIn]) -> Self: + def collation(self, collation: _CollationIn | None) -> Self: """Adds a :class:`~pymongo.collation.Collation` to this query. Raises :exc:`TypeError` if `collation` is not an instance of @@ -1035,9 +1033,9 @@ def collation(self, collation: Optional[_CollationIn]) -> Self: def _unpack_response( self, response: _OpMsg, - cursor_id: Optional[int], + cursor_id: int | None, codec_options: CodecOptions, # type: ignore[type-arg] - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, legacy_response: bool = False, ) -> Sequence[_DocumentOut]: return response.unpack_response(cursor_id, codec_options, user_fields, legacy_response) @@ -1063,28 +1061,28 @@ def __deepcopy__(self, memo: Any) -> Any: return self._clone(deepcopy=True) @overload - def _deepcopy(self, x: Iterable, memo: Optional[dict[int, Union[list, dict]]] = None) -> list: # type: ignore[type-arg] + def _deepcopy(self, x: Iterable, memo: dict[int, list | dict] | None = None) -> list: # type: ignore[type-arg] ... @overload def _deepcopy( self, x: SupportsItems, # type: ignore[type-arg] - memo: Optional[dict[int, Union[list, dict]]] = None, # type: ignore[type-arg] + memo: dict[int, list | dict] | None = None, # type: ignore[type-arg] ) -> dict: # type: ignore[type-arg] ... def _deepcopy( self, - x: Union[Iterable, SupportsItems], # type: ignore[type-arg] - memo: Optional[dict[int, Union[list, dict]]] = None, # type: ignore[type-arg] - ) -> Union[list[Any], dict[str, Any]]: + x: Iterable | SupportsItems, # type: ignore[type-arg] + memo: dict[int, list | dict] | None = None, # type: ignore[type-arg] + ) -> list[Any] | dict[str, Any]: """Deepcopy helper for the data dictionary or list. Regular expressions cannot be deep copied but as they are immutable we don't have to copy them when cloning. """ - y: Union[list[Any], dict[str, Any]] + y: list[Any] | dict[str, Any] iterator: Iterable[tuple[Any, Any]] if not hasattr(x, "items"): y, is_list, iterator = [], True, enumerate(x) diff --git a/pymongo/daemon.py b/pymongo/daemon.py index 90e8fa42fc..65cf6a3248 100644 --- a/pymongo/daemon.py +++ b/pymongo/daemon.py @@ -26,14 +26,14 @@ import sys import warnings from collections.abc import Sequence -from typing import Any, Optional +from typing import Any # The maximum amount of time to wait for the intermediate subprocess. _WAIT_TIMEOUT = 10 _THIS_FILE = os.path.realpath(__file__) -def _popen_wait(popen: subprocess.Popen[Any], timeout: Optional[float]) -> Optional[int]: +def _popen_wait(popen: subprocess.Popen[Any], timeout: float | None) -> int | None: """Implement wait timeout support for Python 3.""" try: return popen.wait(timeout=timeout) @@ -42,7 +42,7 @@ def _popen_wait(popen: subprocess.Popen[Any], timeout: Optional[float]) -> Optio return None -def _silence_resource_warning(popen: Optional[subprocess.Popen[Any]]) -> None: +def _silence_resource_warning(popen: subprocess.Popen[Any] | None) -> None: """Silence Popen's ResourceWarning. Note this should only be used if the process was created as a daemon. @@ -91,7 +91,7 @@ def _spawn_daemon(args: Sequence[str]) -> None: # to be safe to call from any thread. Using Popen instead of fork also # avoids triggering the application's os.register_at_fork() callbacks when # we spawn the mongocryptd daemon process. - def _spawn(args: Sequence[str]) -> Optional[subprocess.Popen[Any]]: + def _spawn(args: Sequence[str]) -> subprocess.Popen[Any] | None: """Spawn the process and silence stdout/stderr.""" try: with open(os.devnull, "r+b") as devnull: diff --git a/pymongo/driver_info.py b/pymongo/driver_info.py index a325ea1493..518347aa01 100644 --- a/pymongo/driver_info.py +++ b/pymongo/driver_info.py @@ -20,7 +20,6 @@ from __future__ import annotations from collections import namedtuple -from typing import Optional class DriverInfo(namedtuple("DriverInfo", ["name", "version", "platform"])): @@ -40,7 +39,7 @@ class DriverInfo(namedtuple("DriverInfo", ["name", "version", "platform"])): """ def __new__( - cls, name: str, version: Optional[str] = None, platform: Optional[str] = None + cls, name: str, version: str | None = None, platform: str | None = None ) -> DriverInfo: self = super().__new__(cls, name, version, platform) for key, value in self._asdict().items(): diff --git a/pymongo/encryption_options.py b/pymongo/encryption_options.py index fdabecbbcb..bdaf5caae0 100644 --- a/pymongo/encryption_options.py +++ b/pymongo/encryption_options.py @@ -21,7 +21,7 @@ import warnings from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, Callable, Optional, TypedDict +from typing import TYPE_CHECKING, Any, Callable, TypedDict from pymongo.uri_parser_shared import _parse_kms_tls_options @@ -69,20 +69,20 @@ def __init__( self, kms_providers: Mapping[str, Any], key_vault_namespace: str, - key_vault_client: Optional[_AgnosticMongoClient] = None, - schema_map: Optional[Mapping[str, Any]] = None, + key_vault_client: _AgnosticMongoClient | None = None, + schema_map: Mapping[str, Any] | None = None, bypass_auto_encryption: bool = False, mongocryptd_uri: str = "mongodb://localhost:27020", mongocryptd_bypass_spawn: bool = False, mongocryptd_spawn_path: str = "mongocryptd", - mongocryptd_spawn_args: Optional[list[str]] = None, - kms_tls_options: Optional[Mapping[str, Any]] = None, - crypt_shared_lib_path: Optional[str] = None, + mongocryptd_spawn_args: list[str] | None = None, + kms_tls_options: Mapping[str, Any] | None = None, + crypt_shared_lib_path: str | None = None, crypt_shared_lib_required: bool = False, bypass_query_analysis: bool = False, - encrypted_fields_map: Optional[Mapping[str, Any]] = None, - key_expiration_ms: Optional[int] = None, - kms_connect_callback: Optional[Callable[[KMSConnectContext], Any]] = None, + encrypted_fields_map: Mapping[str, Any] | None = None, + key_expiration_ms: int | None = None, + kms_connect_callback: Callable[[KMSConnectContext], Any] | None = None, ) -> None: """Options to configure automatic client-side field level encryption. @@ -274,8 +274,8 @@ def __init__( self._mongocryptd_spawn_args.append("--idleShutdownTimeoutSecs=60") # Maps KMS provider name to a SSLContext. self._kms_tls_options = kms_tls_options - self._sync_kms_ssl_contexts: Optional[dict[str, SSLContext]] = None - self._async_kms_ssl_contexts: Optional[dict[str, SSLContext]] = None + self._sync_kms_ssl_contexts: dict[str, SSLContext] | None = None + self._async_kms_ssl_contexts: dict[str, SSLContext] | None = None self._bypass_query_analysis = bypass_query_analysis self._key_expiration_ms = key_expiration_ms if kms_connect_callback is not None and not callable(kms_connect_callback): @@ -300,11 +300,11 @@ class RangeOpts: def __init__( self, - sparsity: Optional[int] = None, - trim_factor: Optional[int] = None, - min: Optional[Any] = None, - max: Optional[Any] = None, - precision: Optional[int] = None, + sparsity: int | None = None, + trim_factor: int | None = None, + min: Any | None = None, + max: Any | None = None, + precision: int | None = None, ) -> None: """Options to configure encrypted queries using the range algorithm. @@ -342,11 +342,11 @@ class StringOpts: def __init__( self, - substring: Optional[SubstringOpts] = None, - prefix: Optional[PrefixOpts] = None, - suffix: Optional[SuffixOpts] = None, - case_sensitive: Optional[bool] = None, - diacritic_sensitive: Optional[bool] = None, + substring: SubstringOpts | None = None, + prefix: PrefixOpts | None = None, + suffix: SuffixOpts | None = None, + case_sensitive: bool | None = None, + diacritic_sensitive: bool | None = None, ) -> None: """Options to configure encrypted queries using the string algorithm. diff --git a/pymongo/encryption_shared.py b/pymongo/encryption_shared.py index 08c65510d0..8d9ad1d170 100644 --- a/pymongo/encryption_shared.py +++ b/pymongo/encryption_shared.py @@ -19,7 +19,7 @@ import contextlib import enum from collections.abc import Iterator -from typing import Any, Optional +from typing import Any try: from pymongocrypt.mongocrypt import MongoCryptOptions # type:ignore[import-untyped] @@ -51,11 +51,11 @@ class RewrapManyDataKeyResult: .. versionadded:: 4.2 """ - def __init__(self, bulk_write_result: Optional[BulkWriteResult] = None) -> None: + def __init__(self, bulk_write_result: BulkWriteResult | None = None) -> None: self._bulk_write_result = bulk_write_result @property - def bulk_write_result(self) -> Optional[BulkWriteResult]: + def bulk_write_result(self) -> BulkWriteResult | None: """The result of the bulk write operation used to update the key vault collection with one or more rewrapped data keys. If :meth:`~pymongo.encryption.ClientEncryption.rewrap_many_data_key` or :meth:`~pymongo.asynchronous.encryption.AsyncClientEncryption.rewrap_many_data_key` does not diff --git a/pymongo/errors.py b/pymongo/errors.py index e66d53cd5f..cc643ae810 100644 --- a/pymongo/errors.py +++ b/pymongo/errors.py @@ -21,7 +21,7 @@ from collections.abc import Iterable, Mapping, Sequence from ssl import SSLCertVerificationError as _CertificateError # noqa: F401 -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any from bson.errors import InvalidDocument @@ -33,7 +33,7 @@ class PyMongoError(Exception): """Base class for all PyMongo exceptions.""" - def __init__(self, message: str = "", error_labels: Optional[Iterable[str]] = None) -> None: + def __init__(self, message: str = "", error_labels: Iterable[str] | None = None) -> None: super().__init__(message) self._message = message self._error_labels = set(error_labels or []) @@ -96,11 +96,11 @@ class AutoReconnect(ConnectionFailure): Subclass of :exc:`~pymongo.errors.ConnectionFailure`. """ - errors: Union[Mapping[str, Any], Sequence[Any]] - details: Union[Mapping[str, Any], Sequence[Any]] + errors: Mapping[str, Any] | Sequence[Any] + details: Mapping[str, Any] | Sequence[Any] def __init__( - self, message: str = "", errors: Optional[Union[Mapping[str, Any], Sequence[Any]]] = None + self, message: str = "", errors: Mapping[str, Any] | Sequence[Any] | None = None ) -> None: error_labels = None if errors is not None: @@ -124,9 +124,7 @@ def timeout(self) -> bool: return True -def _format_detailed_error( - message: str, details: Optional[Union[Mapping[str, Any], list[Any]]] -) -> str: +def _format_detailed_error(message: str, details: Mapping[str, Any] | list[Any] | None) -> str: if details is not None: message = f"{message}, full error: {details}" return message @@ -149,7 +147,7 @@ class NotPrimaryError(AutoReconnect): """ def __init__( - self, message: str = "", errors: Optional[Union[Mapping[str, Any], list[Any]]] = None + self, message: str = "", errors: Mapping[str, Any] | list[Any] | None = None ) -> None: super().__init__(_format_detailed_error(message, errors), errors=errors) @@ -185,9 +183,9 @@ class OperationFailure(PyMongoError): def __init__( self, error: str, - code: Optional[int] = None, - details: Optional[Mapping[str, Any]] = None, - max_wire_version: Optional[int] = None, + code: int | None = None, + details: Mapping[str, Any] | None = None, + max_wire_version: int | None = None, ) -> None: error_labels = None base_backoff_ms = None @@ -201,16 +199,16 @@ def __init__( self.__base_backoff_ms = base_backoff_ms @property - def _max_wire_version(self) -> Optional[int]: + def _max_wire_version(self) -> int | None: return self.__max_wire_version @property - def code(self) -> Optional[int]: + def code(self) -> int | None: """The error code returned by the server, if any.""" return self.__code @property - def details(self) -> Optional[Mapping[str, Any]]: + def details(self) -> Mapping[str, Any] | None: """The complete error document returned by the server. Depending on the error that occurred, the error document @@ -226,7 +224,7 @@ def timeout(self) -> bool: return self.__code in (50,) @property - def _base_backoff_ms(self) -> Optional[float]: + def _base_backoff_ms(self) -> float | None: return self.__base_backoff_ms @@ -330,7 +328,7 @@ def __reduce__(self) -> tuple[Any, Any]: return self.__class__, (self.details,) @property - def error(self) -> Optional[Any]: + def error(self) -> Any | None: """A top-level error that occurred when attempting to communicate with the server or execute the bulk write. @@ -340,7 +338,7 @@ def error(self) -> Optional[Any]: return self.details.get("error", None) @property - def write_concern_errors(self) -> Optional[list[WriteConcernError]]: + def write_concern_errors(self) -> list[WriteConcernError] | None: """Write concern errors that occurred during the bulk write. This list may have multiple items if more than one @@ -349,7 +347,7 @@ def write_concern_errors(self) -> Optional[list[WriteConcernError]]: return self.details.get("writeConcernErrors", []) @property - def write_errors(self) -> Optional[list[WriteError]]: + def write_errors(self) -> list[WriteError] | None: """Errors that occurred during the execution of individual write operations. This list will contain at most one entry if the bulk write was ordered. @@ -357,7 +355,7 @@ def write_errors(self) -> Optional[list[WriteError]]: return self.details.get("writeErrors", {}) @property - def partial_result(self) -> Optional[ClientBulkWriteResult]: + def partial_result(self) -> ClientBulkWriteResult | None: """The results of any successful operations that were performed before the error was encountered. """ diff --git a/pymongo/hello.py b/pymongo/hello.py index a6519eed8d..e6e97bfbae 100644 --- a/pymongo/hello.py +++ b/pymongo/hello.py @@ -20,7 +20,7 @@ import datetime import itertools from collections.abc import Mapping -from typing import Any, Generic, Optional +from typing import Any, Generic from bson.objectid import ObjectId from pymongo import common @@ -117,7 +117,7 @@ def tags(self) -> Mapping[str, Any]: return self._doc.get("tags", {}) @property - def primary(self) -> Optional[tuple[str, int]]: + def primary(self) -> tuple[str, int] | None: """This server's opinion about who the primary is, or None.""" if self._doc.get("primary"): return common.partition_node(self._doc["primary"]) @@ -125,7 +125,7 @@ def primary(self) -> Optional[tuple[str, int]]: return None @property - def replica_set_name(self) -> Optional[str]: + def replica_set_name(self) -> str | None: """Replica set name or None.""" return self._doc.get("setName") @@ -150,19 +150,19 @@ def max_wire_version(self) -> int: return self._doc.get("maxWireVersion", common.MAX_WIRE_VERSION) @property - def set_version(self) -> Optional[int]: + def set_version(self) -> int | None: return self._doc.get("setVersion") @property - def election_id(self) -> Optional[ObjectId]: + def election_id(self) -> ObjectId | None: return self._doc.get("electionId") @property - def cluster_time(self) -> Optional[ClusterTime]: + def cluster_time(self) -> ClusterTime | None: return self._doc.get("$clusterTime") @property - def logical_session_timeout_minutes(self) -> Optional[int]: + def logical_session_timeout_minutes(self) -> int | None: return self._doc.get("logicalSessionTimeoutMinutes") @property @@ -174,18 +174,18 @@ def is_readable(self) -> bool: return self._is_readable @property - def me(self) -> Optional[tuple[str, int]]: + def me(self) -> tuple[str, int] | None: me = self._doc.get("me") if me: return common.clean_node(me) return None @property - def last_write_date(self) -> Optional[datetime.datetime]: + def last_write_date(self) -> datetime.datetime | None: return self._doc.get("lastWrite", {}).get("lastWriteDate") @property - def compressors(self) -> Optional[list[str]]: + def compressors(self) -> list[str] | None: return self._doc.get("compression") @property @@ -201,12 +201,12 @@ def sasl_supported_mechs(self) -> list[str]: return self._doc.get("saslSupportedMechs", []) @property - def speculative_authenticate(self) -> Optional[Mapping[str, Any]]: + def speculative_authenticate(self) -> Mapping[str, Any] | None: """The speculativeAuthenticate field.""" return self._doc.get("speculativeAuthenticate") @property - def topology_version(self) -> Optional[Mapping[str, Any]]: + def topology_version(self) -> Mapping[str, Any] | None: return self._doc.get("topologyVersion") @property @@ -214,7 +214,7 @@ def awaitable(self) -> bool: return self._awaitable @property - def service_id(self) -> Optional[ObjectId]: + def service_id(self) -> ObjectId | None: return self._doc.get("serviceId") @property @@ -222,5 +222,5 @@ def hello_ok(self) -> bool: return self._doc.get("helloOk", False) @property - def connection_id(self) -> Optional[int]: + def connection_id(self) -> int | None: return self._doc.get("connectionId") diff --git a/pymongo/helpers_shared.py b/pymongo/helpers_shared.py index 15792972ac..0a59f1a760 100644 --- a/pymongo/helpers_shared.py +++ b/pymongo/helpers_shared.py @@ -26,8 +26,6 @@ TYPE_CHECKING, Any, NoReturn, - Optional, - Union, ) from pymongo import ASCENDING @@ -128,7 +126,7 @@ class _RetryPolicy: def __init__( self, - attempts: Optional[int] = None, + attempts: int | None = None, backoff_initial: float = _BACKOFF_INITIAL, backoff_max: float = _BACKOFF_MAX, ): @@ -139,7 +137,7 @@ def __init__( self.backoff_initial = backoff_initial self.backoff_max = backoff_max - def backoff(self, attempt: int, base_backoff: Optional[float] = None) -> float: + def backoff(self, attempt: int, base_backoff: float | None = None) -> float: """Return the actual backoff duration for the given attempt and base backoff.""" return _backoff( max(0, attempt), @@ -177,7 +175,7 @@ def _get_timeout_details(options: PoolOptions) -> dict[str, float]: return details -def format_timeout_details(details: Optional[dict[str, float]]) -> str: +def format_timeout_details(details: dict[str, float] | None) -> str: result = "" if details: result += " (configured timeouts:" @@ -195,8 +193,8 @@ def _gen_index_name(keys: _IndexList) -> str: def _index_list( - key_or_list: _Hint, direction: Optional[Union[int, str]] = None -) -> Sequence[tuple[str, Union[int, str, Mapping[str, Any]]]]: + key_or_list: _Hint, direction: int | str | None = None +) -> Sequence[tuple[str, int | str | Mapping[str, Any]]]: """Helper to generate a list of (key, direction) pairs. Takes such a list, or a single key, or a single key and direction. @@ -266,10 +264,10 @@ def _validate_index_key_pair(key: Any, value: Any) -> None: def _check_command_response( response: _DocumentOut, - max_wire_version: Optional[int], - allowable_errors: Optional[Container[Union[int, str]]] = None, + max_wire_version: int | None, + allowable_errors: Container[int | str] | None = None, parse_write_concern_error: bool = False, - pool_opts: Optional[PoolOptions] = None, + pool_opts: PoolOptions | None = None, ) -> None: """Check the response to a command for errors.""" if "ok" not in response: @@ -343,7 +341,7 @@ def _raise_write_concern_error(error: Any) -> NoReturn: raise WriteConcernError(error.get("errmsg"), error.get("code"), error) -def _get_wce_doc(result: Mapping[str, Any]) -> Optional[Mapping[str, Any]]: +def _get_wce_doc(result: Mapping[str, Any]) -> Mapping[str, Any] | None: """Return the writeConcernError or None.""" wce = result.get("writeConcernError") if wce: @@ -370,7 +368,7 @@ def _check_write_command_response(result: Mapping[str, Any]) -> None: def _fields_list_to_dict( - fields: Union[Mapping[str, Any], Iterable[str]], option_name: str + fields: Mapping[str, Any] | Iterable[str], option_name: str ) -> Mapping[str, Any]: """Takes a sequence of field names and returns a matching dictionary. diff --git a/pymongo/lock.py b/pymongo/lock.py index ad990fce3f..eabe82f38e 100644 --- a/pymongo/lock.py +++ b/pymongo/lock.py @@ -22,7 +22,7 @@ import threading import weakref from asyncio import wait_for -from typing import Any, Optional, TypeVar +from typing import Any, TypeVar import pymongo._asyncio_lock @@ -59,7 +59,7 @@ def _async_create_lock() -> Lock: def _create_condition( - lock: threading.Lock, condition_class: Optional[Any] = None + lock: threading.Lock, condition_class: Any | None = None ) -> threading.Condition: """Represents a threading.Condition.""" if condition_class: @@ -67,7 +67,7 @@ def _create_condition( return threading.Condition(lock) -def _async_create_condition(lock: Lock, condition_class: Optional[Any] = None) -> Condition: +def _async_create_condition(lock: Lock, condition_class: Any | None = None) -> Condition: """Represents an asyncio.Condition.""" if condition_class: return condition_class(lock) @@ -81,12 +81,12 @@ def _release_locks() -> None: lock.release() -async def _async_cond_wait(condition: Condition, timeout: Optional[float]) -> bool: +async def _async_cond_wait(condition: Condition, timeout: float | None) -> bool: try: return await wait_for(condition.wait(), timeout) except asyncio.TimeoutError: return False -def _cond_wait(condition: threading.Condition, timeout: Optional[float]) -> bool: +def _cond_wait(condition: threading.Condition, timeout: float | None) -> bool: return condition.wait(timeout) diff --git a/pymongo/message.py b/pymongo/message.py index be91726f37..b6eb7d61e2 100644 --- a/pymongo/message.py +++ b/pymongo/message.py @@ -31,8 +31,6 @@ Any, Callable, NoReturn, - Optional, - Union, ) import bson @@ -171,15 +169,15 @@ def _convert_client_bulk_exception(exception: Exception) -> dict[str, Any]: def _gen_find_command( coll: str, spec: Mapping[str, Any], - projection: Optional[Union[Mapping[str, Any], Iterable[str]]], + projection: Mapping[str, Any] | Iterable[str] | None, skip: int, limit: int, - batch_size: Optional[int], - options: Optional[int], + batch_size: int | None, + options: int | None, read_concern: ReadConcern, - collation: Optional[Mapping[str, Any]] = None, - session: Optional[_AgnosticClientSession] = None, - allow_disk_use: Optional[bool] = None, + collation: Mapping[str, Any] | None = None, + session: _AgnosticClientSession | None = None, + allow_disk_use: bool | None = None, ) -> dict[str, Any]: """Generate a find command document.""" cmd: dict[str, Any] = {"find": coll} @@ -224,11 +222,11 @@ def _gen_find_command( def _gen_get_more_command( - cursor_id: Optional[int], + cursor_id: int | None, coll: str, - batch_size: Optional[int], - max_await_time_ms: Optional[int], - comment: Optional[Any], + batch_size: int | None, + max_await_time_ms: int | None, + comment: Any | None, ) -> dict[str, Any]: """Generate a getMore command document.""" cmd: dict[str, Any] = {"getMore": cursor_id, "collection": coll} @@ -246,7 +244,7 @@ def _gen_get_more_command( def _compress( - operation: int, data: bytes, ctx: Union[SnappyContext, ZlibContext, ZstdContext] + operation: int, data: bytes, ctx: SnappyContext | ZlibContext | ZstdContext ) -> tuple[int, bytes]: """Takes message data, compresses it, and adds an OP_COMPRESSED header.""" compressed = ctx.compress(data) @@ -286,7 +284,7 @@ def _op_msg_no_header( flags: int, command: Mapping[str, Any], identifier: str, - docs: Optional[list[Mapping[str, Any]]], + docs: list[Mapping[str, Any]] | None, opts: CodecOptions[Any], ) -> tuple[bytes, int, int]: """Get a OP_MSG message. @@ -318,9 +316,9 @@ def _op_msg_compressed( flags: int, command: Mapping[str, Any], identifier: str, - docs: Optional[list[Mapping[str, Any]]], + docs: list[Mapping[str, Any]] | None, opts: CodecOptions[Any], - ctx: Union[SnappyContext, ZlibContext, ZstdContext], + ctx: SnappyContext | ZlibContext | ZstdContext, ) -> tuple[int, bytes, int, int]: """Internal OP_MSG message helper.""" msg, total_size, max_bson_size = _op_msg_no_header(flags, command, identifier, docs, opts) @@ -332,7 +330,7 @@ def _op_msg_uncompressed( flags: int, command: Mapping[str, Any], identifier: str, - docs: Optional[list[Mapping[str, Any]]], + docs: list[Mapping[str, Any]] | None, opts: CodecOptions[Any], ) -> tuple[int, bytes, int, int]: """Internal compressed OP_MSG message helper.""" @@ -349,9 +347,9 @@ def _op_msg( flags: int, command: MutableMapping[str, Any], dbname: str, - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, opts: CodecOptions[Any], - ctx: Union[SnappyContext, ZlibContext, ZstdContext, None] = None, + ctx: SnappyContext | ZlibContext | ZstdContext | None = None, ) -> tuple[int, bytes, int, int]: """Get a OP_MSG message.""" command["$db"] = dbname @@ -408,9 +406,9 @@ def __init__( database_name: str, cmd_name: str, conn: _AgnosticConnection, - operation_id: Optional[int], + operation_id: int | None, listeners: _EventListeners, - session: Optional[_AgnosticClientSession], + session: _AgnosticClientSession | None, op_type: int, codec: CodecOptions[Any], ): @@ -459,9 +457,9 @@ def __init__( database_name: str, cmd_name: str, conn: _AgnosticConnection, - operation_id: Optional[int], + operation_id: int | None, listeners: _EventListeners, - session: Optional[_AgnosticClientSession], + session: _AgnosticClientSession | None, op_type: int, codec: CodecOptions[Any], ): @@ -478,7 +476,7 @@ def __init__( def batch_command( self, cmd: MutableMapping[str, Any], docs: list[Mapping[str, Any]] - ) -> tuple[int, Union[bytes, dict[str, Any]], list[Mapping[str, Any]]]: + ) -> tuple[int, bytes | dict[str, Any], list[Mapping[str, Any]]]: namespace = self.db_name + ".$cmd" request_id, msg, to_send = _do_batched_op_msg( namespace, self.op_type, cmd, docs, self.codec, self @@ -702,9 +700,9 @@ def __init__( database_name: str, cmd_name: str, conn: _AgnosticConnection, - operation_id: Optional[int], + operation_id: int | None, listeners: _EventListeners, - session: Optional[_AgnosticClientSession], + session: _AgnosticClientSession | None, codec: CodecOptions[Any], ): super().__init__( @@ -723,7 +721,7 @@ def batch_command( cmd: MutableMapping[str, Any], operations: list[tuple[str, Mapping[str, Any]]], namespaces: list[str], - ) -> tuple[int, Union[bytes, dict[str, Any]], list[Mapping[str, Any]], list[Mapping[str, Any]]]: + ) -> tuple[int, bytes | dict[str, Any], list[Mapping[str, Any]], list[Mapping[str, Any]]]: request_id, msg, to_send_ops, to_send_ns = _client_do_batched_op_msg( cmd, operations, namespaces, self.codec, self ) @@ -1101,8 +1099,8 @@ def __init__(self, flags: int, payload_document: bytes | memoryview): def raw_response( self, - cursor_id: Optional[int] = None, - user_fields: Optional[Mapping[str, Any]] = {}, + cursor_id: int | None = None, + user_fields: Mapping[str, Any] | None = {}, ) -> list[Mapping[str, Any]]: """ cursor_id is ignored @@ -1115,9 +1113,9 @@ def raw_response( def unpack_response( self, - cursor_id: Optional[int] = None, + cursor_id: int | None = None, codec_options: CodecOptions[Any] = _UNICODE_REPLACE_CODEC_OPTIONS, - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, legacy_response: bool = False, ) -> list[dict[str, Any]]: """Unpack a OP_MSG command response. @@ -1217,16 +1215,16 @@ def __init__( coll: str, ntoskip: int, spec: Mapping[str, Any], - fields: Optional[Mapping[str, Any]], + fields: Mapping[str, Any] | None, codec_options: CodecOptions[Any], read_preference: _ServerMode, limit: int, batch_size: int, read_concern: ReadConcern, - collation: Optional[Mapping[str, Any]], - session: Optional[_AgnosticClientSession], + collation: Mapping[str, Any] | None, + session: _AgnosticClientSession | None, client: _AgnosticMongoClient, - allow_disk_use: Optional[bool], + allow_disk_use: bool | None, exhaust: bool, ): self.flags = flags @@ -1245,7 +1243,7 @@ def __init__( self.client = client self.allow_disk_use = allow_disk_use self.name = "find" - self._as_command: Optional[tuple[dict[str, Any], str]] = None + self._as_command: tuple[dict[str, Any], str] | None = None self.exhaust = exhaust def reset(self) -> None: @@ -1351,9 +1349,9 @@ def __init__( cursor_id: int, codec_options: CodecOptions[Any], read_preference: _ServerMode, - session: Optional[_AgnosticClientSession], + session: _AgnosticClientSession | None, client: _AgnosticMongoClient, - max_await_time_ms: Optional[int], + max_await_time_ms: int | None, conn_mgr: Any, exhaust: bool, comment: Any, @@ -1368,7 +1366,7 @@ def __init__( self.client = client self.max_await_time_ms = max_await_time_ms self.conn_mgr = conn_mgr - self._as_command: Optional[tuple[dict[str, Any], str]] = None + self._as_command: tuple[dict[str, Any], str] | None = None self.exhaust = exhaust self.comment = comment diff --git a/pymongo/mongo_client_shared.py b/pymongo/mongo_client_shared.py index 2951af5db1..a8d44824b1 100644 --- a/pymongo/mongo_client_shared.py +++ b/pymongo/mongo_client_shared.py @@ -17,7 +17,7 @@ from __future__ import annotations from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, Callable, Generic, Optional, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar, cast from pymongo import _csot from pymongo._telemetry import _generate_op_id_or_none, log_command_retry @@ -54,7 +54,7 @@ _FuncT = TypeVar("_FuncT", bound=Callable[..., Any]) -def _retryable_error_doc(exc: PyMongoError) -> Optional[Mapping[str, Any]]: +def _retryable_error_doc(exc: PyMongoError) -> Mapping[str, Any] | None: """Return the server response from PyMongo exception or None.""" if isinstance(exc, (BulkWriteError, ClientBulkWriteException)): # Check the last writeConcernError to determine if this @@ -162,18 +162,18 @@ def __init__( self, mongo_client: _AgnosticMongoClient, func: _FuncT, - bulk: Optional[Union[_AgnosticBulk, _AgnosticClientBulk]], + bulk: _AgnosticBulk | _AgnosticClientBulk | None, operation: str, is_read: bool = False, - session: Optional[_AgnosticClientSession] = None, - read_pref: Optional[_ServerMode] = None, - address: Optional[_Address] = None, + session: _AgnosticClientSession | None = None, + read_pref: _ServerMode | None = None, + address: _Address | None = None, retryable: bool = False, - operation_id: Optional[int] = None, + operation_id: int | None = None, is_run_command: bool = False, is_aggregate_write: bool = False, ): - self._last_error: Optional[Exception] = None + self._last_error: Exception | None = None self._retrying = False self._always_retryable = False self._max_retries = float("inf") if _csot.get_timeout() is not None else 1 @@ -190,7 +190,7 @@ def __init__( ) self._address = address self._server: _ServerT = None # type: ignore - self._deprioritized_servers: Optional[list[_ServerT]] = None + self._deprioritized_servers: list[_ServerT] | None = None self._operation = operation # Only generate an operation id when APM/logging is enabled. if operation_id is None: @@ -199,7 +199,7 @@ def __init__( self._attempt_number = 0 self._is_run_command = is_run_command self._is_aggregate_write = is_aggregate_write - self._base_backoff_ms: Optional[float] = None + self._base_backoff_ms: float | None = None def _is_not_eligible_for_retry(self) -> bool: """Checks if the exchange is not eligible for retry""" diff --git a/pymongo/monitoring.py b/pymongo/monitoring.py index 252b0daef0..57bd34d08c 100644 --- a/pymongo/monitoring.py +++ b/pymongo/monitoring.py @@ -191,7 +191,7 @@ def connection_checked_in(self, event): import datetime from collections import abc, namedtuple from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from bson.objectid import ObjectId from pymongo.hello import Hello, HelloCompat @@ -541,10 +541,10 @@ def __init__( command_name: str, request_id: int, connection_id: _Address, - operation_id: Optional[int], - service_id: Optional[ObjectId] = None, + operation_id: int | None, + service_id: ObjectId | None = None, database_name: str = "", - server_connection_id: Optional[int] = None, + server_connection_id: int | None = None, ) -> None: self.__cmd_name = command_name self.__rqst_id = request_id @@ -570,7 +570,7 @@ def connection_id(self) -> _Address: return self.__conn_id @property - def service_id(self) -> Optional[ObjectId]: + def service_id(self) -> ObjectId | None: """The service_id this command was sent to, or ``None``. .. versionadded:: 3.12 @@ -578,7 +578,7 @@ def service_id(self) -> Optional[ObjectId]: return self.__service_id @property - def operation_id(self) -> Optional[int]: + def operation_id(self) -> int | None: """An id for this series of events or None.""" return self.__op_id @@ -591,7 +591,7 @@ def database_name(self) -> str: return self.__db @property - def server_connection_id(self) -> Optional[int]: + def server_connection_id(self) -> int | None: """The server-side connection id for the connection this command was sent on, or ``None``. .. versionadded:: 4.7 @@ -619,9 +619,9 @@ def __init__( database_name: str, request_id: int, connection_id: _Address, - operation_id: Optional[int], - service_id: Optional[ObjectId] = None, - server_connection_id: Optional[int] = None, + operation_id: int | None, + service_id: ObjectId | None = None, + server_connection_id: int | None = None, ) -> None: if not command: raise ValueError(f"{command!r} is not a valid command") @@ -679,10 +679,10 @@ def __init__( command_name: str, request_id: int, connection_id: _Address, - operation_id: Optional[int], - service_id: Optional[ObjectId] = None, + operation_id: int | None, + service_id: ObjectId | None = None, database_name: str = "", - server_connection_id: Optional[int] = None, + server_connection_id: int | None = None, ) -> None: super().__init__( command_name, @@ -737,10 +737,10 @@ def __init__( command_name: str, request_id: int, connection_id: _Address, - operation_id: Optional[int], - service_id: Optional[ObjectId] = None, + operation_id: int | None, + service_id: ObjectId | None = None, database_name: str = "", - server_connection_id: Optional[int] = None, + server_connection_id: int | None = None, ) -> None: super().__init__( command_name, @@ -842,7 +842,7 @@ class PoolClearedEvent(_PoolEvent): def __init__( self, address: _Address, - service_id: Optional[ObjectId] = None, + service_id: ObjectId | None = None, interrupt_connections: bool = False, ) -> None: super().__init__(address) @@ -850,7 +850,7 @@ def __init__( self.__interrupt_connections = interrupt_connections @property - def service_id(self) -> Optional[ObjectId]: + def service_id(self) -> ObjectId | None: """Connections with this service_id are cleared. When service_id is ``None``, all connections in the pool are cleared. @@ -965,12 +965,12 @@ class _ConnectionDurationEvent(_ConnectionIdEvent): __slots__ = ("__duration",) - def __init__(self, address: _Address, connection_id: int, duration: Optional[float]) -> None: + def __init__(self, address: _Address, connection_id: int, duration: float | None) -> None: super().__init__(address, connection_id) self.__duration = duration @property - def duration(self) -> Optional[float]: + def duration(self) -> float | None: """The duration of the connection event. .. versionadded:: 4.7 @@ -1064,7 +1064,7 @@ class ConnectionCheckOutFailedEvent(_ConnectionDurationEvent): __slots__ = ("__reason",) - def __init__(self, address: _Address, reason: str, duration: Optional[float]) -> None: + def __init__(self, address: _Address, reason: str, duration: float | None) -> None: super().__init__(address=address, connection_id=0, duration=duration) self.__reason = reason @@ -1387,7 +1387,7 @@ class _EventListeners: :param listeners: A list of event listeners. """ - def __init__(self, listeners: Optional[Sequence[_EventListener]]): + def __init__(self, listeners: Sequence[_EventListener] | None): self.__command_listeners = _LISTENERS.command_listeners[:] self.__server_listeners = _LISTENERS.server_listeners[:] lst = _LISTENERS.server_heartbeat_listeners @@ -1453,9 +1453,9 @@ def publish_command_start( database_name: str, request_id: int, connection_id: _Address, - server_connection_id: Optional[int], - op_id: Optional[int] = None, - service_id: Optional[ObjectId] = None, + server_connection_id: int | None, + op_id: int | None = None, + service_id: ObjectId | None = None, ) -> None: """Publish a CommandStartedEvent to all command listeners. @@ -1492,9 +1492,9 @@ def publish_command_success( command_name: str, request_id: int, connection_id: _Address, - server_connection_id: Optional[int], - op_id: Optional[int] = None, - service_id: Optional[ObjectId] = None, + server_connection_id: int | None, + op_id: int | None = None, + service_id: ObjectId | None = None, speculative_hello: bool = False, database_name: str = "", ) -> None: @@ -1541,9 +1541,9 @@ def publish_command_failure( command_name: str, request_id: int, connection_id: _Address, - server_connection_id: Optional[int], - op_id: Optional[int] = None, - service_id: Optional[ObjectId] = None, + server_connection_id: int | None, + op_id: int | None = None, + service_id: ObjectId | None = None, database_name: str = "", ) -> None: """Publish a CommandFailedEvent to all command listeners. @@ -1749,7 +1749,7 @@ def publish_pool_ready(self, address: _Address) -> None: def publish_pool_cleared( self, address: _Address, - service_id: Optional[ObjectId], + service_id: ObjectId | None, interrupt_connections: bool = False, ) -> None: """Publish a :class:`PoolClearedEvent` to all pool listeners.""" diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 3f5afc1262..8abf7b6ec6 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -28,8 +28,6 @@ TYPE_CHECKING, Any, Callable, - Optional, - Union, ) from pymongo import _csot, ssl_support @@ -73,7 +71,7 @@ # These socket-based I/O methods are for KMS requests and any other network operations that do not use # the MongoDB wire protocol -async def async_socket_sendall(sock: Union[socket.socket, _sslConn], buf: bytes) -> None: +async def async_socket_sendall(sock: socket.socket | _sslConn, buf: bytes) -> None: timeout = sock.gettimeout() loop = asyncio.get_running_loop() try: @@ -95,7 +93,7 @@ async def async_socket_sendall(sock: Union[socket.socket, _sslConn], buf: bytes) if sys.platform != "win32": async def _async_socket_receive_ssl( - conn: _sslConn, length: int, loop: AbstractEventLoop, once: Optional[bool] = False + conn: _sslConn, length: int, loop: AbstractEventLoop, once: bool | None = False ) -> memoryview: mv = memoryview(bytearray(length)) total_read = 0 @@ -148,7 +146,7 @@ def _is_ready(fut: Future[Any]) -> None: # The default Windows asyncio event loop does not support loop.add_reader/add_writer: # https://docs.python.org/3/library/asyncio-platforms.html#asyncio-platform-support async def _async_socket_receive_ssl( - conn: _sslConn, length: int, dummy: AbstractEventLoop, once: Optional[bool] = False + conn: _sslConn, length: int, dummy: AbstractEventLoop, once: bool | None = False ) -> memoryview: mv = memoryview(bytearray(length)) total_read = 0 @@ -184,7 +182,7 @@ async def _async_blocking_socket_call( loop: AbstractEventLoop, func: Callable[..., Any], arg: Any, - timeout: Optional[float], + timeout: float | None, ) -> Any: """Run a blocking socket operation in a worker thread with a timeout. @@ -213,7 +211,7 @@ async def _async_blocking_socket_call( raise OSError("connection closed") from exc -def sendall(sock: Union[socket.socket, _sslConn], buf: bytes) -> None: +def sendall(sock: socket.socket | _sslConn, buf: bytes) -> None: """Send all of buf on the socket.""" sock.sendall(buf) @@ -227,9 +225,7 @@ async def _poll_cancellation(conn: AsyncConnection) -> None: await asyncio.sleep(_POLL_TIMEOUT) -async def async_receive_data_socket( - sock: Union[socket.socket, _sslConn], length: int -) -> memoryview: +async def async_receive_data_socket(sock: socket.socket | _sslConn, length: int) -> memoryview: """Receive exactly length bytes from the socket.""" timeout = sock.gettimeout() sock.settimeout(0.0) @@ -268,7 +264,7 @@ async def _async_socket_receive( _WINDOWS = sys.platform == "win32" -def wait_for_read(conn: Connection, deadline: Optional[float]) -> None: +def wait_for_read(conn: Connection, deadline: float | None) -> None: """Block until at least one byte is read, or a timeout, or a cancel.""" sock = conn.conn.sock timed_out = False @@ -301,7 +297,7 @@ def wait_for_read(conn: Connection, deadline: Optional[float]) -> None: raise socket.timeout("timed out") -def receive_data(conn: Connection, length: int, deadline: Optional[float]) -> memoryview: +def receive_data(conn: Connection, length: int, deadline: float | None) -> memoryview: buf = bytearray(length) mv = memoryview(buf) bytes_read = 0 @@ -413,7 +409,7 @@ def sock(self) -> socket.socket: class NetworkingInterface(NetworkingInterfaceBase): - def __init__(self, conn: Union[socket.socket, _sslConn]): + def __init__(self, conn: socket.socket | _sslConn): super().__init__(conn) def gettimeout(self) -> float | None: @@ -429,11 +425,11 @@ def is_closing(self) -> bool: return self.conn.is_closing() @property - def get_conn(self) -> Union[socket.socket, _sslConn]: + def get_conn(self) -> socket.socket | _sslConn: return self.conn @property - def sock(self) -> Union[socket.socket, _sslConn]: + def sock(self) -> socket.socket | _sslConn: return self.conn def fileno(self) -> int: @@ -444,7 +440,7 @@ def recv_into(self, buffer: bytes | memoryview) -> int: class PyMongoProtocol(BufferedProtocol): - def __init__(self, timeout: Optional[float] = None): + def __init__(self, timeout: float | None = None): self.transport: Transport = None # type: ignore[assignment] # Each message is reader in 2-3 parts: header, compression header, and message body # The message buffer is allocated after the header is read. @@ -452,7 +448,7 @@ def __init__(self, timeout: Optional[float] = None): self._header_index = 0 self._compression_header = memoryview(bytearray(9)) self._compression_index = 0 - self._message: Optional[memoryview] = None + self._message: memoryview | None = None self._message_index = 0 # State. TODO: replace booleans with an enum? self._expecting_header = True @@ -460,12 +456,12 @@ def __init__(self, timeout: Optional[float] = None): self._message_size = 0 self._op_code = 0 self._connection_lost = False - self._read_waiter: Optional[Future[Any]] = None + self._read_waiter: Future[Any] | None = None self._timeout = timeout self._is_compressed = False - self._compressor_id: Optional[int] = None + self._compressor_id: int | None = None self._max_message_size = MAX_MESSAGE_SIZE - self._response_to: Optional[int] = None + self._response_to: int | None = None self._closed = asyncio.get_running_loop().create_future() self._pending_messages: collections.deque[Future[Any]] = collections.deque() self._done_messages: collections.deque[Future[Any]] = collections.deque() @@ -492,7 +488,7 @@ async def write(self, message: bytes) -> None: self.transport.write(message) self.transport.resume_reading() - async def read(self, request_id: Optional[int], max_message_size: int) -> tuple[bytes, int]: + async def read(self, request_id: int | None, max_message_size: int) -> tuple[bytes, int]: """Read a single MongoDB Wire Protocol message from this connection.""" if self.transport: try: @@ -649,7 +645,7 @@ def process_compression_header(self) -> tuple[int, int, int]: ) return op_code, uncompressed_size, compressor_id - def _resolve_pending_messages(self, exc: Optional[Exception] = None) -> None: + def _resolve_pending_messages(self, exc: Exception | None = None) -> None: pending = list(self._pending_messages) for msg in pending: if not msg.done(): @@ -659,12 +655,12 @@ def _resolve_pending_messages(self, exc: Optional[Exception] = None) -> None: msg.set_exception(exc) self._done_messages.append(msg) - def close(self, exc: Optional[Exception] = None) -> None: + def close(self, exc: Exception | None = None) -> None: self.transport.abort() self._resolve_pending_messages(exc) self._connection_lost = True - def connection_lost(self, exc: Optional[Exception] = None) -> None: + def connection_lost(self, exc: Exception | None = None) -> None: self._resolve_pending_messages(exc) if not self._closed.done(): self._closed.set_result(None) @@ -683,11 +679,11 @@ async def async_sendall(conn: PyMongoProtocol, buf: bytes) -> None: async def async_receive_message( conn: AsyncConnection, - request_id: Optional[int], + request_id: int | None, max_message_size: int = MAX_MESSAGE_SIZE, ) -> _OpMsg: """Receive a raw BSON message or raise socket.error.""" - timeout: Optional[Union[float, int]] + timeout: float | int | None timeout = conn.conn.gettimeout if _csot.get_timeout(): deadline = _csot.get_deadline() @@ -733,7 +729,7 @@ async def async_receive_message( def receive_message( - conn: Connection, request_id: Optional[int], max_message_size: int = MAX_MESSAGE_SIZE + conn: Connection, request_id: int | None, max_message_size: int = MAX_MESSAGE_SIZE ) -> _OpMsg: """Receive a raw BSON message or raise socket.error.""" if _csot.get_timeout(): diff --git a/pymongo/ocsp_cache.py b/pymongo/ocsp_cache.py index 2df232848f..afa0c50ea9 100644 --- a/pymongo/ocsp_cache.py +++ b/pymongo/ocsp_cache.py @@ -19,7 +19,7 @@ from collections import namedtuple from datetime import datetime as _datetime from datetime import timezone -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from pymongo.lock import _create_lock @@ -27,7 +27,7 @@ from cryptography.x509.ocsp import OCSPRequest, OCSPResponse -def _next_update(value: OCSPResponse) -> Optional[_datetime]: +def _next_update(value: OCSPResponse) -> _datetime | None: """Compat helper to return the response's next_update_utc.""" # Added in cryptography 43.0.0. if hasattr(value, "next_update_utc"): @@ -35,7 +35,7 @@ def _next_update(value: OCSPResponse) -> Optional[_datetime]: return value.next_update -def _this_update(value: OCSPResponse) -> Optional[_datetime]: +def _this_update(value: OCSPResponse) -> _datetime | None: """Compat helper to return the response's this_update_utc.""" # Added in cryptography 43.0.0. if hasattr(value, "this_update_utc"): diff --git a/pymongo/ocsp_support.py b/pymongo/ocsp_support.py index 7990c953b4..b73e87acbe 100644 --- a/pymongo/ocsp_support.py +++ b/pymongo/ocsp_support.py @@ -21,7 +21,7 @@ from collections.abc import Iterable from datetime import datetime as _datetime from datetime import timezone -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING from cryptography.exceptions import InvalidSignature as _InvalidSignature from cryptography.hazmat.backends import default_backend as _default_backend @@ -92,20 +92,20 @@ # Must stay in step with cryptography's CertificatePublicKeyTypes, the # return type of Certificate.public_key(). cryptography does not export a # public alias for it, so it is spelled out here. - CertificateIssuerPublicKeyTypes = Union[ - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - mldsa.MLDSA44PublicKey, - mldsa.MLDSA65PublicKey, - mldsa.MLDSA87PublicKey, - mlkem.MLKEM768PublicKey, - mlkem.MLKEM1024PublicKey, - x25519.X25519PublicKey, - x448.X448PublicKey, - ] + CertificateIssuerPublicKeyTypes = ( + dsa.DSAPublicKey + | rsa.RSAPublicKey + | ec.EllipticCurvePublicKey + | ed25519.Ed25519PublicKey + | ed448.Ed448PublicKey + | mldsa.MLDSA44PublicKey + | mldsa.MLDSA65PublicKey + | mldsa.MLDSA87PublicKey + | mlkem.MLKEM768PublicKey + | mlkem.MLKEM1024PublicKey + | x25519.X25519PublicKey + | x448.X448PublicKey + ) # Note: the functions in this module generally return 1 or 0. The reason # is simple. The entry point, ocsp_callback, is registered as a callback @@ -120,8 +120,8 @@ def _get_issuer_cert( - cert: Certificate, chain: Iterable[Certificate], trusted_ca_certs: Optional[list[Certificate]] -) -> Optional[Certificate]: + cert: Certificate, chain: Iterable[Certificate], trusted_ca_certs: list[Certificate] | None +) -> Certificate | None: issuer_name = cert.issuer for candidate in chain: if candidate.subject == issuer_name: @@ -141,7 +141,7 @@ def _get_issuer_cert( def _verify_signature( key: CertificateIssuerPublicKeyTypes, signature: bytes, - algorithm: Union[Prehashed, HashAlgorithm, None], + algorithm: Prehashed | HashAlgorithm | None, data: bytes, ) -> int: # See cryptography.x509.Certificate.public_key @@ -173,7 +173,7 @@ def _verify_signature( def _get_extension( cert: Certificate, klass: type[ExtensionTypeVar] -) -> Optional[Extension[ExtensionTypeVar]]: +) -> Extension[ExtensionTypeVar] | None: try: return cert.extensions.get_extension_for_class(klass) except _ExtensionNotFound: @@ -198,7 +198,7 @@ def _public_key_hash(cert: Certificate) -> bytes: def _get_certs_by_key_hash( - certificates: Iterable[Certificate], issuer: Certificate, responder_key_hash: Optional[bytes] + certificates: Iterable[Certificate], issuer: Certificate, responder_key_hash: bytes | None ) -> list[Certificate]: return [ cert @@ -208,7 +208,7 @@ def _get_certs_by_key_hash( def _get_certs_by_name( - certificates: Iterable[Certificate], issuer: Certificate, responder_name: Optional[Name] + certificates: Iterable[Certificate], issuer: Certificate, responder_name: Name | None ) -> list[Certificate]: return [ cert @@ -305,8 +305,8 @@ def _verify_response(issuer: Certificate, response: OCSPResponse) -> int: def _get_ocsp_response( - cert: Certificate, issuer: Certificate, uri: Union[str, bytes], ocsp_response_cache: _OCSPCache -) -> Optional[OCSPResponse]: + cert: Certificate, issuer: Certificate, uri: str | bytes, ocsp_response_cache: _OCSPCache +) -> OCSPResponse | None: ocsp_request = _build_ocsp_request(cert, issuer) try: ocsp_response = ocsp_response_cache[ocsp_request] @@ -349,7 +349,7 @@ def _get_ocsp_response( return ocsp_response -def _ocsp_callback(conn: Connection, ocsp_bytes: bytes, user_data: Optional[_CallbackData]) -> bool: +def _ocsp_callback(conn: Connection, ocsp_bytes: bytes, user_data: _CallbackData | None) -> bool: """Callback for use with OpenSSL.SSL.Context.set_ocsp_client_callback.""" # always pass in user_data but OpenSSL requires it be optional assert user_data diff --git a/pymongo/operations.py b/pymongo/operations.py index 99b6a00c52..fc28b4cc3a 100644 --- a/pymongo/operations.py +++ b/pymongo/operations.py @@ -25,8 +25,6 @@ TYPE_CHECKING, Any, Generic, - Optional, - Union, ) from bson.raw_bson import RawBSONDocument @@ -43,10 +41,8 @@ # Hint supports index name, "myIndex", a list of either strings or index pairs: [('x', 1), ('y', -1), 'z''], or a dictionary -_IndexList = Union[ - Sequence[Union[str, tuple[str, Union[int, str, Mapping[str, Any]]]]], Mapping[str, Any] -] -_IndexKeyHint = Union[str, _IndexList] +_IndexList = Sequence[str | tuple[str, int | str | Mapping[str, Any]]] | Mapping[str, Any] +_IndexKeyHint = str | _IndexList class _Op(str, enum.Enum): @@ -105,7 +101,7 @@ class InsertOne(Generic[_DocumentType]): "_namespace", ) - def __init__(self, document: _DocumentType, namespace: Optional[str] = None) -> None: + def __init__(self, document: _DocumentType, namespace: str | None = None) -> None: """Create an InsertOne instance. For use with :meth:`~pymongo.asynchronous.collection.AsyncCollection.bulk_write`, :meth:`~pymongo.collection.Collection.bulk_write`, @@ -165,14 +161,14 @@ class _DeleteOp: def __init__( self, filter: Mapping[str, Any], - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - namespace: Optional[str] = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + namespace: str | None = None, ) -> None: if filter is not None: validate_is_mapping("filter", filter) if hint is not None and not isinstance(hint, str): - self._hint: Union[str, dict[str, Any], None] = helpers_shared._index_document(hint) + self._hint: str | dict[str, Any] | None = helpers_shared._index_document(hint) else: self._hint = hint @@ -214,9 +210,9 @@ class DeleteOne(_DeleteOp): def __init__( self, filter: Mapping[str, Any], - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - namespace: Optional[str] = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + namespace: str | None = None, ) -> None: """Create a DeleteOne instance. @@ -275,9 +271,9 @@ class DeleteMany(_DeleteOp): def __init__( self, filter: Mapping[str, Any], - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - namespace: Optional[str] = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + namespace: str | None = None, ) -> None: """Create a DeleteMany instance. @@ -344,12 +340,12 @@ class ReplaceOne(Generic[_DocumentType]): def __init__( self, filter: Mapping[str, Any], - replacement: Union[_DocumentType, RawBSONDocument], - upsert: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - namespace: Optional[str] = None, - sort: Optional[Mapping[str, Any]] = None, + replacement: _DocumentType | RawBSONDocument, + upsert: bool | None = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + namespace: str | None = None, + sort: Mapping[str, Any] | None = None, ) -> None: """Create a ReplaceOne instance. @@ -385,7 +381,7 @@ def __init__( if upsert is not None: validate_boolean("upsert", upsert) if hint is not None and not isinstance(hint, str): - self._hint: Union[str, dict[str, Any], None] = helpers_shared._index_document(hint) + self._hint: str | dict[str, Any] | None = helpers_shared._index_document(hint) else: self._hint = hint @@ -472,13 +468,13 @@ class _UpdateOp: def __init__( self, filter: Mapping[str, Any], - doc: Union[Mapping[str, Any], _Pipeline], - upsert: Optional[bool], - collation: Optional[_CollationIn], - array_filters: Optional[list[Mapping[str, Any]]], - hint: Optional[_IndexKeyHint], - namespace: Optional[str], - sort: Optional[Mapping[str, Any]], + doc: Mapping[str, Any] | _Pipeline, + upsert: bool | None, + collation: _CollationIn | None, + array_filters: list[Mapping[str, Any]] | None, + hint: _IndexKeyHint | None, + namespace: str | None, + sort: Mapping[str, Any] | None, ): if filter is not None: validate_is_mapping("filter", filter) @@ -487,7 +483,7 @@ def __init__( if array_filters is not None: validate_list("array_filters", array_filters) if hint is not None and not isinstance(hint, str): - self._hint: Union[str, dict[str, Any], None] = helpers_shared._index_document(hint) + self._hint: str | dict[str, Any] | None = helpers_shared._index_document(hint) else: self._hint = hint self._filter = filter @@ -540,13 +536,13 @@ class UpdateOne(_UpdateOp): def __init__( self, filter: Mapping[str, Any], - update: Union[Mapping[str, Any], _Pipeline], - upsert: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - array_filters: Optional[list[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - namespace: Optional[str] = None, - sort: Optional[Mapping[str, Any]] = None, + update: Mapping[str, Any] | _Pipeline, + upsert: bool | None = None, + collation: _CollationIn | None = None, + array_filters: list[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + namespace: str | None = None, + sort: Mapping[str, Any] | None = None, ) -> None: """Represents an update_one operation. @@ -625,12 +621,12 @@ class UpdateMany(_UpdateOp): def __init__( self, filter: Mapping[str, Any], - update: Union[Mapping[str, Any], _Pipeline], - upsert: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - array_filters: Optional[list[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - namespace: Optional[str] = None, + update: Mapping[str, Any] | _Pipeline, + upsert: bool | None = None, + collation: _CollationIn | None = None, + array_filters: list[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + namespace: str | None = None, ) -> None: """Create an UpdateMany instance. @@ -793,8 +789,8 @@ class SearchIndexModel: def __init__( self, definition: Mapping[str, Any], - name: Optional[str] = None, - type: Optional[str] = None, + name: str | None = None, + type: str | None = None, **kwargs: Any, ) -> None: """Create a Search Index instance. diff --git a/pymongo/periodic_executor.py b/pymongo/periodic_executor.py index 88149115ff..8f8bf2bfea 100644 --- a/pymongo/periodic_executor.py +++ b/pymongo/periodic_executor.py @@ -21,7 +21,7 @@ import threading import time import weakref -from typing import Any, Optional +from typing import Any from pymongo import _csot, _op_id from pymongo.lock import _create_lock @@ -35,7 +35,7 @@ def __init__( interval: float, min_interval: float, target: Any, - name: Optional[str] = None, + name: str | None = None, ): """Run a target function periodically on a background task. @@ -52,7 +52,7 @@ def __init__( self._min_interval = min_interval self._target = target self._stopped = False - self._task: Optional[asyncio.Task[Any]] = None + self._task: asyncio.Task[Any] | None = None self._name = name self._skip_sleep = False @@ -78,7 +78,7 @@ def close(self, dummy: Any = None) -> None: if self._task is not None: self._task.cancel() - async def join(self, timeout: Optional[int] = None) -> None: + async def join(self, timeout: int | None = None) -> None: if self._task is not None: await asyncio.wait([self._task], timeout=timeout) # type-ignore: [arg-type] @@ -143,7 +143,7 @@ def __init__( interval: float, min_interval: float, target: Any, - name: Optional[str] = None, + name: str | None = None, ): """Run a target function periodically on a background thread. @@ -164,7 +164,7 @@ def __init__( self._min_interval = min_interval self._target = target self._stopped = False - self._thread: Optional[threading.Thread] = None + self._thread: threading.Thread | None = None self._name = name self._skip_sleep = False self._thread_will_exit = False @@ -236,7 +236,7 @@ def close(self, dummy: Any = None) -> None: """ self._stopped = True - def join(self, timeout: Optional[int] = None) -> None: + def join(self, timeout: int | None = None) -> None: if self._thread is not None: try: self._thread.join(timeout) diff --git a/pymongo/pool_options.py b/pymongo/pool_options.py index 45a9adf5da..87c127d721 100644 --- a/pymongo/pool_options.py +++ b/pymongo/pool_options.py @@ -26,7 +26,7 @@ from collections.abc import MutableMapping from contextlib import AbstractContextManager, nullcontext from pathlib import Path -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any import bson from pymongo import __version__ @@ -165,7 +165,7 @@ def _is_faas() -> bool: _MAX_AGENT_SIZE = 64 -def _metadata_agent() -> Optional[str]: +def _metadata_agent() -> str | None: """Detect a coding agent from the environment for client.env.agent.""" for var, name in _AGENT_ENV_VARS: if (os.getenv(var) or "").strip(): @@ -181,7 +181,7 @@ def _metadata_agent() -> Optional[str]: return agent.encode(errors="ignore")[:_MAX_AGENT_SIZE].decode(errors="ignore") -def _getenv_int(key: str) -> Optional[int]: +def _getenv_int(key: str) -> int | None: """Like os.getenv but returns an int, or None if the value is missing/malformed.""" val = os.getenv(key) if not val: @@ -409,22 +409,22 @@ def __init__( self, max_pool_size: int = MAX_POOL_SIZE, min_pool_size: int = MIN_POOL_SIZE, - max_idle_time_seconds: Optional[int] = MAX_IDLE_TIME_SEC, - connect_timeout: Optional[float] = None, - socket_timeout: Optional[float] = None, - wait_queue_timeout: Optional[int] = WAIT_QUEUE_TIMEOUT, - ssl_context: Optional[SSLContext] = None, + max_idle_time_seconds: int | None = MAX_IDLE_TIME_SEC, + connect_timeout: float | None = None, + socket_timeout: float | None = None, + wait_queue_timeout: int | None = WAIT_QUEUE_TIMEOUT, + ssl_context: SSLContext | None = None, tls_allow_invalid_hostnames: bool = False, - event_listeners: Optional[_EventListeners] = None, - appname: Optional[str] = None, - driver: Optional[DriverInfo] = None, - compression_settings: Optional[CompressionSettings] = None, + event_listeners: _EventListeners | None = None, + appname: str | None = None, + driver: DriverInfo | None = None, + compression_settings: CompressionSettings | None = None, max_connecting: int = MAX_CONNECTING, pause_enabled: bool = True, - server_api: Optional[ServerApi] = None, - load_balanced: Optional[bool] = None, - credentials: Optional[MongoCredential] = None, - is_sync: Optional[bool] = True, + server_api: ServerApi | None = None, + load_balanced: bool | None = None, + credentials: MongoCredential | None = None, + is_sync: bool | None = True, ): self.__max_pool_size = max_pool_size self.__min_pool_size = min_pool_size @@ -524,7 +524,7 @@ def _update_metadata(self, driver: DriverInfo) -> None: self.__appended_drivers.add(driver) @property - def _credentials(self) -> Optional[MongoCredential]: + def _credentials(self) -> MongoCredential | None: """A :class:`~pymongo.auth.MongoCredentials` instance or None.""" return self.__credentials @@ -583,7 +583,7 @@ def pause_enabled(self) -> bool: return self.__pause_enabled @property - def max_idle_time_seconds(self) -> Optional[int]: + def max_idle_time_seconds(self) -> int | None: """The maximum number of seconds that a connection can remain idle in the pool before being removed and replaced. Defaults to `None` (no limit). @@ -591,24 +591,24 @@ def max_idle_time_seconds(self) -> Optional[int]: return self.__max_idle_time_seconds @property - def connect_timeout(self) -> Optional[float]: + def connect_timeout(self) -> float | None: """How long a connection can take to be opened before timing out.""" return self.__connect_timeout @property - def socket_timeout(self) -> Optional[float]: + def socket_timeout(self) -> float | None: """How long a send or receive on a socket can take before timing out.""" return self.__socket_timeout @property - def wait_queue_timeout(self) -> Optional[int]: + def wait_queue_timeout(self) -> int | None: """How long a thread will wait for a socket from the pool if the pool has no free sockets. """ return self.__wait_queue_timeout @property - def _ssl_context(self) -> Optional[SSLContext]: + def _ssl_context(self) -> SSLContext | None: """An SSLContext instance or None.""" return self.__ssl_context @@ -618,22 +618,22 @@ def tls_allow_invalid_hostnames(self) -> bool: return self.__tls_allow_invalid_hostnames @property - def _event_listeners(self) -> Optional[_EventListeners]: + def _event_listeners(self) -> _EventListeners | None: """An instance of pymongo.monitoring._EventListeners.""" return self.__event_listeners @property - def appname(self) -> Optional[str]: + def appname(self) -> str | None: """The application name, for sending with hello in server handshake.""" return self.__appname @property - def driver(self) -> Optional[DriverInfo]: + def driver(self) -> DriverInfo | None: """Driver name and version, for sending with hello in handshake.""" return self.__driver @property - def _compression_settings(self) -> Optional[CompressionSettings]: + def _compression_settings(self) -> CompressionSettings | None: return self.__compression_settings @property @@ -642,11 +642,11 @@ def metadata(self) -> dict[str, Any]: return self.__metadata.copy() @property - def server_api(self) -> Optional[ServerApi]: + def server_api(self) -> ServerApi | None: """A pymongo.server_api.ServerApi or None.""" return self.__server_api @property - def load_balanced(self) -> Optional[bool]: + def load_balanced(self) -> bool | None: """True if this Pool is configured in load balanced mode.""" return self.__load_balanced diff --git a/pymongo/pool_shared.py b/pymongo/pool_shared.py index 48a0a27bf1..7c6588fabb 100644 --- a/pymongo/pool_shared.py +++ b/pymongo/pool_shared.py @@ -32,10 +32,8 @@ Any, Generic, NoReturn, - Optional, Protocol, TypeVar, - Union, ) from pymongo import _csot @@ -96,9 +94,9 @@ class _ConnectionTelemetryInfo(Protocol): """Protocol for connection fields consumed by :class:`~pymongo._telemetry._CommandTelemetry`.""" id: int - server_connection_id: Optional[int] + server_connection_id: int | None address: tuple[str, int] - service_id: Optional[ObjectId] + service_id: ObjectId | None class _AgnosticBaseConnection(_ConnectionTelemetryInfo, Generic[_PoolT]): @@ -132,12 +130,12 @@ def __init__( self.listeners = pool.opts._event_listeners self._telemetry = pool._telemetry self.compression_settings = pool.opts._compression_settings - self.compression_context: Union[SnappyContext, ZlibContext, ZstdContext, None] = None + self.compression_context: SnappyContext | ZlibContext | ZstdContext | None = None self.socket_checker: SocketChecker = SocketChecker() - self.oidc_token_gen_id: Optional[int] = None + self.oidc_token_gen_id: int | None = None # Support for mechanism negotiation on the initial handshake. - self.negotiated_mechs: Optional[list[str]] = None - self.auth_ctx: Optional[_AuthContext] = None + self.negotiated_mechs: list[str] | None = None + self.auth_ctx: _AuthContext | None = None # The pool's generation changes with each reset() so we can close # sockets created before the last reset. @@ -148,8 +146,8 @@ def __init__( self.opts = pool.opts self.more_to_come: bool = False # For load balancer support. - self.service_id: Optional[ObjectId] = None - self.server_connection_id: Optional[int] = None + self.service_id: ObjectId | None = None + self.server_connection_id: int | None = None # When executing a transaction in load balancing mode, this flag is # set to true to indicate that the session now owns the connection. self.pinned_txn = False @@ -160,14 +158,14 @@ def __init__( # For gossiping $clusterTime from the connection handshake to the client. self._cluster_time = None - def set_conn_timeout(self, timeout: Optional[float]) -> None: + def set_conn_timeout(self, timeout: float | None) -> None: """Cache last timeout to avoid duplicate calls to conn.settimeout.""" if timeout == self.last_timeout: return self.last_timeout = timeout self.conn.get_conn.settimeout(timeout) - def apply_timeout(self, cmd: Optional[MutableMapping[str, Any]]) -> Optional[float]: + def apply_timeout(self, cmd: MutableMapping[str, Any] | None) -> float | None: # CSOT: use remaining timeout when set. timeout = _csot.remaining() if timeout is None: @@ -217,7 +215,7 @@ def _raise_if_not_writable(self) -> None: raise NotPrimaryError("not primary", {"ok": 0, "errmsg": "not primary", "code": 10107}) def validate_session( - self, client: Optional[_AgnosticMongoClient], session: Optional[_AgnosticClientSession] + self, client: _AgnosticMongoClient | None, session: _AgnosticClientSession | None ) -> None: """Validate this session before use with client. @@ -232,8 +230,8 @@ def validate_session( def send_cluster_time( self, command: MutableMapping[str, Any], - session: Optional[_AgnosticClientSession], - client: Optional[_AgnosticMongoClient], + session: _AgnosticClientSession | None, + client: _AgnosticMongoClient | None, ) -> None: """Add $clusterTime.""" if client: @@ -272,7 +270,7 @@ def __init__( address: _Address, options: PoolOptions, is_sdam: bool = False, - client_id: Optional[ObjectId] = None, + client_id: ObjectId | None = None, ): """ :param address: a (hostname, port) tuple @@ -295,7 +293,7 @@ def __init__( # Monotonically increasing connection ID required for CMAP Events. self.next_connection_id = 1 # Track whether the sockets in this pool are writeable or not. - self.is_writable: Optional[bool] = None + self.is_writable: bool | None = None # Keep track of resets, so we notice sockets created before the most # recent reset and close them. @@ -311,7 +309,7 @@ def __init__( self.max_pool_size = float("inf") self._pending = 0 self._max_connecting = self.opts.max_connecting - self._ssl_session_cache: Optional[list[Any]] = ( + self._ssl_session_cache: list[Any] | None = ( [None] if self.opts._ssl_context is not None and not _TLS_SESSION_REUSE_LEAKS else None ) self._telemetry = _CmapTelemetry( @@ -331,7 +329,7 @@ def __init__( def closed(self) -> bool: return self.state == PoolState.CLOSED - def stale_generation(self, gen: int, service_id: Optional[ObjectId]) -> bool: + def stale_generation(self, gen: int, service_id: ObjectId | None) -> bool: return self.gen.stale(gen, service_id) def _handle_connection_error(self, error: BaseException) -> None: @@ -376,7 +374,7 @@ def _restore_applied(self, applied: int) -> None: def _checkin_apply( self, conn: _ConnT, txn: bool, cursor: bool, forked: bool - ) -> tuple[Optional[str], bool, bool]: + ) -> tuple[str | None, bool, bool]: """Apply checkin accounting; caller holds ``size_cond``. No cooperative I/O, so safe while a gevent greenlet unwinds. Returns @@ -390,7 +388,7 @@ def _checkin_apply( self.requests -= 1 self.active_sockets -= 1 self.operation_count -= 1 - close_conn_reason: Optional[str] = None + close_conn_reason: str | None = None emit_closed = False appended = False if not forked: @@ -429,7 +427,7 @@ def _raise_wait_queue_timeout(self, checkout_started_time: float) -> NoReturn: ) -def _get_ssl_session(ssl_sock: Any) -> Optional[Any]: +def _get_ssl_session(ssl_sock: Any) -> Any | None: """Return the TLS session from an SSL socket, handling both PyOpenSSL and stdlib ssl.""" if hasattr(ssl_sock, "get_session"): return ssl_sock.get_session() @@ -514,8 +512,8 @@ def _set_keepalive_times(sock: socket.socket) -> None: def _raise_connection_failure( address: Any, error: Exception, - msg_prefix: Optional[str] = None, - timeout_details: Optional[dict[str, float]] = None, + msg_prefix: str | None = None, + timeout_details: dict[str, float] | None = None, ) -> NoReturn: """Convert a socket.error to ConnectionFailure and raise it.""" host, port = address @@ -563,7 +561,7 @@ def __init__(self) -> None: # Overall pool generation. self._generation = 0 - def get(self, service_id: Optional[ObjectId]) -> int: + def get(self, service_id: ObjectId | None) -> int: """Get the generation for the given service_id.""" if service_id is None: return self._generation @@ -573,7 +571,7 @@ def get_overall(self) -> int: """Get the Pool's overall generation.""" return self._generation - def inc(self, service_id: Optional[ObjectId]) -> None: + def inc(self, service_id: ObjectId | None) -> None: """Increment the generation for the given service_id.""" self._generation += 1 if service_id is None: @@ -582,7 +580,7 @@ def inc(self, service_id: Optional[ObjectId]) -> None: else: self._generations[service_id] += 1 - def stale(self, gen: int, service_id: Optional[ObjectId]) -> bool: + def stale(self, gen: int, service_id: ObjectId | None) -> bool: """Return if the given generation for a given service_id is stale.""" return gen != self.get(service_id) @@ -684,7 +682,7 @@ def _close_late_socket(future: asyncio.Future[Any]) -> None: async def _async_wrap_socket_tls( sock: socket.socket, address: _Address, options: PoolOptions -) -> Union[socket.socket, _sslConn]: +) -> socket.socket | _sslConn: """Given a connected socket, (host, port), and PoolOptions, apply TLS. The handshake, SNI, and certificate/hostname verification all target @@ -749,7 +747,7 @@ async def _async_wrap_socket_tls( async def _async_configured_socket( address: _Address, options: PoolOptions -) -> Union[socket.socket, _sslConn]: +) -> socket.socket | _sslConn: """Given (host, port) and PoolOptions, return a raw configured socket. Can raise socket.error, ConnectionFailure, or _CertificateError. @@ -763,7 +761,7 @@ async def _async_configured_socket( async def _configured_protocol_interface( address: _Address, options: PoolOptions, - ssl_session_cache: Optional[list[Any]] = None, + ssl_session_cache: list[Any] | None = None, ) -> AsyncNetworkingInterface: """Given (host, port) and PoolOptions, return a configured AsyncNetworkingInterface. @@ -912,7 +910,7 @@ def _create_connection(address: _Address, options: PoolOptions) -> socket.socket def _wrap_socket_tls( sock: socket.socket, address: _Address, options: PoolOptions -) -> Union[socket.socket, _sslConn]: +) -> socket.socket | _sslConn: """Given a connected socket, (host, port), and PoolOptions, apply TLS. The handshake, SNI, and certificate/hostname verification all target @@ -964,7 +962,7 @@ def _wrap_socket_tls( return ssl_sock -def _configured_socket(address: _Address, options: PoolOptions) -> Union[socket.socket, _sslConn]: +def _configured_socket(address: _Address, options: PoolOptions) -> socket.socket | _sslConn: """Given (host, port) and PoolOptions, return a raw configured socket. Can raise socket.error, ConnectionFailure, or _CertificateError. @@ -978,7 +976,7 @@ def _configured_socket(address: _Address, options: PoolOptions) -> Union[socket. def _configured_socket_interface( address: _Address, options: PoolOptions, - ssl_session_cache: Optional[list[Any]] = None, + ssl_session_cache: list[Any] | None = None, ) -> NetworkingInterface: """Given (host, port) and PoolOptions, return a NetworkingInterface wrapping a configured socket. diff --git a/pymongo/pyopenssl_context.py b/pymongo/pyopenssl_context.py index c33603dea3..bff265718d 100644 --- a/pymongo/pyopenssl_context.py +++ b/pymongo/pyopenssl_context.py @@ -26,7 +26,7 @@ import time as _time from errno import EINTR as _EINTR from ipaddress import ip_address as _ip_address -from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union +from typing import TYPE_CHECKING, Any, Callable, TypeVar import cryptography.x509 as x509 import service_identity @@ -113,7 +113,7 @@ class _sslConn(_SSL.Connection): def __init__( self, ctx: _SSL.Context, - sock: Optional[_socket.socket], + sock: _socket.socket | None, suppress_ragged_eofs: bool, ): self.socket_checker = _SocketChecker() @@ -196,8 +196,8 @@ class _CallbackData: """Data class which is passed to the OCSP callback.""" def __init__(self) -> None: - self.trusted_ca_certs: Optional[list[x509.Certificate]] = None - self.check_ocsp_endpoint: Optional[bool] = None + self.trusted_ca_certs: list[x509.Certificate] | None = None + self.check_ocsp_endpoint: bool | None = None self.ocsp_response_cache = _OCSPCache() @@ -252,7 +252,7 @@ def __set_check_hostname(self, value: Any) -> None: check_hostname = property(__get_check_hostname, __set_check_hostname) - def __get_check_ocsp_endpoint(self) -> Optional[bool]: + def __get_check_ocsp_endpoint(self) -> bool | None: return self._callback_data.check_ocsp_endpoint def __set_check_ocsp_endpoint(self, value: bool) -> None: @@ -275,9 +275,9 @@ def __set_options(self, value: int) -> None: def load_cert_chain( self, - certfile: Union[str, bytes], - keyfile: Union[str, bytes, None] = None, - password: Optional[str] = None, + certfile: str | bytes, + keyfile: str | bytes | None = None, + password: str | None = None, ) -> None: """Load a private key and the corresponding certificate. The certfile string must be the path to a single file in PEM format containing the @@ -291,7 +291,7 @@ def load_cert_chain( # Password callback MUST be set first or it will be ignored. if password: - def _pwcb(_max_length: int, _prompt_twice: bool, _user_data: Optional[bytes]) -> bytes: + def _pwcb(_max_length: int, _prompt_twice: bool, _user_data: bytes | None) -> bytes: # XXX:We could check the password length against what OpenSSL # tells us is the max, but we can't raise an exception, so... # warn? @@ -303,9 +303,7 @@ def _pwcb(_max_length: int, _prompt_twice: bool, _user_data: Optional[bytes]) -> self._ctx.use_privatekey_file(keyfile or certfile) self._ctx.check_privatekey() - def load_verify_locations( - self, cafile: Optional[str] = None, capath: Optional[str] = None - ) -> None: + def load_verify_locations(self, cafile: str | None = None, capath: str | None = None) -> None: """Load a set of "certification authority"(CA) certificates used to validate other peers' certificates when `~verify_mode` is other than ssl.CERT_NONE. @@ -367,8 +365,8 @@ def wrap_socket( server_side: bool = False, do_handshake_on_connect: bool = True, suppress_ragged_eofs: bool = True, - server_hostname: Optional[str] = None, - session: Optional[_SSL.Session] = None, + server_hostname: str | None = None, + session: _SSL.Session | None = None, ) -> _sslConn: """Wrap an existing Python socket connection and return a TLS socket object. diff --git a/pymongo/read_concern.py b/pymongo/read_concern.py index 9a11c8f562..aa789709fc 100644 --- a/pymongo/read_concern.py +++ b/pymongo/read_concern.py @@ -19,7 +19,7 @@ from __future__ import annotations -from typing import Any, Optional +from typing import Any class ReadConcern: @@ -35,14 +35,14 @@ class ReadConcern: """ - def __init__(self, level: Optional[str] = None) -> None: + def __init__(self, level: str | None = None) -> None: if level is None or isinstance(level, str): self.__level = level else: raise TypeError(f"level must be a string or None, not {type(level)}") @property - def level(self) -> Optional[str]: + def level(self) -> str | None: """The read concern level.""" return self.__level diff --git a/pymongo/read_preferences.py b/pymongo/read_preferences.py index 204bb49101..2d287b0881 100644 --- a/pymongo/read_preferences.py +++ b/pymongo/read_preferences.py @@ -22,7 +22,7 @@ import warnings from collections import abc from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from pymongo import max_staleness_selectors from pymongo.errors import ConfigurationError @@ -55,7 +55,7 @@ _TagSets = Sequence[Mapping[str, Any]] -def _validate_tag_sets(tag_sets: Optional[_TagSets]) -> Optional[_TagSets]: +def _validate_tag_sets(tag_sets: _TagSets | None) -> _TagSets | None: """Validate tag sets for a MongoClient.""" if tag_sets is None: return tag_sets @@ -97,7 +97,7 @@ def _validate_max_staleness(max_staleness: Any) -> int: return max_staleness -def _validate_hedge(hedge: Optional[_Hedge]) -> Optional[_Hedge]: +def _validate_hedge(hedge: _Hedge | None) -> _Hedge | None: """Validate hedge.""" if hedge is None: return None @@ -121,9 +121,9 @@ class _ServerMode: def __init__( self, mode: int, - tag_sets: Optional[_TagSets] = None, + tag_sets: _TagSets | None = None, max_staleness: int = -1, - hedge: Optional[_Hedge] = None, + hedge: _Hedge | None = None, ) -> None: self.__mongos_mode = _MONGOS_MODES[mode] self.__mode = mode @@ -189,7 +189,7 @@ def max_staleness(self) -> int: return self.__max_staleness @property - def hedge(self) -> Optional[_Hedge]: + def hedge(self) -> _Hedge | None: """**DEPRECATED** - The read preference 'hedge' option is deprecated in PyMongo 4.12+ because hedged reads are deprecated in MongoDB version 8.0+. Support for 'hedge' will be removed in PyMongo 5.0. The read preference ``hedge`` parameter. @@ -336,9 +336,9 @@ class PrimaryPreferred(_ServerMode): def __init__( self, - tag_sets: Optional[_TagSets] = None, + tag_sets: _TagSets | None = None, max_staleness: int = -1, - hedge: Optional[_Hedge] = None, + hedge: _Hedge | None = None, ) -> None: super().__init__(_PRIMARY_PREFERRED, tag_sets, max_staleness, hedge) @@ -378,9 +378,9 @@ class Secondary(_ServerMode): def __init__( self, - tag_sets: Optional[_TagSets] = None, + tag_sets: _TagSets | None = None, max_staleness: int = -1, - hedge: Optional[_Hedge] = None, + hedge: _Hedge | None = None, ) -> None: super().__init__(_SECONDARY, tag_sets, max_staleness, hedge) @@ -421,9 +421,9 @@ class SecondaryPreferred(_ServerMode): def __init__( self, - tag_sets: Optional[_TagSets] = None, + tag_sets: _TagSets | None = None, max_staleness: int = -1, - hedge: Optional[_Hedge] = None, + hedge: _Hedge | None = None, ) -> None: super().__init__(_SECONDARY_PREFERRED, tag_sets, max_staleness, hedge) @@ -465,9 +465,9 @@ class Nearest(_ServerMode): def __init__( self, - tag_sets: Optional[_TagSets] = None, + tag_sets: _TagSets | None = None, max_staleness: int = -1, - hedge: Optional[_Hedge] = None, + hedge: _Hedge | None = None, ) -> None: super().__init__(_NEAREST, tag_sets, max_staleness, hedge) @@ -522,7 +522,7 @@ def __getattr__(self, name: str) -> Any: def make_read_preference( - mode: int, tag_sets: Optional[_TagSets], max_staleness: int = -1 + mode: int, tag_sets: _TagSets | None, max_staleness: int = -1 ) -> _ServerMode: if mode == _PRIMARY: if tag_sets not in (None, [{}]): @@ -614,7 +614,7 @@ def read_pref_mode_from_name(name: str) -> int: class MovingAverage: """Tracks an exponentially-weighted moving average.""" - average: Optional[float] + average: float | None def __init__(self) -> None: self.average = None @@ -629,7 +629,7 @@ def add_sample(self, sample: float) -> None: # average with alpha = 0.2. self.average = 0.8 * self.average + 0.2 * sample - def get(self) -> Optional[float]: + def get(self) -> float | None: """Get the calculated average, or None if no samples yet.""" return self.average diff --git a/pymongo/response.py b/pymongo/response.py index b832036708..3ea4fa5c5b 100644 --- a/pymongo/response.py +++ b/pymongo/response.py @@ -17,7 +17,7 @@ from __future__ import annotations from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any if TYPE_CHECKING: from datetime import timedelta @@ -34,7 +34,7 @@ def __init__( data: _OpMsg, address: _Address, request_id: int, - duration: Optional[timedelta], + duration: timedelta | None, docs: Sequence[Mapping[str, Any]], ): """Represent a response from the server. @@ -66,7 +66,7 @@ def request_id(self) -> int: return self._request_id @property - def duration(self) -> Optional[timedelta]: + def duration(self) -> timedelta | None: """The duration of the operation.""" return self._duration @@ -85,7 +85,7 @@ def __init__( address: _Address, conn: _AgnosticConnection, request_id: int, - duration: Optional[timedelta], + duration: timedelta | None, docs: list[_DocumentOut], more_to_come: bool, ): diff --git a/pymongo/results.py b/pymongo/results.py index 594a28bc88..6b4bfd6c1b 100644 --- a/pymongo/results.py +++ b/pymongo/results.py @@ -20,7 +20,7 @@ from __future__ import annotations from collections.abc import Mapping, MutableMapping -from typing import Any, Optional, cast +from typing import Any, cast from pymongo.errors import InvalidOperation @@ -128,7 +128,7 @@ class UpdateResult(_WriteResult): def __init__( self, - raw_result: Optional[Mapping[str, Any]], + raw_result: Mapping[str, Any] | None, acknowledged: bool, in_client_bulk: bool = False, ): @@ -140,7 +140,7 @@ def __repr__(self) -> str: return f"{self.__class__.__name__}({self.__raw_result!r}, acknowledged={self.acknowledged})" @property - def raw_result(self) -> Optional[Mapping[str, Any]]: + def raw_result(self) -> Mapping[str, Any] | None: """The raw result document returned by the server.""" return self.__raw_result @@ -280,7 +280,7 @@ def __repr__(self) -> str: ) @property - def upserted_ids(self) -> Optional[dict[int, Any]]: + def upserted_ids(self) -> dict[int, Any] | None: """A map of operation index to the _id of the upserted document.""" self._raise_if_unacknowledged("upserted_ids") if self.bulk_api_result: diff --git a/pymongo/saslprep.py b/pymongo/saslprep.py index 25d97721c1..7ed3c69bf9 100644 --- a/pymongo/saslprep.py +++ b/pymongo/saslprep.py @@ -16,7 +16,7 @@ from __future__ import annotations -from typing import Any, Optional +from typing import Any try: import stringprep @@ -25,7 +25,7 @@ def saslprep( data: Any, - prohibit_unassigned_code_points: Optional[bool] = True, # noqa: ARG001 + prohibit_unassigned_code_points: bool | None = True, # noqa: ARG001 ) -> Any: """SASLprep dummy""" if isinstance(data, str): @@ -55,7 +55,7 @@ def saslprep( stringprep.in_table_c9, ) - def saslprep(data: Any, prohibit_unassigned_code_points: Optional[bool] = True) -> Any: + def saslprep(data: Any, prohibit_unassigned_code_points: bool | None = True) -> Any: """An implementation of RFC4013 SASLprep. :param data: The string to SASLprep. Unicode strings diff --git a/pymongo/server_api.py b/pymongo/server_api.py index b492c4e278..afc54c5e39 100644 --- a/pymongo/server_api.py +++ b/pymongo/server_api.py @@ -87,7 +87,7 @@ from __future__ import annotations from collections.abc import MutableMapping -from typing import Any, Optional +from typing import Any class ServerApiVersion: @@ -104,7 +104,7 @@ class ServerApi: """MongoDB Stable API.""" def __init__( - self, version: str, strict: Optional[bool] = None, deprecation_errors: Optional[bool] = None + self, version: str, strict: bool | None = None, deprecation_errors: bool | None = None ): """Options to configure MongoDB Stable API. @@ -143,7 +143,7 @@ def version(self) -> str: return self._version @property - def strict(self) -> Optional[bool]: + def strict(self) -> bool | None: """The API strict mode setting. When set, this value is sent to the server in the "apiStrict" field. @@ -151,7 +151,7 @@ def strict(self) -> Optional[bool]: return self._strict @property - def deprecation_errors(self) -> Optional[bool]: + def deprecation_errors(self) -> bool | None: """The API deprecation errors setting. When set, this value is sent to the server in the @@ -160,7 +160,7 @@ def deprecation_errors(self) -> Optional[bool]: return self._deprecation_errors -def _add_to_command(cmd: MutableMapping[str, Any], server_api: Optional[ServerApi]) -> None: +def _add_to_command(cmd: MutableMapping[str, Any], server_api: ServerApi | None) -> None: """Internal helper which adds API versioning options to a command. :param cmd: The command. diff --git a/pymongo/server_description.py b/pymongo/server_description.py index c5bb7f9769..8da8412ded 100644 --- a/pymongo/server_description.py +++ b/pymongo/server_description.py @@ -22,7 +22,7 @@ import time import warnings from collections.abc import Mapping -from typing import Any, Optional +from typing import Any from bson import EPOCH_NAIVE from bson.objectid import ObjectId @@ -71,9 +71,9 @@ class ServerDescription: def __init__( self, address: _Address, - hello: Optional[Hello[dict[str, Any]]] = None, - round_trip_time: Optional[float] = None, - error: Optional[Exception] = None, + hello: Hello[dict[str, Any]] | None = None, + round_trip_time: float | None = None, + error: Exception | None = None, min_round_trip_time: float = 0.0, ) -> None: self._address = address @@ -107,7 +107,7 @@ def __init__( if isinstance(details, dict): self._topology_version = details.get("topologyVersion") - self._last_write_date: Optional[float] + self._last_write_date: float | None if hello.last_write_date: # Convert from datetime to seconds. delta = hello.last_write_date - EPOCH_NAIVE @@ -143,12 +143,12 @@ def tags(self) -> Mapping[str, Any]: return self._tags @property - def replica_set_name(self) -> Optional[str]: + def replica_set_name(self) -> str | None: """Replica set name or None.""" return self._replica_set_name @property - def primary(self) -> Optional[tuple[str, int]]: + def primary(self) -> tuple[str, int] | None: """This server's opinion about who the primary is, or None.""" return self._primary @@ -173,19 +173,19 @@ def max_wire_version(self) -> int: return self._max_wire_version @property - def set_version(self) -> Optional[int]: + def set_version(self) -> int | None: return self._set_version @property - def election_id(self) -> Optional[ObjectId]: + def election_id(self) -> ObjectId | None: return self._election_id @property - def cluster_time(self) -> Optional[ClusterTime]: + def cluster_time(self) -> ClusterTime | None: return self._cluster_time @property - def election_tuple(self) -> tuple[Optional[int], Optional[ObjectId]]: + def election_tuple(self) -> tuple[int | None, ObjectId | None]: warnings.warn( "'election_tuple' is deprecated, use 'set_version' and 'election_id' instead", DeprecationWarning, @@ -194,15 +194,15 @@ def election_tuple(self) -> tuple[Optional[int], Optional[ObjectId]]: return self._set_version, self._election_id @property - def me(self) -> Optional[tuple[str, int]]: + def me(self) -> tuple[str, int] | None: return self._me @property - def logical_session_timeout_minutes(self) -> Optional[int]: + def logical_session_timeout_minutes(self) -> int | None: return self._ls_timeout_minutes @property - def last_write_date(self) -> Optional[float]: + def last_write_date(self) -> float | None: return self._last_write_date @property @@ -210,7 +210,7 @@ def last_update_time(self) -> float: return self._last_update_time @property - def round_trip_time(self) -> Optional[float]: + def round_trip_time(self) -> float | None: """The current average latency or None.""" # This override is for unittesting only! if self._address in self._host_to_round_trip_time: @@ -224,7 +224,7 @@ def min_round_trip_time(self) -> float: return self._min_round_trip_time @property - def error(self) -> Optional[Exception]: + def error(self) -> Exception | None: """The last error attempting to connect to the server, or None.""" return self._error @@ -257,10 +257,10 @@ def retryable_reads_supported(self) -> bool: return self._max_wire_version >= 6 @property - def topology_version(self) -> Optional[Mapping[str, Any]]: + def topology_version(self) -> Mapping[str, Any] | None: return self._topology_version - def to_unknown(self, error: Optional[Exception] = None) -> ServerDescription: + def to_unknown(self, error: Exception | None = None) -> ServerDescription: unknown = ServerDescription(self.address, error=error) unknown._topology_version = self.topology_version return unknown diff --git a/pymongo/server_selectors.py b/pymongo/server_selectors.py index b43272f7f3..b4aded09cb 100644 --- a/pymongo/server_selectors.py +++ b/pymongo/server_selectors.py @@ -17,7 +17,7 @@ from __future__ import annotations from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any, Optional, TypeVar, cast +from typing import TYPE_CHECKING, Any, TypeVar, cast from pymongo.server_type import SERVER_TYPE @@ -54,8 +54,8 @@ def __init__( self, topology_description: TopologyDescription, server_descriptions: list[ServerDescription], - common_wire_version: Optional[int], - primary: Optional[ServerDescription], + common_wire_version: int | None, + primary: ServerDescription | None, ): self.topology_description = topology_description self.server_descriptions = server_descriptions @@ -67,7 +67,7 @@ def with_server_descriptions(self, server_descriptions: list[ServerDescription]) self.topology_description, server_descriptions, self.common_wire_version, self.primary ) - def secondary_with_max_last_write_date(self) -> Optional[ServerDescription]: + def secondary_with_max_last_write_date(self) -> ServerDescription | None: secondaries = secondary_server_selector(self) if secondaries.server_descriptions: return max( diff --git a/pymongo/settings_shared.py b/pymongo/settings_shared.py index bf45c40334..293a697e20 100644 --- a/pymongo/settings_shared.py +++ b/pymongo/settings_shared.py @@ -19,7 +19,7 @@ import threading import traceback from collections.abc import Collection -from typing import Any, Callable, Generic, Optional, TypeVar, Union +from typing import Any, Callable, Generic, TypeVar from bson.objectid import ObjectId from pymongo import common @@ -38,23 +38,23 @@ def __init__( self, pool_class: _PoolClassType, monitor_class: _MonitorClassType, - seeds: Optional[Collection[tuple[str, int]]] = None, - replica_set_name: Optional[str] = None, - pool_options: Optional[PoolOptions] = None, - condition_class: Optional[type[threading.Condition]] = None, + seeds: Collection[tuple[str, int]] | None = None, + replica_set_name: str | None = None, + pool_options: PoolOptions | None = None, + condition_class: type[threading.Condition] | None = None, local_threshold_ms: int = LOCAL_THRESHOLD_MS, server_selection_timeout: int = SERVER_SELECTION_TIMEOUT, heartbeat_frequency: int = common.HEARTBEAT_FREQUENCY, - server_selector: Optional[_ServerSelector] = None, - fqdn: Optional[str] = None, - direct_connection: Optional[bool] = False, - load_balanced: Optional[bool] = None, + server_selector: _ServerSelector | None = None, + fqdn: str | None = None, + direct_connection: bool | None = False, + load_balanced: bool | None = None, srv_service_name: str = common.SRV_SERVICE_NAME, srv_max_hosts: int = 0, - srv_allowed_hosts_suffix: Optional[str] = None, - srv_host_validator: Optional[Callable[[str], bool]] = None, + srv_allowed_hosts_suffix: str | None = None, + srv_host_validator: Callable[[str], bool] | None = None, server_monitoring_mode: str = common.SERVER_MONITORING_MODE, - topology_id: Optional[ObjectId] = None, + topology_id: ObjectId | None = None, ): """Represent MongoClient's configuration. @@ -96,7 +96,7 @@ def seeds(self) -> Collection[tuple[str, int]]: return self._seeds @property - def replica_set_name(self) -> Optional[str]: + def replica_set_name(self) -> str | None: return self._replica_set_name @property @@ -124,7 +124,7 @@ def server_selection_timeout(self) -> int: return self._server_selection_timeout @property - def server_selector(self) -> Optional[_ServerSelector]: + def server_selector(self) -> _ServerSelector | None: return self._server_selector @property @@ -132,11 +132,11 @@ def heartbeat_frequency(self) -> int: return self._heartbeat_frequency @property - def fqdn(self) -> Optional[str]: + def fqdn(self) -> str | None: return self._fqdn @property - def direct(self) -> Optional[bool]: + def direct(self) -> bool | None: """Connect directly to a single server, or use a set of servers? True if there is one seed and no replica_set_name. @@ -144,7 +144,7 @@ def direct(self) -> Optional[bool]: return self._direct @property - def load_balanced(self) -> Optional[bool]: + def load_balanced(self) -> bool | None: """True if the client was configured to connect to a load balancer.""" return self._load_balanced @@ -159,12 +159,12 @@ def srv_max_hosts(self) -> int: return self._srv_max_hosts @property - def srv_allowed_hosts_suffix(self) -> Optional[str]: + def srv_allowed_hosts_suffix(self) -> str | None: """The srvAllowedHostsSuffix.""" return self._srv_allowed_hosts_suffix @property - def srv_host_validator(self) -> Optional[Callable[[str], bool]]: + def srv_host_validator(self) -> Callable[[str], bool] | None: """The srv_host_validator callback.""" return self._srv_host_validator @@ -183,6 +183,6 @@ def get_topology_type(self) -> int: else: return TOPOLOGY_TYPE.Unknown - def get_server_descriptions(self) -> dict[Union[tuple[str, int], Any], ServerDescription]: + def get_server_descriptions(self) -> dict[tuple[str, int] | Any, ServerDescription]: """Initial dict of (address, ServerDescription) for all seeds.""" return {address: ServerDescription(address) for address in self.seeds} diff --git a/pymongo/socket_checker.py b/pymongo/socket_checker.py index 8424bf5062..9af7bbe0b7 100644 --- a/pymongo/socket_checker.py +++ b/pymongo/socket_checker.py @@ -18,13 +18,13 @@ import errno import select -from typing import Any, Optional, cast +from typing import Any, cast _HAVE_POLL = hasattr(select, "poll") _SelectError = getattr(select, "error", OSError) -def _errno_from_exception(exc: BaseException) -> Optional[int]: +def _errno_from_exception(exc: BaseException) -> int | None: if hasattr(exc, "errno"): return cast(int, exc.errno) if exc.args: @@ -34,14 +34,14 @@ def _errno_from_exception(exc: BaseException) -> Optional[int]: class SocketChecker: def __init__(self) -> None: - self._poller: Optional[select.poll] + self._poller: select.poll | None if _HAVE_POLL: self._poller = select.poll() else: self._poller = None def select( - self, sock: Any, read: bool = False, write: bool = False, timeout: Optional[float] = 0 + self, sock: Any, read: bool = False, write: bool = False, timeout: float | None = 0 ) -> bool: """Select for reads or writes with a timeout in seconds (or None). diff --git a/pymongo/ssl_support.py b/pymongo/ssl_support.py index d505f8df27..fff71e3870 100644 --- a/pymongo/ssl_support.py +++ b/pymongo/ssl_support.py @@ -20,7 +20,7 @@ import sys import types import warnings -from typing import Any, Optional, Union +from typing import Any from pymongo.errors import ConfigurationError @@ -90,15 +90,15 @@ def _has_sni(is_sync: bool) -> bool: return _ssl.HAS_SNI def get_ssl_context( - certfile: Optional[str], - passphrase: Optional[str], - ca_certs: Optional[str], - crlfile: Optional[str], + certfile: str | None, + passphrase: str | None, + ca_certs: str | None, + crlfile: str | None, allow_invalid_certificates: bool, allow_invalid_hostnames: bool, disable_ocsp_endpoint_check: bool, is_sync: bool, - ) -> Union[_pyssl.SSLContext, _ssl.SSLContext]: # type: ignore[name-defined] + ) -> _pyssl.SSLContext | _ssl.SSLContext: # type: ignore[name-defined] """Create and return an SSLContext object.""" if is_sync and HAVE_PYSSL: ssl: types.ModuleType = _pyssl diff --git a/pymongo/synchronous/_kms_connect.py b/pymongo/synchronous/_kms_connect.py index 78d8119557..255b01e7f0 100644 --- a/pymongo/synchronous/_kms_connect.py +++ b/pymongo/synchronous/_kms_connect.py @@ -25,7 +25,7 @@ import inspect import socket import ssl -from typing import TYPE_CHECKING, Any, Optional, Union, cast +from typing import TYPE_CHECKING, Any, cast from pymongo import _csot from pymongo._kms_connect_shared import ( @@ -57,9 +57,9 @@ def _connect_kms( address: _Address, opts: PoolOptions, - kms_connect_callback: Optional[KMSConnectCallback], + kms_connect_callback: KMSConnectCallback | None, timeout: float, -) -> Union[socket.socket, _sslConn]: +) -> socket.socket | _sslConn: """Connect to a KMS host and perform the TLS handshake over the socket. Uses ``kms_connect_callback`` when one is provided, otherwise connects diff --git a/pymongo/synchronous/aggregation.py b/pymongo/synchronous/aggregation.py index df49bbfbdd..2aad007143 100644 --- a/pymongo/synchronous/aggregation.py +++ b/pymongo/synchronous/aggregation.py @@ -17,7 +17,7 @@ from __future__ import annotations from collections.abc import Callable, Mapping, MutableMapping -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any from pymongo import common from pymongo.collation import validate_collation_or_none @@ -47,13 +47,13 @@ class _AggregationCommand: def __init__( self, - target: Union[Database[Any], Collection[Any]], + target: Database[Any] | Collection[Any], cursor_class: type[CommandCursor[Any]], pipeline: _Pipeline, options: MutableMapping[str, Any], - let: Optional[Mapping[str, Any]] = None, - user_fields: Optional[MutableMapping[str, Any]] = None, - result_processor: Optional[Callable[[Mapping[str, Any]], None]] = None, + let: Mapping[str, Any] | None = None, + user_fields: MutableMapping[str, Any] | None = None, + result_processor: Callable[[Mapping[str, Any]], None] | None = None, comment: Any = None, ) -> None: if "explain" in options: @@ -103,10 +103,10 @@ def __init__( self._collation = validate_collation_or_none(options.pop("collation", None)) self._max_await_time_ms = options.pop("maxAwaitTimeMS", None) - self._write_preference: Optional[_AggWritePref] = None + self._write_preference: _AggWritePref | None = None @property - def _aggregation_target(self) -> Union[str, int]: + def _aggregation_target(self) -> str | int: """The argument to pass to the aggregate command.""" raise NotImplementedError @@ -124,9 +124,7 @@ def _database(self) -> Database[Any]: """The database against which the aggregation command is run.""" raise NotImplementedError - def get_read_preference( - self, session: Optional[ClientSession] - ) -> Union[_AggWritePref, _ServerMode]: + def get_read_preference(self, session: ClientSession | None) -> _AggWritePref | _ServerMode: if self._write_preference: return self._write_preference pref = self._target._read_preference_for(session) @@ -136,7 +134,7 @@ def get_read_preference( def get_cursor( self, - session: Optional[ClientSession], + session: ClientSession | None, server: Server, conn: Connection, read_preference: _ServerMode, diff --git a/pymongo/synchronous/auth_oidc.py b/pymongo/synchronous/auth_oidc.py index 7d242bc326..f09e156b5b 100644 --- a/pymongo/synchronous/auth_oidc.py +++ b/pymongo/synchronous/auth_oidc.py @@ -21,7 +21,7 @@ import time from collections.abc import Mapping, MutableMapping from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any import bson from pymongo._csot import remaining @@ -54,7 +54,7 @@ class _OIDCAuthenticator(_OIDCAuthenticatorBase): else: lock: threading.Lock = field(default_factory=_create_lock) # type: ignore[assignment, no-redef] - def reauthenticate(self, conn: Connection) -> Optional[Mapping[str, Any]]: + def reauthenticate(self, conn: Connection) -> Mapping[str, Any] | None: """Handle a reauthenticate from the server.""" # Invalidate the token for the connection. self._invalidate(conn.oidc_token_gen_id) @@ -63,7 +63,7 @@ def reauthenticate(self, conn: Connection) -> Optional[Mapping[str, Any]]: return self._authenticate_machine(conn) return self._authenticate_human(conn) - def authenticate(self, conn: Connection) -> Optional[Mapping[str, Any]]: + def authenticate(self, conn: Connection) -> Mapping[str, Any] | None: """Handle an initial authenticate request.""" # First handle speculative auth. # If it succeeded, we are done. @@ -95,7 +95,7 @@ def _authenticate_machine(self, conn: Connection) -> Mapping[str, Any]: raise return self._sasl_start_jwt(conn) - def _authenticate_human(self, conn: Connection) -> Optional[Mapping[str, Any]]: + def _authenticate_human(self, conn: Connection) -> Mapping[str, Any] | None: # If we have a cached access token, try a JwtStepRequest. # authentication fails with error code 18, invalidate the access token, # and try to authenticate again. If authentication fails for any other @@ -128,9 +128,9 @@ def _authenticate_human(self, conn: Connection) -> Optional[Mapping[str, Any]]: # Attempt to authenticate with a JwtStepRequest. return self._sasl_continue_jwt(conn, start_resp) - def _get_access_token(self) -> Optional[str]: + def _get_access_token(self) -> str | None: properties = self.properties - cb: Union[None, OIDCCallback] + cb: None | OIDCCallback resp: OIDCCallbackResult is_human = properties.human_callback is not None @@ -219,7 +219,7 @@ def _sasl_start_jwt(self, conn: Connection) -> Mapping[str, Any]: def _authenticate_oidc( credentials: MongoCredential, conn: Connection, reauthenticate: bool -) -> Optional[Mapping[str, Any]]: +) -> Mapping[str, Any] | None: """Authenticate using MONGODB-OIDC.""" authenticator = _get_authenticator(credentials, conn.address, _OIDCAuthenticator) if reauthenticate: diff --git a/pymongo/synchronous/bulk.py b/pymongo/synchronous/bulk.py index 02a728d37f..ab50a96522 100644 --- a/pymongo/synchronous/bulk.py +++ b/pymongo/synchronous/bulk.py @@ -25,8 +25,6 @@ from typing import ( TYPE_CHECKING, Any, - Optional, - Union, ) from pymongo import _csot @@ -101,7 +99,7 @@ def unack_write( max_doc_size: int, docs: list[Mapping[str, Any]], client: MongoClient[Any], - ) -> Optional[Mapping[str, Any]]: + ) -> Mapping[str, Any] | None: """Send an unacknowledged batch write command.""" # Historically the STARTED log omits the documents while the published # CommandStartedEvent includes them, so log ``cmd`` but publish a copy @@ -122,7 +120,7 @@ def unack_write( def _execute_batch_unack( self, - bwc: Union[_BulkWriteContext, _EncryptedBulkWriteContext], + bwc: _BulkWriteContext | _EncryptedBulkWriteContext, cmd: dict[str, Any], ops: list[Mapping[str, Any]], client: MongoClient[Any], @@ -149,7 +147,7 @@ def _execute_batch_unack( def _execute_batch( self, - bwc: Union[_BulkWriteContext, _EncryptedBulkWriteContext], + bwc: _BulkWriteContext | _EncryptedBulkWriteContext, cmd: dict[str, Any], ops: list[Mapping[str, Any]], client: MongoClient[Any], @@ -173,12 +171,12 @@ def _execute_command( self, generator: Iterator[Any], write_concern: WriteConcern, - session: Optional[ClientSession], + session: ClientSession | None, conn: Connection, - op_id: Optional[int], + op_id: int | None, retryable: bool, full_result: MutableMapping[str, Any], - final_write_concern: Optional[WriteConcern] = None, + final_write_concern: WriteConcern | None = None, ) -> None: db_name = self.collection.database.name client = self.collection.database.client @@ -276,7 +274,7 @@ def execute_command( self, generator: Iterator[Any], write_concern: WriteConcern, - session: Optional[ClientSession], + session: ClientSession | None, operation: str, ) -> dict[str, Any]: """Execute using write commands.""" @@ -295,7 +293,7 @@ def execute_command( op_id = _generate_op_id_or_none(client._event_listeners) def retryable_bulk( - session: Optional[ClientSession], conn: Connection, retryable: bool + session: ClientSession | None, conn: Connection, retryable: bool ) -> None: self._execute_command( generator, @@ -423,7 +421,7 @@ def execute_no_results( def execute( self, write_concern: WriteConcern, - session: Optional[ClientSession], + session: ClientSession | None, operation: str, ) -> Any: """Execute operations.""" diff --git a/pymongo/synchronous/change_stream.py b/pymongo/synchronous/change_stream.py index 0afaffe659..d4f15594ee 100644 --- a/pymongo/synchronous/change_stream.py +++ b/pymongo/synchronous/change_stream.py @@ -16,7 +16,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from bson import _bson_to_dict from pymongo import _csot @@ -60,13 +60,13 @@ class ChangeStream(_AgnosticChangeStream[_DocumentType, "MongoClient[_DocumentTy .. seealso:: The MongoDB documentation on `changeStreams `_. """ - _session: Optional[ClientSession] + _session: ClientSession | None def _initialize_cursor(self) -> None: # Initialize cursor. self._cursor = self._create_cursor() - def _run_aggregation_cmd(self, session: Optional[ClientSession]) -> CommandCursor: # type: ignore[type-arg] + def _run_aggregation_cmd(self, session: ClientSession | None) -> CommandCursor: # type: ignore[type-arg] """Run the full aggregation pipeline for this ChangeStream and return the corresponding CommandCursor. """ @@ -148,7 +148,7 @@ def next(self) -> _DocumentType: __next__ = next @_csot.apply - def try_next(self) -> Optional[_DocumentType]: + def try_next(self) -> _DocumentType | None: """Advance the cursor without blocking indefinitely. This method returns the next change document without waiting diff --git a/pymongo/synchronous/client_bulk.py b/pymongo/synchronous/client_bulk.py index 3af8fdf6b9..b649313932 100644 --- a/pymongo/synchronous/client_bulk.py +++ b/pymongo/synchronous/client_bulk.py @@ -25,8 +25,6 @@ from typing import ( TYPE_CHECKING, Any, - Optional, - Union, ) from pymongo import _csot @@ -86,7 +84,7 @@ def write_command( bwc: _ClientBulkWriteContext, cmd: MutableMapping[str, Any], request_id: int, - msg: Union[bytes, dict[str, Any]], + msg: bytes | dict[str, Any], op_docs: list[Mapping[str, Any]], ns_docs: list[Mapping[str, Any]], client: MongoClient[Any], @@ -117,7 +115,7 @@ def unack_write( op_docs: list[Mapping[str, Any]], ns_docs: list[Mapping[str, Any]], client: MongoClient[Any], - ) -> Optional[Mapping[str, Any]]: + ) -> Mapping[str, Any] | None: """Send an unacknowledged client-level batch write command.""" # Historically the STARTED log omits the ops/nsInfo while the published # CommandStartedEvent includes them, so log ``cmd`` but publish a copy @@ -171,7 +169,7 @@ def _process_results_cursor( full_result: MutableMapping[str, Any], result: MutableMapping[str, Any], conn: Connection, - session: Optional[ClientSession], + session: ClientSession | None, ) -> None: """Internal helper for processing the server reply command cursor.""" if result.get("cursor"): @@ -221,12 +219,12 @@ def _process_results_cursor( def _execute_command( self, write_concern: WriteConcern, - session: Optional[ClientSession], + session: ClientSession | None, conn: Connection, - op_id: Optional[int], + op_id: int | None, retryable: bool, full_result: MutableMapping[str, Any], - final_write_concern: Optional[WriteConcern] = None, + final_write_concern: WriteConcern | None = None, ) -> None: """Internal helper for executing batches of bulkWrite commands.""" db_name = "admin" @@ -359,7 +357,7 @@ def _execute_command( def execute_command( self, - session: Optional[ClientSession], + session: ClientSession | None, operation: str, ) -> MutableMapping[str, Any]: """Execute commands with w=1 WriteConcern.""" @@ -380,7 +378,7 @@ def execute_command( op_id = _generate_op_id_or_none(self.client._event_listeners) def retryable_bulk( - session: Optional[ClientSession], + session: ClientSession | None, conn: Connection, retryable: bool, ) -> None: @@ -471,7 +469,7 @@ def execute_no_results( def execute( self, - session: Optional[ClientSession], + session: ClientSession | None, operation: str, ) -> Any: """Execute operations.""" diff --git a/pymongo/synchronous/client_session.py b/pymongo/synchronous/client_session.py index b536e053c8..c9533c77dd 100644 --- a/pymongo/synchronous/client_session.py +++ b/pymongo/synchronous/client_session.py @@ -143,7 +143,6 @@ TYPE_CHECKING, Any, Callable, - Optional, TypeVar, ) @@ -182,7 +181,7 @@ _IS_SYNC = True -_SESSION: ContextVar[Optional[ClientSession]] = ContextVar("SESSION", default=None) +_SESSION: ContextVar[ClientSession | None] = ContextVar("SESSION", default=None) class _BoundSessionContext: @@ -190,7 +189,7 @@ class _BoundSessionContext: def __init__(self, session: ClientSession, end_session: bool) -> None: self._session = session - self._session_token: Optional[Token[ClientSession]] = None + self._session_token: Token[ClientSession] | None = None self._end_session = end_session def __enter__(self) -> ClientSession: @@ -216,9 +215,9 @@ def __enter__(self) -> _TransactionContext: def __exit__( self, - exc_type: Optional[type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: if self.__session.in_transaction: if exc_val is None: @@ -231,7 +230,7 @@ class _Transaction(_TransactionBase["Connection"]): """Internal class to hold transaction information in a ClientSession.""" _conn_mgr_cls = _ConnectionManager - conn_mgr: Optional[_ConnectionManager] + conn_mgr: _ConnectionManager | None def unpin(self) -> None: self.pinned_address = None @@ -299,10 +298,10 @@ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: def with_transaction( self, callback: Callable[[ClientSession], _T], - read_concern: Optional[ReadConcern] = None, - write_concern: Optional[WriteConcern] = None, - read_preference: Optional[_ServerMode] = None, - max_commit_time_ms: Optional[int] = None, + read_concern: ReadConcern | None = None, + write_concern: WriteConcern | None = None, + read_preference: _ServerMode | None = None, + max_commit_time_ms: int | None = None, ) -> _T: """Execute a callback in a transaction. @@ -398,7 +397,7 @@ def callback(session, custom_arg, custom_kwarg=None): """ start_time = time.monotonic() retry = 0 - last_error: Optional[BaseException] = None + last_error: BaseException | None = None while True: if retry: # Implement exponential backoff on retry. jitter = random.random() # noqa: S311 @@ -454,10 +453,10 @@ def callback(session, custom_arg, custom_kwarg=None): def start_transaction( self, - read_concern: Optional[ReadConcern] = None, - write_concern: Optional[WriteConcern] = None, - read_preference: Optional[_ServerMode] = None, - max_commit_time_ms: Optional[int] = None, + read_concern: ReadConcern | None = None, + write_concern: WriteConcern | None = None, + read_preference: _ServerMode | None = None, + max_commit_time_ms: int | None = None, ) -> AbstractContextManager[Any]: """Start a multi-statement transaction. @@ -570,7 +569,7 @@ def _finish_transaction_with_retry(self, command_name: str) -> dict[str, Any]: """ def func( - _session: Optional[ClientSession], conn: Connection, _retryable: bool + _session: ClientSession | None, conn: Connection, _retryable: bool ) -> dict[str, Any]: return self._finish_transaction(conn, command_name) diff --git a/pymongo/synchronous/collection.py b/pymongo/synchronous/collection.py index 931001e179..e4abb141a0 100644 --- a/pymongo/synchronous/collection.py +++ b/pymongo/synchronous/collection.py @@ -25,9 +25,7 @@ Callable, Generic, NoReturn, - Optional, TypeVar, - Union, cast, overload, ) @@ -94,14 +92,14 @@ _FIND_AND_MODIFY_DOC_FIELDS = {"value": 1} -_WriteOp = Union[ - InsertOne[_DocumentType], - DeleteOne, - DeleteMany, - ReplaceOne[_DocumentType], - UpdateOne, - UpdateMany, -] +_WriteOp = ( + InsertOne[_DocumentType] + | DeleteOne + | DeleteMany + | ReplaceOne[_DocumentType] + | UpdateOne + | UpdateMany +) class ReturnDocument: @@ -136,12 +134,12 @@ def __init__( self, database: Database[_DocumentType], name: str, - create: Optional[bool] = False, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, - session: Optional[ClientSession] = None, + create: bool | None = False, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, + session: ClientSession | None = None, **kwargs: Any, ) -> None: """Get / create a Mongo collection. @@ -333,26 +331,26 @@ def database(self) -> Database[_DocumentType]: def with_options( self, codec_options: None = None, - read_preference: Optional[_ServerMode] = ..., - write_concern: Optional[WriteConcern] = ..., - read_concern: Optional[ReadConcern] = ..., + read_preference: _ServerMode | None = ..., + write_concern: WriteConcern | None = ..., + read_concern: ReadConcern | None = ..., ) -> Collection[_DocumentType]: ... @overload def with_options( self, codec_options: bson.CodecOptions[_DocumentTypeArg], - read_preference: Optional[_ServerMode] = ..., - write_concern: Optional[WriteConcern] = ..., - read_concern: Optional[ReadConcern] = ..., + read_preference: _ServerMode | None = ..., + write_concern: WriteConcern | None = ..., + read_concern: ReadConcern | None = ..., ) -> Collection[_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, + codec_options: bson.CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> Collection[_DocumentType] | Collection[_DocumentTypeArg]: """Get a clone of this collection changing the specified settings. @@ -417,18 +415,18 @@ def __call__(self, *args: Any, **kwargs: Any) -> NoReturn: def watch( self, - pipeline: Optional[_Pipeline] = None, - full_document: Optional[str] = None, - resume_after: Optional[Mapping[str, Any]] = None, - max_await_time_ms: Optional[int] = None, - batch_size: Optional[int] = None, - collation: Optional[_CollationIn] = None, - start_at_operation_time: Optional[Timestamp] = None, - session: Optional[ClientSession] = None, - start_after: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, - full_document_before_change: Optional[str] = None, - show_expanded_events: Optional[bool] = None, + pipeline: _Pipeline | None = None, + full_document: str | None = None, + resume_after: Mapping[str, Any] | None = None, + max_await_time_ms: int | None = None, + batch_size: int | None = None, + collation: _CollationIn | None = None, + start_at_operation_time: Timestamp | None = None, + session: ClientSession | None = None, + start_after: Mapping[str, Any] | None = None, + comment: Any | None = None, + full_document_before_change: str | None = None, + show_expanded_events: bool | None = None, ) -> CollectionChangeStream[_DocumentType]: """Watch changes on this collection. @@ -560,16 +558,16 @@ def _command( self, conn: Connection, command: MutableMapping[str, Any], - read_preference: Optional[_ServerMode] = None, - codec_options: Optional[CodecOptions[Mapping[str, Any]]] = None, + read_preference: _ServerMode | None = None, + codec_options: CodecOptions[Mapping[str, Any]] | None = None, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - read_concern: Optional[ReadConcern] = None, - write_concern: Optional[WriteConcern] = None, - collation: Optional[_CollationIn] = None, - session: Optional[ClientSession] = None, + allowable_errors: Sequence[str | int] | None = None, + read_concern: ReadConcern | None = None, + write_concern: WriteConcern | None = None, + collation: _CollationIn | None = None, + session: ClientSession | None = None, retryable_write: bool = False, - user_fields: Optional[Any] = None, + user_fields: Any | None = None, ) -> Mapping[str, Any]: """Internal command helper. @@ -618,9 +616,9 @@ def _create_helper( self, name: str, options: MutableMapping[str, Any], - collation: Optional[_CollationIn], - session: Optional[ClientSession], - encrypted_fields: Optional[Mapping[str, Any]] = None, + collation: _CollationIn | None, + session: ClientSession | None, + encrypted_fields: Mapping[str, Any] | None = None, qev2_required: bool = False, ) -> None: """Sends a create command with the given options.""" @@ -633,9 +631,7 @@ def _create_helper( options["size"] = float(options["size"]) cmd.update(options) - def inner( - session: Optional[ClientSession], conn: Connection, _retryable_write: bool - ) -> None: + def inner(session: ClientSession | None, conn: Connection, _retryable_write: bool) -> None: if qev2_required and conn.max_wire_version < 21: raise ConfigurationError( "Driver support of Queryable Encryption is incompatible with server. " @@ -657,7 +653,7 @@ def inner( def _create( self, options: MutableMapping[str, Any], - session: Optional[ClientSession], + session: ClientSession | None, ) -> None: collation = validate_collation_or_none(options.pop("collation", None)) encrypted_fields = options.pop("encryptedFields", None) @@ -684,10 +680,10 @@ def bulk_write( self, requests: Sequence[_WriteOp[_DocumentType]], ordered: bool = True, - bypass_document_validation: Optional[bool] = None, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, - let: Optional[Mapping[str, Any]] = None, + bypass_document_validation: bool | None = None, + session: ClientSession | None = None, + comment: Any | None = None, + let: Mapping[str, Any] | None = None, ) -> BulkWriteResult: """Send a batch of write operations to the server. @@ -782,10 +778,10 @@ def _insert_one( doc: Mapping[str, Any], ordered: bool, write_concern: WriteConcern, - op_id: Optional[int], - bypass_doc_val: Optional[bool], - session: Optional[ClientSession], - comment: Optional[Any] = None, + op_id: int | None, + bypass_doc_val: bool | None, + session: ClientSession | None, + comment: Any | None = None, ) -> Any: """Internal helper for inserting a single document.""" write_concern = write_concern or self.write_concern @@ -795,7 +791,7 @@ def _insert_one( command["comment"] = comment def _insert_command( - session: Optional[ClientSession], conn: Connection, retryable_write: bool + session: ClientSession | None, conn: Connection, retryable_write: bool ) -> None: if bypass_doc_val is not None: command["bypassDocumentValidation"] = bypass_doc_val @@ -822,10 +818,10 @@ def _insert_command( def insert_one( self, - document: Union[_DocumentType, RawBSONDocument], - bypass_document_validation: Optional[bool] = None, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + document: _DocumentType | RawBSONDocument, + bypass_document_validation: bool | None = None, + session: ClientSession | None = None, + comment: Any | None = None, ) -> InsertOneResult: """Insert a single document. @@ -887,11 +883,11 @@ def insert_one( @_csot.apply def insert_many( self, - documents: Iterable[Union[_DocumentType, RawBSONDocument]], + documents: Iterable[_DocumentType | RawBSONDocument], ordered: bool = True, - bypass_document_validation: Optional[bool] = None, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + bypass_document_validation: bool | None = None, + session: ClientSession | None = None, + comment: Any | None = None, ) -> InsertManyResult: """Insert an iterable of documents. @@ -963,22 +959,22 @@ def _update( self, conn: Connection, criteria: Mapping[str, Any], - document: Union[Mapping[str, Any], _Pipeline], + document: Mapping[str, Any] | _Pipeline, upsert: bool = False, multi: bool = False, - write_concern: Optional[WriteConcern] = None, - op_id: Optional[int] = None, + write_concern: WriteConcern | None = None, + op_id: int | None = None, ordered: bool = True, - bypass_doc_val: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, + bypass_doc_val: bool | None = None, + collation: _CollationIn | None = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, retryable_write: bool = False, - let: Optional[Mapping[str, Any]] = None, - sort: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, - ) -> Optional[Mapping[str, Any]]: + let: Mapping[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, + comment: Any | None = None, + ) -> Mapping[str, Any] | None: """Internal update / replace helper.""" validate_boolean("upsert", upsert) collation = validate_collation_or_none(collation) @@ -1054,27 +1050,27 @@ def _update( def _update_retryable( self, criteria: Mapping[str, Any], - document: Union[Mapping[str, Any], _Pipeline], + document: Mapping[str, Any] | _Pipeline, operation: str, upsert: bool = False, multi: bool = False, - write_concern: Optional[WriteConcern] = None, - op_id: Optional[int] = None, + write_concern: WriteConcern | None = None, + op_id: int | None = None, ordered: bool = True, - bypass_doc_val: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - sort: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, - ) -> Optional[Mapping[str, Any]]: + bypass_doc_val: bool | None = None, + collation: _CollationIn | None = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, + comment: Any | None = None, + ) -> Mapping[str, Any] | None: """Internal update / replace helper.""" def _update( - session: Optional[ClientSession], conn: Connection, retryable_write: bool - ) -> Optional[Mapping[str, Any]]: + session: ClientSession | None, conn: Connection, retryable_write: bool + ) -> Mapping[str, Any] | None: return self._update( conn, criteria, @@ -1107,13 +1103,13 @@ def replace_one( filter: Mapping[str, Any], replacement: Mapping[str, Any], upsert: bool = False, - bypass_document_validation: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - sort: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + bypass_document_validation: bool | None = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> UpdateResult: """Replace a single document matching the filter. @@ -1213,16 +1209,16 @@ def replace_one( def update_one( self, filter: Mapping[str, Any], - update: Union[Mapping[str, Any], _Pipeline], + update: Mapping[str, Any] | _Pipeline, upsert: bool = False, - bypass_document_validation: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - sort: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + bypass_document_validation: bool | None = None, + collation: _CollationIn | None = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, + sort: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> UpdateResult: """Update a single document matching the filter. @@ -1332,15 +1328,15 @@ def update_one( def update_many( self, filter: Mapping[str, Any], - update: Union[Mapping[str, Any], _Pipeline], + update: Mapping[str, Any] | _Pipeline, upsert: bool = False, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - bypass_document_validation: Optional[bool] = None, - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + bypass_document_validation: bool | None = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> UpdateResult: """Update one or more documents that match the filter. @@ -1431,9 +1427,9 @@ def update_many( def drop( self, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, - encrypted_fields: Optional[Mapping[str, Any]] = None, + session: ClientSession | None = None, + comment: Any | None = None, + encrypted_fields: Mapping[str, Any] | None = None, ) -> None: """Alias for :meth:`~pymongo.database.Database.drop_collection`. @@ -1477,15 +1473,15 @@ def _delete( conn: Connection, criteria: Mapping[str, Any], multi: bool, - write_concern: Optional[WriteConcern] = None, - op_id: Optional[int] = None, + write_concern: WriteConcern | None = None, + op_id: int | None = None, ordered: bool = True, - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, retryable_write: bool = False, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> Mapping[str, Any]: """Internal delete helper.""" common.validate_is_mapping("filter", criteria) @@ -1528,19 +1524,19 @@ def _delete_retryable( self, criteria: Mapping[str, Any], multi: bool, - write_concern: Optional[WriteConcern] = None, - op_id: Optional[int] = None, + write_concern: WriteConcern | None = None, + op_id: int | None = None, ordered: bool = True, - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> Mapping[str, Any]: """Internal delete helper.""" def _delete( - session: Optional[ClientSession], conn: Connection, retryable_write: bool + session: ClientSession | None, conn: Connection, retryable_write: bool ) -> Mapping[str, Any]: return self._delete( conn, @@ -1567,11 +1563,11 @@ def _delete( def delete_one( self, filter: Mapping[str, Any], - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> DeleteResult: """Delete a single document matching the filter. @@ -1632,11 +1628,11 @@ def delete_one( def delete_many( self, filter: Mapping[str, Any], - collation: Optional[_CollationIn] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + collation: _CollationIn | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, ) -> DeleteResult: """Delete one or more documents matching the filter. @@ -1695,8 +1691,8 @@ def delete_many( ) def find_one( - self, filter: Optional[Any] = None, *args: Any, **kwargs: Any - ) -> Optional[_DocumentType]: + self, filter: Any | None = None, *args: Any, **kwargs: Any + ) -> _DocumentType | None: """Get a single document from the database. All arguments to :meth:`find` are also valid arguments for @@ -1971,11 +1967,11 @@ def find_raw_batches(self, *args: Any, **kwargs: Any) -> RawBatchCursor[_Documen def _count_cmd( self, - session: Optional[ClientSession], + session: ClientSession | None, conn: Connection, - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, cmd: dict[str, Any], - collation: Optional[Collation], + collation: Collation | None, ) -> int: """Internal count command helper.""" res = self._command( @@ -1992,11 +1988,11 @@ def _count_cmd( def _aggregate_one_result( self, conn: Connection, - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, cmd: dict[str, Any], - collation: Optional[_CollationIn], - session: Optional[ClientSession], - ) -> Optional[Mapping[str, Any]]: + collation: _CollationIn | None, + session: ClientSession | None, + ) -> Mapping[str, Any] | None: """Internal helper to run an aggregate that returns a single result.""" result = self._command( conn, @@ -2014,7 +2010,7 @@ def _aggregate_one_result( batch = result["cursor"]["firstBatch"] return batch[0] if batch else None - def estimated_document_count(self, comment: Optional[Any] = None, **kwargs: Any) -> int: + def estimated_document_count(self, comment: Any | None = None, **kwargs: Any) -> int: """Get an estimate of the number of documents in this collection using collection metadata. @@ -2047,10 +2043,10 @@ def estimated_document_count(self, comment: Optional[Any] = None, **kwargs: Any) kwargs["comment"] = comment def _cmd( - session: Optional[ClientSession], + session: ClientSession | None, _server: Server, conn: Connection, - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, ) -> int: cmd: dict[str, Any] = {"count": self._name} cmd.update(kwargs) @@ -2061,8 +2057,8 @@ def _cmd( def count_documents( self, filter: Mapping[str, Any], - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> int: """Count the number of documents in this collection. @@ -2133,10 +2129,10 @@ def count_documents( collation = validate_collation_or_none(kwargs.pop("collation", None)) def _cmd( - session: Optional[ClientSession], + session: ClientSession | None, _server: Server, conn: Connection, - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, ) -> int: cmd: dict[str, Any] = {"aggregate": self._name, "pipeline": pipeline, "cursor": {}} cmd.update(kwargs) @@ -2150,10 +2146,10 @@ def _cmd( def _retryable_non_cursor_read( self, func: Callable[ - [Optional[ClientSession], Server, Connection, Optional[_ServerMode]], + [ClientSession | None, Server, Connection, _ServerMode | None], T, ], - session: Optional[ClientSession], + session: ClientSession | None, operation: str, ) -> T: """Non-cursor read helper to handle implicit session creation.""" @@ -2164,8 +2160,8 @@ def _retryable_non_cursor_read( def create_indexes( self, indexes: Sequence[IndexModel], - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> list[str]: """Create one or more indexes on this collection. @@ -2210,7 +2206,7 @@ def create_indexes( @_csot.apply def _create_indexes( - self, indexes: Sequence[IndexModel], session: Optional[ClientSession], **kwargs: Any + self, indexes: Sequence[IndexModel], session: ClientSession | None, **kwargs: Any ) -> list[str]: """Internal createIndexes helper. @@ -2224,7 +2220,7 @@ def _create_indexes( names = [] def inner( - session: Optional[ClientSession], conn: Connection, _retryable_write: bool + session: ClientSession | None, conn: Connection, _retryable_write: bool ) -> list[str]: def gen_indexes() -> Iterator[Mapping[str, Any]]: for index in indexes: @@ -2253,8 +2249,8 @@ def gen_indexes() -> Iterator[Mapping[str, Any]]: def create_index( self, keys: _IndexKeyHint, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> str: """Creates an index on this collection. @@ -2370,8 +2366,8 @@ def create_index( def drop_indexes( self, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> None: """Drops all indexes on this collection. @@ -2404,8 +2400,8 @@ def drop_indexes( def drop_index( self, index_or_name: _IndexKeyHint, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> None: """Drops the specified index on this collection. @@ -2453,8 +2449,8 @@ def drop_index( def _drop_index( self, index_or_name: _IndexKeyHint, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> None: name = index_or_name @@ -2469,9 +2465,7 @@ def _drop_index( if comment is not None: cmd["comment"] = comment - def inner( - session: Optional[ClientSession], conn: Connection, _retryable_write: bool - ) -> None: + def inner(session: ClientSession | None, conn: Connection, _retryable_write: bool) -> None: self._command( conn, cmd, @@ -2485,8 +2479,8 @@ def inner( def list_indexes( self, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, ) -> CommandCursor[MutableMapping[str, Any]]: """Get a cursor over the index documents for this collection. @@ -2523,8 +2517,8 @@ def list_indexes( def _list_indexes( self, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, ) -> CommandCursor[MutableMapping[str, Any]]: codec_options: CodecOptions[Mapping[str, Any]] = CodecOptions(SON) coll = cast( @@ -2534,7 +2528,7 @@ def _list_indexes( read_pref = (session and session._txn_read_preference()) or ReadPreference.PRIMARY def _cmd( - session: Optional[ClientSession], + session: ClientSession | None, _server: Server, conn: Connection, read_preference: _ServerMode, @@ -2570,8 +2564,8 @@ def _cmd( def index_information( self, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, ) -> MutableMapping[str, Any]: """Get information on this collection's indexes. @@ -2612,9 +2606,9 @@ def index_information( def list_search_indexes( self, - name: Optional[str] = None, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + name: str | None = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> CommandCursor[Mapping[str, Any]]: """Return a cursor over search indexes for the current collection. @@ -2673,8 +2667,8 @@ def list_search_indexes( def create_search_index( self, - model: Union[Mapping[str, Any], SearchIndexModel], - session: Optional[ClientSession] = None, + model: Mapping[str, Any] | SearchIndexModel, + session: ClientSession | None = None, comment: Any = None, **kwargs: Any, ) -> str: @@ -2704,8 +2698,8 @@ def create_search_index( def create_search_indexes( self, models: list[SearchIndexModel], - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> list[str]: """Create multiple search indexes for the current collection. @@ -2728,8 +2722,8 @@ def create_search_indexes( def _create_search_indexes( self, models: list[SearchIndexModel], - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> list[str]: if comment is not None: @@ -2747,7 +2741,7 @@ def gen_indexes() -> Iterator[Mapping[str, Any]]: cmd.update(kwargs) def inner( - session: Optional[ClientSession], conn: Connection, _retryable_write: bool + session: ClientSession | None, conn: Connection, _retryable_write: bool ) -> list[str]: resp = self._command( conn, @@ -2765,8 +2759,8 @@ def inner( def drop_search_index( self, name: str, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> None: """Delete a search index by index name. @@ -2788,9 +2782,7 @@ def drop_search_index( if comment is not None: cmd["comment"] = comment - def inner( - session: Optional[ClientSession], conn: Connection, _retryable_write: bool - ) -> None: + def inner(session: ClientSession | None, conn: Connection, _retryable_write: bool) -> None: self._command( conn, cmd, @@ -2806,8 +2798,8 @@ def update_search_index( self, name: str, definition: Mapping[str, Any], - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> None: """Update a search index by replacing the existing index definition with the provided definition. @@ -2830,9 +2822,7 @@ def update_search_index( if comment is not None: cmd["comment"] = comment - def inner( - session: Optional[ClientSession], conn: Connection, _retryable_write: bool - ) -> None: + def inner(session: ClientSession | None, conn: Connection, _retryable_write: bool) -> None: self._command( conn, cmd, @@ -2846,8 +2836,8 @@ def inner( def options( self, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, ) -> MutableMapping[str, Any]: """Get the options set on this collection. @@ -2894,9 +2884,9 @@ def _aggregate( aggregation_command: type[_AggregationCommand], pipeline: _Pipeline, cursor_class: type[CommandCursor], # type: ignore[type-arg] - session: Optional[ClientSession], - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: ClientSession | None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> CommandCursor[_DocumentType]: if comment is not None: @@ -2922,9 +2912,9 @@ def _aggregate( def aggregate( self, pipeline: _Pipeline, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> CommandCursor[_DocumentType]: """Perform an aggregation using the aggregation framework on this @@ -3026,8 +3016,8 @@ def aggregate( def aggregate_raw_batches( self, pipeline: _Pipeline, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> RawBatchCursor[_DocumentType]: """Perform an aggregation and retrieve batches of raw BSON. @@ -3074,8 +3064,8 @@ def aggregate_raw_batches( def rename( self, new_name: str, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> MutableMapping[str, Any]: """Rename this collection. @@ -3125,7 +3115,7 @@ def rename( client = self._database.client def inner( - session: Optional[ClientSession], conn: Connection, _retryable_write: bool + session: ClientSession | None, conn: Connection, _retryable_write: bool ) -> MutableMapping[str, Any]: return conn.command( "admin", @@ -3141,10 +3131,10 @@ def inner( def distinct( self, key: str, - filter: Optional[Mapping[str, Any]] = None, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, - hint: Optional[_IndexKeyHint] = None, + filter: Mapping[str, Any] | None = None, + session: ClientSession | None = None, + comment: Any | None = None, + hint: _IndexKeyHint | None = None, **kwargs: Any, ) -> list[Any]: """Get a list of distinct values for `key` among all documents @@ -3200,10 +3190,10 @@ def distinct( hint = helpers_shared._index_document(hint) def _cmd( - session: Optional[ClientSession], + session: ClientSession | None, _server: Server, conn: Connection, - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, ) -> list: # type: ignore[type-arg] cmd = {"distinct": self._name, "key": key} cmd.update(kwargs) @@ -3228,14 +3218,14 @@ def _cmd( def _find_and_modify( self, filter: Mapping[str, Any], - projection: Optional[Union[Mapping[str, Any], Iterable[str]]], - sort: Optional[_IndexList], - upsert: Optional[bool] = None, + projection: Mapping[str, Any] | Iterable[str] | None, + sort: _IndexList | None, + upsert: bool | None = None, return_document: bool = ReturnDocument.BEFORE, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, **kwargs: Any, ) -> Any: """Internal findAndModify helper.""" @@ -3251,7 +3241,7 @@ def _find_and_modify( write_concern = self._write_concern_for_cmd(kwargs, session) def _find_and_modify_helper( - session: Optional[ClientSession], conn: Connection, retryable_write: bool + session: ClientSession | None, conn: Connection, retryable_write: bool ) -> Any: cmd = {"findAndModify": self._name, "query": filter, "new": return_document} if let is not None: @@ -3298,14 +3288,14 @@ def _find_and_modify_helper( def find_one_and_delete( self, filter: Mapping[str, Any], - projection: Optional[Union[Mapping[str, Any], Iterable[str]]] = None, - sort: Optional[_IndexList] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + projection: Mapping[str, Any] | Iterable[str] | None = None, + sort: _IndexList | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, - ) -> Optional[_DocumentType]: + ) -> _DocumentType | None: """Finds a single document and deletes it, returning the document. >>> db.test.count_documents({'x': 1}) @@ -3392,16 +3382,16 @@ def find_one_and_replace( self, filter: Mapping[str, Any], replacement: Mapping[str, Any], - projection: Optional[Union[Mapping[str, Any], Iterable[str]]] = None, - sort: Optional[_IndexList] = None, + projection: Mapping[str, Any] | Iterable[str] | None = None, + sort: _IndexList | None = None, upsert: bool = False, return_document: bool = ReturnDocument.BEFORE, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, - ) -> Optional[_DocumentType]: + ) -> _DocumentType | None: """Finds a single document and replaces it, returning either the original or the replaced document. @@ -3510,18 +3500,18 @@ def find_one_and_replace( def find_one_and_update( self, filter: Mapping[str, Any], - update: Union[Mapping[str, Any], _Pipeline], - projection: Optional[Union[Mapping[str, Any], Iterable[str]]] = None, - sort: Optional[_IndexList] = None, + update: Mapping[str, Any] | _Pipeline, + projection: Mapping[str, Any] | Iterable[str] | None = None, + sort: _IndexList | None = None, upsert: bool = False, return_document: bool = ReturnDocument.BEFORE, - array_filters: Optional[Sequence[Mapping[str, Any]]] = None, - hint: Optional[_IndexKeyHint] = None, - session: Optional[ClientSession] = None, - let: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + array_filters: Sequence[Mapping[str, Any]] | None = None, + hint: _IndexKeyHint | None = None, + session: ClientSession | None = None, + let: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, - ) -> Optional[_DocumentType]: + ) -> _DocumentType | None: """Finds a single document and updates it, returning either the original or the updated document. diff --git a/pymongo/synchronous/command_cursor.py b/pymongo/synchronous/command_cursor.py index d7fc3e39ca..b726b9557c 100644 --- a/pymongo/synchronous/command_cursor.py +++ b/pymongo/synchronous/command_cursor.py @@ -22,7 +22,6 @@ TYPE_CHECKING, Any, NoReturn, - Optional, ) from bson import CodecOptions, _convert_raw_document_lists_to_streams @@ -161,7 +160,7 @@ def next(self) -> _DocumentType: def __next__(self) -> _DocumentType: return self.next() - def _try_next(self, get_more_allowed: bool) -> Optional[_DocumentType]: + def _try_next(self, get_more_allowed: bool) -> _DocumentType | None: """Advance the cursor blocking for at most one getMore command.""" if not len(self._data) and not self._killed and get_more_allowed: self._refresh() @@ -170,7 +169,7 @@ def _try_next(self, get_more_allowed: bool) -> Optional[_DocumentType]: else: return None - def _next_batch(self, result: list, total: Optional[int] = None) -> bool: # type: ignore[type-arg] + def _next_batch(self, result: list, total: int | None = None) -> bool: # type: ignore[type-arg] """Get all or some available documents from the cursor.""" if not len(self._data) and not self._killed: self._refresh() @@ -185,7 +184,7 @@ def _next_batch(self, result: list, total: Optional[int] = None) -> bool: # typ else: return False - def try_next(self) -> Optional[_DocumentType]: + def try_next(self) -> _DocumentType | None: """Advance the cursor without blocking indefinitely. This method returns the next document without waiting @@ -211,10 +210,10 @@ def __init__( self, collection: Collection[_DocumentType], cursor_info: Mapping[str, Any], - address: Optional[_Address], + address: _Address | None, batch_size: int = 0, - max_await_time_ms: Optional[int] = None, - session: Optional[ClientSession] = None, + max_await_time_ms: int | None = None, + session: ClientSession | None = None, comment: Any = None, ) -> None: """Create a new cursor / iterator over raw batches of BSON data. @@ -239,9 +238,9 @@ def __init__( def _unpack_response( # type: ignore[override] self, response: _OpMsg, - cursor_id: Optional[int], + cursor_id: int | None, codec_options: CodecOptions[dict[str, Any]], - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, legacy_response: bool = False, ) -> list[Mapping[str, Any]]: raw_response = response.raw_response(cursor_id, user_fields=user_fields) diff --git a/pymongo/synchronous/command_runner.py b/pymongo/synchronous/command_runner.py index 2aa62e87ad..ad840e4b33 100644 --- a/pymongo/synchronous/command_runner.py +++ b/pymongo/synchronous/command_runner.py @@ -42,8 +42,6 @@ TYPE_CHECKING, Any, Callable, - Optional, - Union, cast, ) @@ -80,19 +78,19 @@ def _run_command( request_id: int, msg: bytes, *, - client: Optional[MongoClient[Any]], - session: Optional[ClientSession], - listeners: Optional[_EventListeners], - topology_id: Optional[ObjectId], + client: MongoClient[Any] | None, + session: ClientSession | None, + listeners: _EventListeners | None, + topology_id: ObjectId | None, codec_options: CodecOptions[_DocumentType], - user_fields: Optional[Mapping[str, Any]] = None, - orig: Optional[MutableMapping[str, Any]] = None, - op_id: Optional[int] = None, - command_name: Optional[str] = None, + user_fields: Mapping[str, Any] | None = None, + orig: MutableMapping[str, Any] | None = None, + op_id: int | None = None, + command_name: str | None = None, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, + allowable_errors: Sequence[str | int] | None = None, parse_write_concern_error: bool = False, - pool_opts: Optional[PoolOptions] = None, + pool_opts: PoolOptions | None = None, unacknowledged: bool = False, speculative_hello: bool = False, ensure_db: bool = False, @@ -100,9 +98,9 @@ def _run_command( max_doc_size: int = 0, more_to_come: bool = False, set_conn_more_to_come: bool = False, - unpack_res: Optional[Callable[..., Any]] = None, - cursor_id: Optional[int] = None, -) -> tuple[list[dict[str, Any]], Optional[_OpMsg], float]: + unpack_res: Callable[..., Any] | None = None, + cursor_id: int | None = None, +) -> tuple[list[dict[str, Any]], _OpMsg | None, float]: """Send ``msg`` over ``conn`` and return ``(docs, reply, duration_s)``, where ``duration_s`` is the round-trip duration in seconds. @@ -165,7 +163,7 @@ def _run_command( # Fast path: skip telemetry construction when logging and APM are disabled # Inline enabled check here for performance - telemetry: Optional[_CommandTelemetry] = None + telemetry: _CommandTelemetry | None = None if (topology_id is not None and _is_debug_enabled(_COMMAND_LOGGER)) or ( listeners is not None and listeners.enabled_for_commands ): @@ -177,7 +175,7 @@ def _run_command( else: start = time.monotonic() - reply: Optional[_OpMsg] = None + reply: _OpMsg | None = None docs: list[dict[str, Any]] = [{"ok": 1}] try: if more_to_come: @@ -248,11 +246,11 @@ def run_bulk_write_command( request_id: int, msg: bytes, *, - client: Optional[MongoClient[Any]], - orig: Optional[MutableMapping[str, Any]] = None, + client: MongoClient[Any] | None, + orig: MutableMapping[str, Any] | None = None, max_doc_size: int = 0, unacknowledged: bool = False, -) -> tuple[list[dict[str, Any]], Optional[_OpMsg], float]: +) -> tuple[list[dict[str, Any]], _OpMsg | None, float]: """Send a bulk write batch and return ``(docs, reply, duration_s)``. :param bwc: Bulk write context supplying the connection, session, listeners, etc. @@ -293,18 +291,18 @@ def run_cursor_command( request_id: int, msg: bytes, *, - client: Optional[MongoClient[Any]], - session: Optional[ClientSession], - listeners: Optional[_EventListeners], + client: MongoClient[Any] | None, + session: ClientSession | None, + listeners: _EventListeners | None, codec_options: CodecOptions[_DocumentType], command_name: str, - user_fields: Optional[Mapping[str, Any]] = None, - pool_opts: Optional[PoolOptions] = None, + user_fields: Mapping[str, Any] | None = None, + pool_opts: PoolOptions | None = None, max_doc_size: int = 0, more_to_come: bool = False, - unpack_res: Optional[Callable[..., Any]] = None, - cursor_id: Optional[int] = None, -) -> tuple[list[dict[str, Any]], Optional[_OpMsg], datetime.timedelta]: + unpack_res: Callable[..., Any] | None = None, + cursor_id: int | None = None, +) -> tuple[list[dict[str, Any]], _OpMsg | None, datetime.timedelta]: """Run a cursor ``find``/``getMore`` operation over ``conn``. :param conn: The Connection to send on. @@ -354,22 +352,22 @@ def run_command( conn: Connection, dbname: str, spec: MutableMapping[str, Any], - read_preference: Optional[_ServerMode], + read_preference: _ServerMode | None, codec_options: CodecOptions[_DocumentType], - session: Optional[ClientSession], - client: Optional[MongoClient[Any]], + session: ClientSession | None, + client: MongoClient[Any] | None, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - listeners: Optional[_EventListeners] = None, - max_bson_size: Optional[int] = None, - read_concern: Optional[ReadConcern] = None, + allowable_errors: Sequence[str | int] | None = None, + listeners: _EventListeners | None = None, + max_bson_size: int | None = None, + read_concern: ReadConcern | None = None, parse_write_concern_error: bool = False, - collation: Optional[_CollationIn] = None, - compression_ctx: Union[SnappyContext, ZlibContext, ZstdContext, None] = None, + collation: _CollationIn | None = None, + compression_ctx: SnappyContext | ZlibContext | ZstdContext | None = None, unacknowledged: bool = False, - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, exhaust_allowed: bool = False, - write_concern: Optional[WriteConcern] = None, + write_concern: WriteConcern | None = None, ) -> _DocumentType: """Encode and execute a command over ``conn``, or raise socket.error. diff --git a/pymongo/synchronous/cursor.py b/pymongo/synchronous/cursor.py index 6d6dccf3de..d9000105b3 100644 --- a/pymongo/synchronous/cursor.py +++ b/pymongo/synchronous/cursor.py @@ -22,8 +22,6 @@ TYPE_CHECKING, Any, NoReturn, - Optional, - Union, cast, overload, ) @@ -87,7 +85,7 @@ def __getitem__(self, index: int) -> _DocumentType: ... @overload def __getitem__(self, index: slice) -> Cursor[_DocumentType]: ... - def __getitem__(self, index: Union[int, slice]) -> Union[_DocumentType, Cursor[_DocumentType]]: + def __getitem__(self, index: int | slice) -> _DocumentType | Cursor[_DocumentType]: """Get a single document or a slice of documents from this cursor. .. warning:: A :class:`~Cursor` is not a Python :class:`list`. Each @@ -219,7 +217,7 @@ def distinct(self, key: str) -> list[Any]: return self._collection.distinct(key, session=self._session, **options) - def _send_message(self, operation: Union[_Query, _GetMore]) -> None: + def _send_message(self, operation: _Query | _GetMore) -> None: """Send a query or getmore operation and handles the response. If operation is ``None`` this is an exhaust cursor, which reads @@ -385,7 +383,7 @@ def next(self) -> _DocumentType: else: raise StopIteration - def _next_batch(self, result: list, total: Optional[int] = None) -> bool: # type: ignore[type-arg] + def _next_batch(self, result: list, total: int | None = None) -> bool: # type: ignore[type-arg] """Get all or some documents from the cursor.""" if self._empty: return False @@ -430,9 +428,9 @@ def __init__(self, collection: Collection[_DocumentType], *args: Any, **kwargs: def _unpack_response( self, response: _OpMsg, - cursor_id: Optional[int], + cursor_id: int | None, codec_options: CodecOptions[Mapping[str, Any]], - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, legacy_response: bool = False, ) -> list[_DocumentOut]: raw_response = response.raw_response(cursor_id, user_fields=user_fields) diff --git a/pymongo/synchronous/cursor_base.py b/pymongo/synchronous/cursor_base.py index 0106f12645..70ba49f999 100644 --- a/pymongo/synchronous/cursor_base.py +++ b/pymongo/synchronous/cursor_base.py @@ -18,7 +18,7 @@ from abc import abstractmethod from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any, Optional, Union, cast +from typing import TYPE_CHECKING, Any, cast from pymongo import _csot from pymongo.cursor_shared import _CURSOR_DOC_FIELDS, _AgnosticCursorBase, _split_message @@ -38,7 +38,7 @@ def _operation_to_command( - operation: Union[_Query, _GetMore], + operation: _Query | _GetMore, conn: Connection, apply_timeout: bool, ) -> tuple[dict[str, Any], str]: @@ -55,7 +55,7 @@ class _ConnectionManager: """Used with exhaust cursors to ensure the connection is returned.""" def __init__(self, conn: Connection, more_to_come: bool): - self.conn: Optional[Connection] = conn + self.conn: Connection | None = conn self.more_to_come = more_to_come self._lock = _create_lock() @@ -73,24 +73,24 @@ class _CursorBase(_AgnosticCursorBase[_DocumentType]): """Synchronous cursor base class.""" @property - def session(self) -> Optional[ClientSession]: + def session(self) -> ClientSession | None: """The cursor's :class:`~pymongo.client_session.ClientSession`, or None. .. versionadded:: 3.6 """ - return cast("Optional[ClientSession]", super().session) + return cast("ClientSession | None", super().session) @abstractmethod - def _next_batch(self, result: list, total: Optional[int] = None) -> bool: # type: ignore[type-arg] + def _next_batch(self, result: list, total: int | None = None) -> bool: # type: ignore[type-arg] ... @abstractmethod def _unpack_response( self, response: _OpMsg, - cursor_id: Optional[int], + cursor_id: int | None, codec_options: Any, - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, legacy_response: bool = False, ) -> Sequence[_DocumentOut]: ... @@ -98,7 +98,7 @@ def _unpack_response( def _run_with_conn( self, conn: Connection, - operation: Union[_Query, _GetMore], + operation: _Query | _GetMore, read_preference: _ServerMode, ) -> Response: """Execute a cursor operation on the given connection and return a Response. @@ -191,7 +191,7 @@ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: self.close() @_csot.apply - def to_list(self, length: Optional[int] = None) -> list[_DocumentType]: + def to_list(self, length: int | None = None) -> list[_DocumentType]: """Converts the contents of this cursor to a list more efficiently than ``[doc for doc in cursor]``. To use:: diff --git a/pymongo/synchronous/database.py b/pymongo/synchronous/database.py index 65c5d2a7f0..cd6be5c6a6 100644 --- a/pymongo/synchronous/database.py +++ b/pymongo/synchronous/database.py @@ -23,8 +23,6 @@ Any, Generic, NoReturn, - Optional, - Union, cast, overload, ) @@ -62,10 +60,10 @@ def __init__( self, client: MongoClient[_DocumentType], name: str, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> None: """Get a database by client and name. @@ -149,26 +147,26 @@ def name(self) -> str: def with_options( self, codec_options: None = None, - read_preference: Optional[_ServerMode] = ..., - write_concern: Optional[WriteConcern] = ..., - read_concern: Optional[ReadConcern] = ..., + read_preference: _ServerMode | None = ..., + write_concern: WriteConcern | None = ..., + read_concern: ReadConcern | None = ..., ) -> Database[_DocumentType]: ... @overload def with_options( self, codec_options: bson.CodecOptions[_DocumentTypeArg], - read_preference: Optional[_ServerMode] = ..., - write_concern: Optional[WriteConcern] = ..., - read_concern: Optional[ReadConcern] = ..., + read_preference: _ServerMode | None = ..., + write_concern: WriteConcern | None = ..., + read_concern: ReadConcern | None = ..., ) -> Database[_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, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> Database[_DocumentType] | Database[_DocumentTypeArg]: """Get a clone of this database changing the specified settings. @@ -249,10 +247,10 @@ def __getitem__(self, name: str) -> Collection[_DocumentType]: def get_collection( self, name: str, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> Collection[_DocumentType]: """Get a :class:`~pymongo.collection.Collection` with the given name and options. @@ -302,7 +300,7 @@ def get_collection( def _get_encrypted_fields( self, kwargs: Mapping[str, Any], coll_name: str, ask_db: bool - ) -> Optional[Mapping[str, Any]]: + ) -> Mapping[str, Any] | None: encrypted_fields = kwargs.get("encryptedFields") if encrypted_fields: return cast(Mapping[str, Any], deepcopy(encrypted_fields)) @@ -344,18 +342,18 @@ def __bool__(self) -> NoReturn: def watch( self, - pipeline: Optional[_Pipeline] = None, - full_document: Optional[str] = None, - resume_after: Optional[Mapping[str, Any]] = None, - max_await_time_ms: Optional[int] = None, - batch_size: Optional[int] = None, - collation: Optional[_CollationIn] = None, - start_at_operation_time: Optional[Timestamp] = None, - session: Optional[ClientSession] = None, - start_after: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, - full_document_before_change: Optional[str] = None, - show_expanded_events: Optional[bool] = None, + pipeline: _Pipeline | None = None, + full_document: str | None = None, + resume_after: Mapping[str, Any] | None = None, + max_await_time_ms: int | None = None, + batch_size: int | None = None, + collation: _CollationIn | None = None, + start_at_operation_time: Timestamp | None = None, + session: ClientSession | None = None, + start_after: Mapping[str, Any] | None = None, + comment: Any | None = None, + full_document_before_change: str | None = None, + show_expanded_events: bool | None = None, ) -> DatabaseChangeStream[_DocumentType]: """Watch changes on this database. @@ -477,12 +475,12 @@ def watch( def create_collection( self, name: str, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, - session: Optional[ClientSession] = None, - check_exists: Optional[bool] = True, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, + session: ClientSession | None = None, + check_exists: bool | None = True, **kwargs: Any, ) -> Collection[_DocumentType]: """Create a new :class:`~pymongo.collection.Collection` in this @@ -634,7 +632,7 @@ def create_collection( return coll def aggregate( - self, pipeline: _Pipeline, session: Optional[ClientSession] = None, **kwargs: Any + self, pipeline: _Pipeline, session: ClientSession | None = None, **kwargs: Any ) -> CommandCursor[_DocumentType]: """Perform a database-level aggregation. @@ -720,15 +718,15 @@ def aggregate( def _command( self, conn: Connection, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: int = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, + allowable_errors: Sequence[str | int] | None = None, read_preference: _ServerMode = ReadPreference.PRIMARY, codec_options: CodecOptions[dict[str, Any]] = DEFAULT_CODEC_OPTIONS, - write_concern: Optional[WriteConcern] = None, + write_concern: WriteConcern | None = None, parse_write_concern_error: bool = False, - session: Optional[ClientSession] = None, + session: ClientSession | None = None, **kwargs: Any, ) -> dict[str, Any]: ... @@ -736,34 +734,33 @@ def _command( def _command( self, conn: Connection, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: int = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, + allowable_errors: Sequence[str | int] | None = None, read_preference: _ServerMode = ReadPreference.PRIMARY, codec_options: CodecOptions[_CodecDocumentType] = ..., - write_concern: Optional[WriteConcern] = None, + write_concern: WriteConcern | None = None, parse_write_concern_error: bool = False, - session: Optional[ClientSession] = None, + session: ClientSession | None = None, **kwargs: Any, ) -> _CodecDocumentType: ... def _command( self, conn: Connection, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: int = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, + allowable_errors: Sequence[str | int] | None = None, read_preference: _ServerMode = ReadPreference.PRIMARY, - codec_options: Union[ - CodecOptions[dict[str, Any]], CodecOptions[_CodecDocumentType] - ] = DEFAULT_CODEC_OPTIONS, - write_concern: Optional[WriteConcern] = None, + codec_options: CodecOptions[dict[str, Any]] + | CodecOptions[_CodecDocumentType] = DEFAULT_CODEC_OPTIONS, + write_concern: WriteConcern | None = None, parse_write_concern_error: bool = False, - session: Optional[ClientSession] = None, + session: ClientSession | None = None, **kwargs: Any, - ) -> Union[dict[str, Any], _CodecDocumentType]: + ) -> dict[str, Any] | _CodecDocumentType: """Internal command helper.""" if isinstance(command, str): command = {command: value} @@ -786,44 +783,44 @@ def _command( @overload def command( self, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: Any = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - read_preference: Optional[_ServerMode] = None, + allowable_errors: Sequence[str | int] | None = None, + read_preference: _ServerMode | None = None, codec_options: None = None, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> dict[str, Any]: ... @overload def command( self, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: Any = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - read_preference: Optional[_ServerMode] = None, + allowable_errors: Sequence[str | int] | None = None, + read_preference: _ServerMode | None = None, codec_options: CodecOptions[_CodecDocumentType] = ..., - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> _CodecDocumentType: ... @_csot.apply def command( self, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: Any = 1, check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - read_preference: Optional[_ServerMode] = None, - codec_options: Optional[bson.codec_options.CodecOptions[_CodecDocumentType]] = None, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + allowable_errors: Sequence[str | int] | None = None, + read_preference: _ServerMode | None = None, + codec_options: bson.codec_options.CodecOptions[_CodecDocumentType] | None = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, - ) -> Union[dict[str, Any], _CodecDocumentType]: + ) -> dict[str, Any] | _CodecDocumentType: """Issue a MongoDB command. Send command `command` to the database and return the @@ -926,11 +923,11 @@ def command( read_preference = (session and session._txn_read_preference()) or ReadPreference.PRIMARY def inner( - session: Optional[ClientSession], + session: ClientSession | None, _server: Server, conn: Connection, read_preference: _ServerMode, - ) -> Union[dict[str, Any], _CodecDocumentType]: + ) -> dict[str, Any] | _CodecDocumentType: return self._command( conn, command, @@ -950,13 +947,13 @@ def inner( @_csot.apply def cursor_command( self, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], value: Any = 1, - read_preference: Optional[_ServerMode] = None, - codec_options: Optional[CodecOptions[_CodecDocumentType]] = None, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, - max_await_time_ms: Optional[int] = None, + read_preference: _ServerMode | None = None, + codec_options: CodecOptions[_CodecDocumentType] | None = None, + session: ClientSession | None = None, + comment: Any | None = None, + max_await_time_ms: int | None = None, **kwargs: Any, ) -> CommandCursor[_DocumentType]: """Issue a MongoDB command and parse the response as a cursor. @@ -1020,7 +1017,7 @@ def cursor_command( ) or ReadPreference.PRIMARY def inner( - session: Optional[ClientSession], + session: ClientSession | None, _server: Server, conn: Connection, read_preference: _ServerMode, @@ -1057,15 +1054,15 @@ def inner( def _retryable_read_command( self, - command: Union[str, MutableMapping[str, Any]], + command: str | MutableMapping[str, Any], operation: str, - session: Optional[ClientSession] = None, + session: ClientSession | None = None, ) -> dict[str, Any]: """Same as command but used for retryable read commands.""" read_preference = (session and session._txn_read_preference()) or ReadPreference.PRIMARY def _cmd( - session: Optional[ClientSession], + session: ClientSession | None, _server: Server, conn: Connection, read_preference: _ServerMode, @@ -1082,7 +1079,7 @@ def _cmd( def _list_collections( self, conn: Connection, - session: Optional[ClientSession], + session: ClientSession | None, read_preference: _ServerMode, **kwargs: Any, ) -> CommandCursor[MutableMapping[str, Any]]: @@ -1109,9 +1106,9 @@ def _list_collections( def _list_collections_helper( self, - session: Optional[ClientSession] = None, - filter: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + filter: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> CommandCursor[MutableMapping[str, Any]]: """Get a cursor over the collections of this database. @@ -1140,7 +1137,7 @@ def _list_collections_helper( kwargs["comment"] = comment def _cmd( - session: Optional[ClientSession], + session: ClientSession | None, _server: Server, conn: Connection, read_preference: _ServerMode, @@ -1153,9 +1150,9 @@ def _cmd( def list_collections( self, - session: Optional[ClientSession] = None, - filter: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + filter: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> CommandCursor[MutableMapping[str, Any]]: """Get a cursor over the collections of this database. @@ -1190,9 +1187,9 @@ def list_collections( def _list_collection_names( self, - session: Optional[ClientSession] = None, - filter: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + filter: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> list[str]: if comment is not None: @@ -1214,9 +1211,9 @@ def _list_collection_names( def list_collection_names( self, - session: Optional[ClientSession] = None, - filter: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + filter: Mapping[str, Any] | None = None, + comment: Any | None = None, **kwargs: Any, ) -> list[str]: """Get a list of all the collection names in this database. @@ -1247,14 +1244,14 @@ def list_collection_names( return self._list_collection_names(session, filter, comment, **kwargs) def _drop_helper( - self, name: str, session: Optional[ClientSession] = None, comment: Optional[Any] = None + self, name: str, session: ClientSession | None = None, comment: Any | None = None ) -> dict[str, Any]: command = {"drop": name} if comment is not None: command["comment"] = comment def inner( - session: Optional[ClientSession], conn: Connection, _retryable_write: bool + session: ClientSession | None, conn: Connection, _retryable_write: bool ) -> dict[str, Any]: return self._command( conn, @@ -1270,10 +1267,10 @@ def inner( @_csot.apply def drop_collection( self, - name_or_collection: Union[str, Collection[_DocumentTypeArg]], - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, - encrypted_fields: Optional[Mapping[str, Any]] = None, + name_or_collection: str | Collection[_DocumentTypeArg], + session: ClientSession | None = None, + comment: Any | None = None, + encrypted_fields: Mapping[str, Any] | None = None, ) -> dict[str, Any]: """Drop a collection. @@ -1347,12 +1344,12 @@ def drop_collection( def validate_collection( self, - name_or_collection: Union[str, Collection[_DocumentTypeArg]], + name_or_collection: str | Collection[_DocumentTypeArg], scandata: bool = False, full: bool = False, - session: Optional[ClientSession] = None, - background: Optional[bool] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + background: bool | None = None, + comment: Any | None = None, ) -> dict[str, Any]: """Validate a collection. @@ -1432,10 +1429,10 @@ def validate_collection( def dereference( self, dbref: DBRef, - session: Optional[ClientSession] = None, - comment: Optional[Any] = None, + session: ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, - ) -> Optional[_DocumentType]: + ) -> _DocumentType | None: """Dereference a :class:`~bson.dbref.DBRef`, getting the document it points to. diff --git a/pymongo/synchronous/encryption.py b/pymongo/synchronous/encryption.py index 1626c16f58..9ffd9331b1 100644 --- a/pymongo/synchronous/encryption.py +++ b/pymongo/synchronous/encryption.py @@ -27,8 +27,6 @@ TYPE_CHECKING, Any, Generic, - Optional, - Union, cast, ) @@ -118,9 +116,9 @@ class _EncryptionIO(MongoCryptCallback): # type: ignore[misc] def __init__( self, - client: Optional[MongoClient[_DocumentTypeArg]], + client: MongoClient[_DocumentTypeArg] | None, key_vault_coll: Collection[_DocumentTypeArg], - mongocryptd_client: Optional[MongoClient[_DocumentTypeArg]], + mongocryptd_client: MongoClient[_DocumentTypeArg] | None, opts: AutoEncryptionOpts, ): """Internal class to perform I/O on behalf of pymongocrypt.""" @@ -130,7 +128,7 @@ def __init__( self.client_ref = weakref.ref(client) else: self.client_ref = None - self.key_vault_coll: Optional[Collection[RawBSONDocument]] = cast( + self.key_vault_coll: Collection[RawBSONDocument] | None = cast( Collection[RawBSONDocument], key_vault_coll.with_options( codec_options=_KEY_VAULT_OPTS, @@ -239,7 +237,7 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: ) raise exc from final_err - def collection_info(self, database: str, filter: bytes) -> Optional[list[bytes]]: + def collection_info(self, database: str, filter: bytes) -> list[bytes] | None: """Get the collection info for a namespace. The returned collection info is passed to libmongocrypt which reads @@ -442,7 +440,7 @@ def encrypt( # TODO: PYTHON-1922 avoid decoding the encrypted_cmd. return _inflate_bson(encrypted_cmd, DEFAULT_RAW_BSON_OPTIONS) - def decrypt(self, response: bytes | memoryview) -> Optional[bytes]: + def decrypt(self, response: bytes | memoryview) -> bytes | None: """Decrypt a MongoDB command response. :param response: A MongoDB command response as BSON. @@ -485,8 +483,8 @@ def _string_opts_kwarg() -> str: def _resolve_string_opts( - string_opts: Optional[StringOpts], text_opts: Optional[StringOpts] -) -> Optional[StringOpts]: + string_opts: StringOpts | None, text_opts: StringOpts | None +) -> StringOpts | None: """Resolve string_opts and its former name, text_opts. pymongocrypt accepts only one of the two names per release, so passing the @@ -518,9 +516,9 @@ def __init__( key_vault_namespace: str, key_vault_client: MongoClient[_DocumentTypeArg], codec_options: CodecOptions[_DocumentTypeArg], - kms_tls_options: Optional[Mapping[str, Any]] = None, - key_expiration_ms: Optional[int] = None, - kms_connect_callback: Optional[KMSConnectCallback] = None, + kms_tls_options: Mapping[str, Any] | None = None, + key_expiration_ms: int | None = None, + kms_connect_callback: KMSConnectCallback | None = None, ) -> None: """Explicit client-side field level encryption. @@ -648,9 +646,7 @@ def __init__( kms_connect_callback=kms_connect_callback, ) self._kms_ssl_contexts = _parse_kms_tls_options(opts._kms_tls_options, _IS_SYNC) - self._io_callbacks: Optional[_EncryptionIO] = _EncryptionIO( - None, key_vault_coll, None, opts - ) + self._io_callbacks: _EncryptionIO | None = _EncryptionIO(None, key_vault_coll, None, opts) self._encryption = ExplicitEncrypter( self._io_callbacks, _create_mongocrypt_options( @@ -669,8 +665,8 @@ def create_encrypted_collection( database: Database[_DocumentTypeArg], name: str, encrypted_fields: Mapping[str, Any], - kms_provider: Optional[str] = None, - master_key: Optional[Mapping[str, Any]] = None, + kms_provider: str | None = None, + master_key: Mapping[str, Any] | None = None, **kwargs: Any, ) -> tuple[Collection[_DocumentTypeArg], Mapping[str, Any]]: """Create a collection with encryptedFields. @@ -756,9 +752,9 @@ def create_encrypted_collection( def create_data_key( self, kms_provider: str, - master_key: Optional[Mapping[str, Any]] = None, - key_alt_names: Optional[Sequence[str]] = None, - key_material: Optional[bytes] = None, + master_key: Mapping[str, Any] | None = None, + key_alt_names: Sequence[str] | None = None, + key_material: bytes | None = None, ) -> Binary: """Create and insert a new data key into the key vault collection. @@ -848,13 +844,13 @@ def _encrypt_helper( self, value: Any, algorithm: str, - key_id: Optional[Union[Binary, uuid.UUID]] = None, - key_alt_name: Optional[str] = None, - query_type: Optional[str] = None, - contention_factor: Optional[int] = None, - range_opts: Optional[RangeOpts] = None, + key_id: Binary | uuid.UUID | None = None, + key_alt_name: str | None = None, + query_type: str | None = None, + contention_factor: int | None = None, + range_opts: RangeOpts | None = None, is_expression: bool = False, - string_opts: Optional[StringOpts] = None, + string_opts: StringOpts | None = None, ) -> Any: self._check_closed() if isinstance(key_id, uuid.UUID): @@ -898,13 +894,13 @@ def encrypt( self, value: Any, algorithm: str, - key_id: Optional[Union[Binary, uuid.UUID]] = None, - key_alt_name: Optional[str] = None, - query_type: Optional[str] = None, - contention_factor: Optional[int] = None, - range_opts: Optional[RangeOpts] = None, - string_opts: Optional[StringOpts] = None, - text_opts: Optional[StringOpts] = None, + key_id: Binary | uuid.UUID | None = None, + key_alt_name: str | None = None, + query_type: str | None = None, + contention_factor: int | None = None, + range_opts: RangeOpts | None = None, + string_opts: StringOpts | None = None, + text_opts: StringOpts | None = None, ) -> Binary: """Encrypt a BSON value with a given key and algorithm. @@ -969,11 +965,11 @@ def encrypt_expression( self, expression: Mapping[str, Any], algorithm: str, - key_id: Optional[Union[Binary, uuid.UUID]] = None, - key_alt_name: Optional[str] = None, - query_type: Optional[str] = None, - contention_factor: Optional[int] = None, - range_opts: Optional[RangeOpts] = None, + key_id: Binary | uuid.UUID | None = None, + key_alt_name: str | None = None, + query_type: str | None = None, + contention_factor: int | None = None, + range_opts: RangeOpts | None = None, ) -> RawBSONDocument: """Encrypt a BSON expression with a given key and algorithm. @@ -1037,7 +1033,7 @@ def decrypt(self, value: Binary) -> Any: decrypted_doc = self._encryption.decrypt(doc) return decode(decrypted_doc, codec_options=self._codec_options)["v"] - def get_key(self, id: Binary) -> Optional[RawBSONDocument]: + def get_key(self, id: Binary) -> RawBSONDocument | None: """Get a data key by id. :param id` (Binary): The UUID of a key a which must be a @@ -1096,7 +1092,7 @@ def add_key_alt_name(self, id: Binary, key_alt_name: str) -> Any: assert self._key_vault_coll is not None return self._key_vault_coll.find_one_and_update({"_id": id}, update) - def get_key_by_alt_name(self, key_alt_name: str) -> Optional[RawBSONDocument]: + def get_key_by_alt_name(self, key_alt_name: str) -> RawBSONDocument | None: """Get a key document in the key vault collection that has the given ``key_alt_name``. :param key_alt_name: (str): The key alternate name of the key to get. @@ -1109,7 +1105,7 @@ def get_key_by_alt_name(self, key_alt_name: str) -> Optional[RawBSONDocument]: assert self._key_vault_coll is not None return self._key_vault_coll.find_one({"keyAltNames": key_alt_name}) - def remove_key_alt_name(self, id: Binary, key_alt_name: str) -> Optional[RawBSONDocument]: + def remove_key_alt_name(self, id: Binary, key_alt_name: str) -> RawBSONDocument | None: """Remove ``key_alt_name`` from the set of keyAltNames in the key document with UUID ``id``. Also removes the ``keyAltNames`` field from the key document if it would otherwise be empty. @@ -1148,8 +1144,8 @@ def remove_key_alt_name(self, id: Binary, key_alt_name: str) -> Optional[RawBSON def rewrap_many_data_key( self, filter: Mapping[str, Any], - provider: Optional[str] = None, - master_key: Optional[Mapping[str, Any]] = None, + provider: str | None = None, + master_key: Mapping[str, Any] | None = None, ) -> RewrapManyDataKeyResult: """Decrypts and encrypts all matching data keys in the key vault with a possibly new `master_key` value. diff --git a/pymongo/synchronous/mongo_client.py b/pymongo/synchronous/mongo_client.py index 52ca029d26..8b81ef476e 100644 --- a/pymongo/synchronous/mongo_client.py +++ b/pymongo/synchronous/mongo_client.py @@ -49,7 +49,6 @@ NoReturn, Optional, TypeVar, - Union, cast, ) @@ -152,14 +151,14 @@ _IS_SYNC = True -_WriteOp = Union[ - InsertOne, # type: ignore[type-arg] - DeleteOne, - DeleteMany, - ReplaceOne, # type: ignore[type-arg] - UpdateOne, - UpdateMany, -] +_WriteOp = ( + InsertOne # type: ignore[type-arg] + | DeleteOne + | DeleteMany + | ReplaceOne # type: ignore[type-arg] + | UpdateOne + | UpdateMany +) class MongoClient(common.BaseObject, Generic[_DocumentType]): @@ -172,12 +171,12 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]): def __init__( self, - host: Optional[Union[str, Sequence[str]]] = None, - port: Optional[int] = None, - document_class: Optional[type[_DocumentType]] = None, - tz_aware: Optional[bool] = None, - connect: Optional[bool] = None, - type_registry: Optional[TypeRegistry] = None, + host: str | Sequence[str] | None = None, + port: int | None = None, + document_class: type[_DocumentType] | None = None, + tz_aware: bool | None = None, + connect: bool | None = None, + type_registry: TypeRegistry | None = None, **kwargs: Any, ) -> None: """Client for a MongoDB instance, a replica set, or a set of mongoses. @@ -921,7 +920,7 @@ def validator(host: str) -> bool: self._lock = _create_lock() self._kill_cursors_queue: list = [] # type: ignore[type-arg] - self._encrypter: Optional[_Encrypter] = None + self._encrypter: _Encrypter | None = None self._resolve_srv_info.update( { @@ -959,7 +958,7 @@ def validator(host: str) -> bool: self._opened = False self._closed = False - self._loop: Optional[asyncio.AbstractEventLoop] = None + self._loop: asyncio.AbstractEventLoop | None = None if not is_srv: self._init_background() @@ -1122,7 +1121,7 @@ def _connect(self) -> None: """Explicitly connect to MongoDB synchronously instead of on the first operation.""" self._get_topology() - def _init_background(self, old_pid: Optional[int] = None) -> None: + def _init_background(self, old_pid: int | None = None) -> None: self._topology = Topology(self._topology_settings) if _HAS_REGISTER_AT_FORK: # Add this client to the list of weakly referenced items. @@ -1166,7 +1165,7 @@ def append_metadata(self, driver_info: DriverInfo) -> None: ) self._options.pool_options._update_metadata(driver_info) - def _should_pin_cursor(self, session: Optional[ClientSession]) -> Optional[bool]: + def _should_pin_cursor(self, session: ClientSession | None) -> bool | None: return self._options.load_balanced and not (session and session.in_transaction) def _after_fork(self) -> None: @@ -1182,18 +1181,18 @@ def _duplicate(self, **kwargs: Any) -> MongoClient: # type: ignore[type-arg] def watch( self, - pipeline: Optional[_Pipeline] = None, - full_document: Optional[str] = None, - resume_after: Optional[Mapping[str, Any]] = None, - max_await_time_ms: Optional[int] = None, - batch_size: Optional[int] = None, - collation: Optional[_CollationIn] = None, - start_at_operation_time: Optional[Timestamp] = None, - session: Optional[client_session.ClientSession] = None, - start_after: Optional[Mapping[str, Any]] = None, - comment: Optional[Any] = None, - full_document_before_change: Optional[str] = None, - show_expanded_events: Optional[bool] = None, + pipeline: _Pipeline | None = None, + full_document: str | None = None, + resume_after: Mapping[str, Any] | None = None, + max_await_time_ms: int | None = None, + batch_size: int | None = None, + collation: _CollationIn | None = None, + start_at_operation_time: Timestamp | None = None, + session: client_session.ClientSession | None = None, + start_after: Mapping[str, Any] | None = None, + comment: Any | None = None, + full_document_before_change: str | None = None, + show_expanded_events: bool | None = None, ) -> ChangeStream[_DocumentType]: """Watch changes on this cluster. @@ -1343,7 +1342,7 @@ def topology_description(self) -> TopologyDescription: return self._topology.description @property - def _topology_id(self) -> Optional[ObjectId]: + def _topology_id(self) -> ObjectId | None: return self._topology_settings._topology_id @property @@ -1372,7 +1371,7 @@ def options(self) -> ClientOptions: """ return self._options - def eq_props(self) -> tuple[tuple[_Address, ...], Optional[str], Optional[str], str]: + def eq_props(self) -> tuple[tuple[_Address, ...], str | None, str | None, str]: return ( tuple(sorted(self._resolve_srv_info["seeds"])), self._options.replica_set_name, @@ -1477,8 +1476,8 @@ def __del__(self) -> None: def _close_cursor_soon( self, cursor_id: int, - address: Optional[_CursorAddress], - conn_mgr: Optional[_ConnectionManager] = None, + address: _CursorAddress | None, + conn_mgr: _ConnectionManager | None = None, ) -> None: """Request that a cursor and/or connection be cleaned up soon.""" self._kill_cursors_queue.append((address, cursor_id, conn_mgr)) @@ -1490,9 +1489,9 @@ def _start_session(self, implicit: bool, **kwargs: Any) -> ClientSession: def start_session( self, - causal_consistency: Optional[bool] = None, - default_transaction_options: Optional[TransactionOptions] = None, - snapshot: Optional[bool] = False, + causal_consistency: bool | None = None, + default_transaction_options: TransactionOptions | None = None, + snapshot: bool | None = False, ) -> client_session.ClientSession: """Start a logical session. @@ -1517,7 +1516,7 @@ def start_session( snapshot=snapshot, ) - def _ensure_session(self, session: Optional[ClientSession] = None) -> Optional[ClientSession]: + def _ensure_session(self, session: ClientSession | None = None) -> ClientSession | None: """If provided session and bound session are None, lend a temporary session.""" session = session or self._get_bound_session() if session: @@ -1532,13 +1531,13 @@ def _ensure_session(self, session: Optional[ClientSession] = None) -> Optional[C return None def _send_cluster_time( - self, command: MutableMapping[str, Any], session: Optional[ClientSession] + self, command: MutableMapping[str, Any], session: ClientSession | None ) -> None: topology_time = self._topology.max_cluster_time() session_time = session.cluster_time if session else None if topology_time and session_time: if topology_time["clusterTime"] > session_time["clusterTime"]: - cluster_time: Optional[ClusterTime] = topology_time + cluster_time: ClusterTime | None = topology_time else: cluster_time = session_time else: @@ -1548,11 +1547,11 @@ def _send_cluster_time( def get_default_database( self, - default: Optional[str] = None, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, + default: str | None = None, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> database.Database[_DocumentType]: """Get the database named in the MongoDB connection URI. @@ -1608,11 +1607,11 @@ def get_default_database( def get_database( self, - name: Optional[str] = None, - codec_options: Optional[CodecOptions[_DocumentTypeArg]] = None, - read_preference: Optional[_ServerMode] = None, - write_concern: Optional[WriteConcern] = None, - read_concern: Optional[ReadConcern] = None, + name: str | None = None, + codec_options: CodecOptions[_DocumentTypeArg] | None = None, + read_preference: _ServerMode | None = None, + write_concern: WriteConcern | None = None, + read_concern: ReadConcern | None = None, ) -> database.Database[_DocumentType]: """Get a :class:`~pymongo.database.Database` with the given name and options. @@ -1704,7 +1703,7 @@ def _server_property(self, attr_name: str) -> Any: return getattr(server.description, attr_name) @property - def address(self) -> Optional[tuple[str, int]]: + def address(self) -> tuple[str, int] | None: """(host, port) of the current standalone, primary, or mongos, or None. Accessing :attr:`address` raises :exc:`~.errors.InvalidOperation` if @@ -1731,7 +1730,7 @@ def address(self) -> Optional[tuple[str, int]]: return self._server_property("address") @property - def primary(self) -> Optional[tuple[str, int]]: + def primary(self) -> tuple[str, int] | None: """The (host, port) of the current primary of the replica set. Returns ``None`` if this client is not connected to a replica set, @@ -1876,17 +1875,17 @@ def _get_topology(self) -> Topology: self._opened = True return self._topology - def _checkout(self, server: Server, session: Optional[ClientSession]) -> _ClientCheckout: + def _checkout(self, server: Server, session: ClientSession | None) -> _ClientCheckout: return _ClientCheckout(self, server, session) def _select_server( self, server_selector: Callable[[Selection], Selection], - session: Optional[ClientSession], + session: ClientSession | None, operation: str, - address: Optional[_Address] = None, - deprioritized_servers: Optional[list[Server]] = None, - operation_id: Optional[int] = None, + address: _Address | None = None, + deprioritized_servers: list[Server] | None = None, + operation_id: int | None = None, ) -> Server: """Select a server to run an operation on this client. @@ -1928,12 +1927,12 @@ def _select_server( session._unpin() raise - def _conn_for_writes(self, session: Optional[ClientSession], operation: str) -> _ClientCheckout: + def _conn_for_writes(self, session: ClientSession | None, operation: str) -> _ClientCheckout: server = self._select_server(writable_server_selector, session, operation) return self._checkout(server, session) def _conn_from_server( - self, read_preference: _ServerMode, server: Server, session: Optional[ClientSession] + self, read_preference: _ServerMode, server: Server, session: ClientSession | None ) -> _ClientReadCheckout: assert read_preference is not None, "read_preference must not be None" return _ClientReadCheckout(self, server, session, read_preference) @@ -1941,7 +1940,7 @@ def _conn_from_server( def _conn_for_reads( self, read_preference: _ServerMode, - session: Optional[ClientSession], + session: ClientSession | None, operation: str, ) -> _ClientReadCheckout: assert read_preference is not None, "read_preference must not be None" @@ -1951,9 +1950,9 @@ def _conn_for_reads( @_csot.apply def _run_operation( self, - operation: Union[_Query, _GetMore], + operation: _Query | _GetMore, run_with_conn: Callable, # type: ignore[type-arg] - address: Optional[_Address] = None, + address: _Address | None = None, ) -> Response: """Run a _Query/_GetMore operation and return a Response. @@ -1983,7 +1982,7 @@ def _run_operation( ) def _cmd( - _session: Optional[ClientSession], + _session: ClientSession | None, _server: Server, conn: Connection, read_preference: _ServerMode, @@ -2004,10 +2003,10 @@ def _retry_with_session( self, retryable: bool, func: _WriteCall[T], - session: Optional[ClientSession], - bulk: Optional[Union[_Bulk, _ClientBulk]], + session: ClientSession | None, + bulk: _Bulk | _ClientBulk | None, operation: str, - operation_id: Optional[int] = None, + operation_id: int | None = None, ) -> T: """Execute an operation with at most one consecutive retries @@ -2034,14 +2033,14 @@ def _retry_with_session( def _retry_internal( self, func: _WriteCall[T] | _ReadCall[T], - session: Optional[ClientSession], - bulk: Optional[Union[_Bulk, _ClientBulk]], + session: ClientSession | None, + bulk: _Bulk | _ClientBulk | None, operation: str, is_read: bool = False, - address: Optional[_Address] = None, - read_pref: Optional[_ServerMode] = None, + address: _Address | None = None, + read_pref: _ServerMode | None = None, retryable: bool = False, - operation_id: Optional[int] = None, + operation_id: int | None = None, is_run_command: bool = False, is_aggregate_write: bool = False, ) -> T: @@ -2080,11 +2079,11 @@ def _retryable_read( self, func: _ReadCall[T], read_pref: _ServerMode, - session: Optional[ClientSession], + session: ClientSession | None, operation: str, - address: Optional[_Address] = None, + address: _Address | None = None, retryable: bool = True, - operation_id: Optional[int] = None, + operation_id: int | None = None, is_run_command: bool = False, is_aggregate_write: bool = False, ) -> T: @@ -2131,10 +2130,10 @@ def _retryable_write( self, retryable: bool, func: _WriteCall[T], - session: Optional[ClientSession], + session: ClientSession | None, operation: str, - bulk: Optional[Union[_Bulk, _ClientBulk]] = None, - operation_id: Optional[int] = None, + bulk: _Bulk | _ClientBulk | None = None, + operation_id: int | None = None, ) -> T: """Execute an operation with consecutive retries if possible @@ -2156,9 +2155,9 @@ def _retryable_write( def _cleanup_cursor_no_lock( self, cursor_id: int, - address: Optional[_CursorAddress], + address: _CursorAddress | None, conn_mgr: _ConnectionManager, - session: Optional[ClientSession], + session: ClientSession | None, ) -> None: """Cleanup a cursor from __del__ without locking. @@ -2179,9 +2178,9 @@ def _cleanup_cursor_no_lock( def _cleanup_cursor_lock( self, cursor_id: int, - address: Optional[_CursorAddress], + address: _CursorAddress | None, conn_mgr: _ConnectionManager, - session: Optional[ClientSession], + session: ClientSession | None, ) -> None: """Cleanup a cursor from cursor.close() using a lock. @@ -2211,9 +2210,9 @@ def _cleanup_cursor_lock( def _close_cursor_now( self, cursor_id: int, - address: Optional[_CursorAddress], - session: Optional[ClientSession] = None, - conn_mgr: Optional[_ConnectionManager] = None, + address: _CursorAddress | None, + session: ClientSession | None = None, + conn_mgr: _ConnectionManager | None = None, ) -> None: """Send a kill cursors message with the given id. @@ -2238,9 +2237,9 @@ def _close_cursor_now( def _kill_cursors( self, cursor_ids: Sequence[int], - address: Optional[_CursorAddress], + address: _CursorAddress | None, topology: Topology, - session: Optional[ClientSession], + session: ClientSession | None, ) -> None: """Send a kill cursors message with the given ids.""" if address: @@ -2259,7 +2258,7 @@ def _kill_cursor_impl( self, cursor_ids: Sequence[int], address: _CursorAddress, - session: Optional[ClientSession], + session: ClientSession | None, conn: Connection, ) -> None: namespace = address.namespace @@ -2321,9 +2320,7 @@ def _process_periodic_tasks(self) -> None: else: _log_client_error() - def _return_server_session( - self, server_session: Union[_ServerSession, _EmptyServerSession] - ) -> None: + def _return_server_session(self, server_session: _ServerSession | _EmptyServerSession) -> None: """Internal: return a _ServerSession to the pool.""" if isinstance(server_session, _EmptyServerSession): return None @@ -2331,8 +2328,8 @@ def _return_server_session( @contextlib.contextmanager def _tmp_session( - self, session: Optional[client_session.ClientSession] - ) -> Generator[Optional[client_session.ClientSession], None]: + self, session: client_session.ClientSession | None + ) -> Generator[client_session.ClientSession | None, None]: """If provided session is None, lend a temporary session.""" if session is not None and not isinstance(session, client_session.ClientSession): raise ValueError( @@ -2364,12 +2361,12 @@ def _tmp_session( else: yield None - def _process_response(self, reply: Mapping[str, Any], session: Optional[ClientSession]) -> None: + def _process_response(self, reply: Mapping[str, Any], session: ClientSession | None) -> None: self._topology.receive_cluster_time(reply.get("$clusterTime")) if session is not None: session._process_response(reply) - def _get_bound_session(self) -> Optional[ClientSession]: + def _get_bound_session(self) -> ClientSession | None: bound_session = _SESSION.get() if bound_session: if bound_session.client is self: @@ -2381,7 +2378,7 @@ def _get_bound_session(self) -> Optional[ClientSession]: else: return None - def server_info(self, session: Optional[client_session.ClientSession] = None) -> dict[str, Any]: + def server_info(self, session: client_session.ClientSession | None = None) -> dict[str, Any]: """Get information about the MongoDB server we're connected to. :param session: a @@ -2399,8 +2396,8 @@ def server_info(self, session: Optional[client_session.ClientSession] = None) -> def _list_databases( self, - session: Optional[client_session.ClientSession] = None, - comment: Optional[Any] = None, + session: client_session.ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> CommandCursor[dict[str, Any]]: cmd = {"listDatabases": 1} @@ -2419,8 +2416,8 @@ def _list_databases( def list_databases( self, - session: Optional[client_session.ClientSession] = None, - comment: Optional[Any] = None, + session: client_session.ClientSession | None = None, + comment: Any | None = None, **kwargs: Any, ) -> CommandCursor[dict[str, Any]]: """Get a cursor over the databases of the connected server. @@ -2453,8 +2450,8 @@ def list_databases( def list_database_names( self, - session: Optional[client_session.ClientSession] = None, - comment: Optional[Any] = None, + session: client_session.ClientSession | None = None, + comment: Any | None = None, ) -> list[str]: """Get a list of the names of all databases on the connected server. @@ -2474,9 +2471,9 @@ def list_database_names( @_csot.apply def drop_database( self, - name_or_database: Union[str, database.Database[_DocumentTypeArg]], - session: Optional[client_session.ClientSession] = None, - comment: Optional[Any] = None, + name_or_database: str | database.Database[_DocumentTypeArg], + session: client_session.ClientSession | None = None, + comment: Any | None = None, ) -> None: """Drop a database. @@ -2526,13 +2523,13 @@ def drop_database( def bulk_write( self, models: Sequence[_WriteOp], - session: Optional[ClientSession] = None, + session: ClientSession | None = None, ordered: bool = True, verbose_results: bool = False, - bypass_document_validation: Optional[bool] = None, - comment: Optional[Any] = None, - let: Optional[Mapping[str, Any]] = None, - write_concern: Optional[WriteConcern] = None, + bypass_document_validation: bool | None = None, + comment: Any | None = None, + let: Mapping[str, Any] | None = None, + write_concern: WriteConcern | None = None, ) -> ClientBulkWriteResult: """Send a batch of write operations, potentially across multiple namespaces, to the server. @@ -2680,7 +2677,7 @@ def __init__( self, client: MongoClient, # type: ignore[type-arg] server: Server, - session: Optional[ClientSession], + session: ClientSession | None, ): if not isinstance(client, MongoClient): # This is for compatibility with mocked and subclassed types, such as in Motor. @@ -2697,10 +2694,10 @@ def __init__( # of the pool at the time the connection attempt was started." self.sock_generation = server.pool.gen.get_overall() self.completed_handshake = False - self.service_id: Optional[ObjectId] = None + self.service_id: ObjectId | None = None self.handled = False - self._existing_conn: Optional[Connection] = None - self._pool_checkout: Optional[_PoolCheckout] = None + self._existing_conn: Connection | None = None + self._pool_checkout: _PoolCheckout | None = None self._server = server def contribute_socket(self, conn: Connection, completed_handshake: bool = True) -> None: @@ -2710,9 +2707,7 @@ def contribute_socket(self, conn: Connection, completed_handshake: bool = True) self.service_id = conn.service_id self.completed_handshake = completed_handshake - def handle( - self, exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException] - ) -> None: + def handle(self, exc_type: type[BaseException] | None, exc_val: BaseException | None) -> None: if self.handled or exc_val is None: return self.handled = True @@ -2788,9 +2783,9 @@ def __enter__(self) -> Connection: def __exit__( self, - exc_type: Optional[type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: # Perform SDAM error handling while the connection is still checked out. try: @@ -2806,7 +2801,7 @@ def for_existing_conn( cls, client: MongoClient, # type: ignore[type-arg] server: Server, - session: Optional[ClientSession], + session: ClientSession | None, conn: Connection, ) -> _ClientCheckout: """Return a _ClientCheckout for an already-checked-out connection. @@ -2833,7 +2828,7 @@ def __init__( self, client: MongoClient, # type: ignore[type-arg] server: Server, - session: Optional[ClientSession], + session: ClientSession | None, read_preference: _ServerMode, ) -> None: super().__init__(client, server, session) @@ -2863,7 +2858,7 @@ class _ClientConnectionRetryable( __slots__ = () _client: MongoClient[Any] - _session: Optional[ClientSession] + _session: ClientSession | None def run(self) -> T: """Runs the supplied func() and attempts a retry diff --git a/pymongo/synchronous/monitor.py b/pymongo/synchronous/monitor.py index 9708c6e1a2..bbedff66e7 100644 --- a/pymongo/synchronous/monitor.py +++ b/pymongo/synchronous/monitor.py @@ -21,7 +21,7 @@ import threading import time import weakref -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from pymongo import common, periodic_executor from pymongo._csot import MovingMinimum @@ -77,7 +77,7 @@ def target() -> bool: self._executor = executor - def _on_topology_gc(dummy: Optional[Topology] = None) -> None: + def _on_topology_gc(dummy: Topology | None = None) -> None: # This prevents GC from waiting 10 seconds for hello to complete # See test_cleanup_executors_on_client_del. monitor = self_ref() @@ -142,9 +142,9 @@ def __init__( self._pool = pool self._settings = topology_settings self._listeners = self._settings._pool_options._event_listeners - self._cancel_context: Optional[_CancellationContext] = None - self._conn_id: Optional[int] = None - self._current_hb: Optional[_HeartbeatTelemetry] = None + self._cancel_context: _CancellationContext | None = None + self._conn_id: int | None = None + self._current_hb: _HeartbeatTelemetry | None = None self._awaited: bool = False self._rtt_monitor = _RttMonitor( topology, @@ -357,7 +357,7 @@ def _run(self) -> None: # Topology was garbage-collected. self.close() - def _get_seedlist(self) -> Optional[list[tuple[str, Any]]]: + def _get_seedlist(self) -> list[tuple[str, Any]] | None: """Poll SRV records for a seedlist. Returns a list of ServerDescriptions. @@ -417,7 +417,7 @@ def add_sample(self, sample: float) -> None: self._moving_average.add_sample(sample) self._moving_min.add_sample(sample) - def get(self) -> tuple[Optional[float], float]: + def get(self) -> tuple[float | None, float]: """Get the calculated average, or None if no samples yet and the min.""" with self._lock: return self._moving_average.get(), self._moving_min.get() diff --git a/pymongo/synchronous/pool.py b/pymongo/synchronous/pool.py index de5fbb0e85..f746631616 100644 --- a/pymongo/synchronous/pool.py +++ b/pymongo/synchronous/pool.py @@ -23,8 +23,6 @@ TYPE_CHECKING, Any, NoReturn, - Optional, - Union, ) from bson import DEFAULT_CODEC_OPTIONS @@ -126,8 +124,8 @@ def hello(self) -> Hello[dict[str, Any]]: def _hello( self, - topology_version: Optional[Any], - heartbeat_frequency: Optional[int], + topology_version: Any | None, + heartbeat_frequency: int | None, ) -> Hello[dict[str, Any]]: cmd = self.hello_cmd() performing_handshake = not self.performed_handshake @@ -179,7 +177,7 @@ def _hello( self.supports_sessions = ( hello.logical_session_timeout_minutes is not None and hello.is_readable ) - self.logical_session_timeout_minutes: Optional[int] = hello.logical_session_timeout_minutes + self.logical_session_timeout_minutes: int | None = hello.logical_session_timeout_minutes # hello_ok is set from helloOk, which is only returned from ismaster # don't overwrite this when connection switches to hello self.hello_ok = self.hello_ok or hello.hello_ok @@ -229,16 +227,16 @@ def command( read_preference: _ServerMode = ReadPreference.PRIMARY, codec_options: CodecOptions[Mapping[str, Any]] = DEFAULT_CODEC_OPTIONS, # type: ignore[assignment] check: bool = True, - allowable_errors: Optional[Sequence[Union[str, int]]] = None, - read_concern: Optional[ReadConcern] = None, - write_concern: Optional[WriteConcern] = None, + allowable_errors: Sequence[str | int] | None = None, + read_concern: ReadConcern | None = None, + write_concern: WriteConcern | None = None, parse_write_concern_error: bool = False, - collation: Optional[_CollationIn] = None, - session: Optional[ClientSession] = None, - client: Optional[MongoClient[Any]] = None, + collation: _CollationIn | None = None, + session: ClientSession | None = None, + client: MongoClient[Any] | None = None, retryable_write: bool = False, publish_events: bool = True, - user_fields: Optional[Mapping[str, Any]] = None, + user_fields: Mapping[str, Any] | None = None, exhaust_allowed: bool = False, ) -> dict[str, Any]: """Execute a command or raise an error. @@ -327,7 +325,7 @@ def send_message(self, message: bytes, max_doc_size: int) -> None: except BaseException as error: self._raise_connection_failure(error) - def receive_message(self, request_id: Optional[int]) -> _OpMsg: + def receive_message(self, request_id: int | None) -> _OpMsg: """Receive a raw BSON message or raise ConnectionFailure. If any exception is raised, the socket is closed. @@ -359,7 +357,7 @@ def authenticate(self, reauthenticate: bool = False) -> None: self.ready = True self._telemetry.connection_ready(self.id, self.creation_time) - def close_conn(self, reason: Optional[str]) -> None: + def close_conn(self, reason: str | None) -> None: """Close this connection with a reason.""" if self.closed: return @@ -429,7 +427,7 @@ def __init__( address: _Address, options: PoolOptions, is_sdam: bool = False, - client_id: Optional[ObjectId] = None, + client_id: ObjectId | None = None, ): """ :param address: a (hostname, port) tuple @@ -459,7 +457,7 @@ def _reset( self, close: bool, pause: bool = True, - service_id: Optional[ObjectId] = None, + service_id: ObjectId | None = None, interrupt_connections: bool = False, ) -> None: old_state = self.state @@ -530,7 +528,7 @@ def _reset( for conn in sockets: conn.close_conn(ConnectionClosedReason.STALE) - def update_is_writable(self, is_writable: Optional[bool]) -> None: + def update_is_writable(self, is_writable: bool | None) -> None: """Updates the is_writable attribute on all sockets currently in the Pool. """ @@ -540,7 +538,7 @@ def update_is_writable(self, is_writable: Optional[bool]) -> None: _socket.update_is_writable(self.is_writable) # type: ignore[arg-type] def reset( - self, service_id: Optional[ObjectId] = None, interrupt_connections: bool = False + self, service_id: ObjectId | None = None, interrupt_connections: bool = False ) -> None: self._reset(close=False, service_id=service_id, interrupt_connections=interrupt_connections) @@ -619,7 +617,7 @@ def remove_stale_sockets(self, reference_generation: int) -> None: self.requests -= 1 self.size_cond.notify() - def connect(self, handler: Optional[_ClientCheckout] = None) -> Connection: + def connect(self, handler: _ClientCheckout | None = None) -> Connection: """Connect to Mongo and return a new Connection. Can raise ConnectionFailure. @@ -687,7 +685,7 @@ def connect(self, handler: Optional[_ClientCheckout] = None) -> Connection: return conn - def checkout(self, handler: Optional[_ClientCheckout] = None) -> _PoolCheckout: + def checkout(self, handler: _ClientCheckout | None = None) -> _PoolCheckout: """Get a connection from the pool. Always use in a with statement:: with pool.checkout() as connection: @@ -701,7 +699,7 @@ def checkout(self, handler: Optional[_ClientCheckout] = None) -> _PoolCheckout: return _PoolCheckout(self, handler) def _get_conn( - self, checkout_started_time: float, handler: Optional[_ClientCheckout] = None + self, checkout_started_time: float, handler: _ClientCheckout | None = None ) -> Connection: """Get or create a Connection. Can raise ConnectionFailure.""" # We use the pid here to avoid issues with fork / multiprocessing. @@ -879,7 +877,7 @@ def checkin(self, conn: Connection) -> None: self._pinned_sockets.discard(conn) forked = self.pid != os.getpid() # Re-apply the accounting if a BaseException interrupts here (PYTHON-6074). - close_conn_reason: Optional[str] = None + close_conn_reason: str | None = None emit_closed = False accounted = False try: @@ -965,11 +963,11 @@ class _PoolCheckout: def __init__( self, pool: Pool, - handler: Optional[_ClientCheckout] = None, + handler: _ClientCheckout | None = None, ) -> None: self._pool = pool self._handler = handler - self._conn: Optional[Connection] = None + self._conn: Connection | None = None def __enter__(self) -> Connection: pool = self._pool @@ -995,9 +993,9 @@ def __enter__(self) -> Connection: def __exit__( self, - exc_type: Optional[type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: conn = self._conn if conn is None: diff --git a/pymongo/synchronous/server.py b/pymongo/synchronous/server.py index 0faed74f76..b8e6487194 100644 --- a/pymongo/synchronous/server.py +++ b/pymongo/synchronous/server.py @@ -19,7 +19,6 @@ from typing import ( TYPE_CHECKING, Any, - Optional, ) from pymongo._telemetry import _SdamTelemetry @@ -43,9 +42,9 @@ def __init__( server_description: ServerDescription, pool: Pool, monitor: Monitor, - topology_id: Optional[ObjectId] = None, - listeners: Optional[_EventListeners] = None, - events: Optional[ReferenceType[Queue[Any]]] = None, + topology_id: ObjectId | None = None, + listeners: _EventListeners | None = None, + events: ReferenceType[Queue[Any]] | None = None, ) -> None: """Represent one MongoDB server.""" self._description = server_description @@ -62,7 +61,7 @@ def open(self) -> None: if not self._pool.opts.load_balanced: self._monitor.open() - def reset(self, service_id: Optional[ObjectId] = None) -> None: + def reset(self, service_id: ObjectId | None = None) -> None: """Clear the connection pool.""" self.pool.reset(service_id) diff --git a/pymongo/synchronous/settings.py b/pymongo/synchronous/settings.py index 3416bbd130..201e3bcfe0 100644 --- a/pymongo/synchronous/settings.py +++ b/pymongo/synchronous/settings.py @@ -18,7 +18,7 @@ import threading from collections.abc import Collection -from typing import Callable, Optional +from typing import Callable from bson.objectid import ObjectId from pymongo import common @@ -35,25 +35,25 @@ class TopologySettings(_BaseTopologySettings[type[Pool], type[monitor.Monitor]]): def __init__( self, - seeds: Optional[Collection[tuple[str, int]]] = None, - replica_set_name: Optional[str] = None, - pool_class: Optional[type[Pool]] = None, - pool_options: Optional[PoolOptions] = None, - monitor_class: Optional[type[monitor.Monitor]] = None, - condition_class: Optional[type[threading.Condition]] = None, + seeds: Collection[tuple[str, int]] | None = None, + replica_set_name: str | None = None, + pool_class: type[Pool] | None = None, + pool_options: PoolOptions | None = None, + monitor_class: type[monitor.Monitor] | None = None, + condition_class: type[threading.Condition] | None = None, local_threshold_ms: int = LOCAL_THRESHOLD_MS, server_selection_timeout: int = SERVER_SELECTION_TIMEOUT, heartbeat_frequency: int = common.HEARTBEAT_FREQUENCY, - server_selector: Optional[_ServerSelector] = None, - fqdn: Optional[str] = None, - direct_connection: Optional[bool] = False, - load_balanced: Optional[bool] = None, + server_selector: _ServerSelector | None = None, + fqdn: str | None = None, + direct_connection: bool | None = False, + load_balanced: bool | None = None, srv_service_name: str = common.SRV_SERVICE_NAME, srv_max_hosts: int = 0, - srv_allowed_hosts_suffix: Optional[str] = None, - srv_host_validator: Optional[Callable[[str], bool]] = None, + srv_allowed_hosts_suffix: str | None = None, + srv_host_validator: Callable[[str], bool] | None = None, server_monitoring_mode: str = common.SERVER_MONITORING_MODE, - topology_id: Optional[ObjectId] = None, + topology_id: ObjectId | None = None, ): """Represent MongoClient's configuration. diff --git a/pymongo/synchronous/srv_resolver.py b/pymongo/synchronous/srv_resolver.py index 25fc6f7ad6..cae81aad02 100644 --- a/pymongo/synchronous/srv_resolver.py +++ b/pymongo/synchronous/srv_resolver.py @@ -18,7 +18,7 @@ import ipaddress import random -from typing import TYPE_CHECKING, Any, Callable, Optional, Union +from typing import TYPE_CHECKING, Any, Callable from pymongo._psl import SPECIAL_USE_LABELS, _to_punycode, is_public_suffix from pymongo._telemetry import log_srv_monitor_invalid_host @@ -33,7 +33,7 @@ # dnspython can return bytes or str from various parts # of its API depending on version. We always want str. -def maybe_decode(text: Union[str, bytes]) -> str: +def maybe_decode(text: str | bytes) -> str: if isinstance(text, bytes): return text.decode() return text @@ -61,11 +61,11 @@ class _SrvResolver: def __init__( self, fqdn: str, - connect_timeout: Optional[float], + connect_timeout: float | None, srv_service_name: str, srv_max_hosts: int = 0, - srv_allowed_hosts_suffix: Optional[str] = None, - srv_host_validator: Optional[Callable[[str], bool]] = None, + srv_allowed_hosts_suffix: str | None = None, + srv_host_validator: Callable[[str], bool] | None = None, ): self.__fqdn = fqdn.lower() self.__srv = srv_service_name @@ -112,7 +112,7 @@ def __init__( self.__slen = len(self.__plist) self.nparts = len(split_fqdn) - def get_options(self) -> Optional[str]: + def get_options(self) -> str | None: from dns import resolver try: diff --git a/pymongo/synchronous/topology.py b/pymongo/synchronous/topology.py index 9accc15dc1..950163ebc0 100644 --- a/pymongo/synchronous/topology.py +++ b/pymongo/synchronous/topology.py @@ -26,7 +26,7 @@ import weakref from collections.abc import Mapping from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Optional, cast +from typing import TYPE_CHECKING, Any, Callable, cast from pymongo import _csot, common, helpers_shared, periodic_executor from pymongo._telemetry import ( @@ -86,7 +86,7 @@ class Topology(_AgnosticTopologyBase[Pool, Server]): _settings: TopologySettings _events_executor: periodic_executor.PeriodicExecutor - _srv_monitor: Optional[SrvMonitor] + _srv_monitor: SrvMonitor | None _monitor_tasks: list[MonitorBase] def __init__(self, topology_settings: TopologySettings): @@ -162,10 +162,10 @@ def select_servers( self, selector: Callable[[Selection], Selection], operation: str, - server_selection_timeout: Optional[float] = None, - address: Optional[_Address] = None, - operation_id: Optional[int] = None, - deprioritized_servers: Optional[list[Server]] = None, + server_selection_timeout: float | None = None, + address: _Address | None = None, + operation_id: int | None = None, + deprioritized_servers: list[Server] | None = None, ) -> list[Server]: """Return a list of Servers matching selector, or time out. @@ -210,16 +210,16 @@ def _select_servers_loop( selector: Callable[[Selection], Selection], timeout: float, operation: str, - operation_id: Optional[int], - address: Optional[_Address], - deprioritized_servers: Optional[list[Server]] = None, + operation_id: int | None, + address: _Address | None, + deprioritized_servers: list[Server] | None = None, ) -> list[ServerDescription]: """select_servers() guts. Hold the lock when calling this.""" now = time.monotonic() end_time = now + timeout logged_waiting = False # Server selection does not have APM events, gate only on logging - ss: Optional[_ServerSelectionTelemetry] = None + ss: _ServerSelectionTelemetry | None = None if _is_debug_enabled(_SERVER_SELECTION_LOGGER): ss = _ServerSelectionTelemetry( self._topology_id, selector, operation, operation_id, self.description @@ -269,10 +269,10 @@ def _select_server( self, selector: Callable[[Selection], Selection], operation: str, - server_selection_timeout: Optional[float] = None, - address: Optional[_Address] = None, - deprioritized_servers: Optional[list[Server]] = None, - operation_id: Optional[int] = None, + server_selection_timeout: float | None = None, + address: _Address | None = None, + deprioritized_servers: list[Server] | None = None, + operation_id: int | None = None, ) -> Server: servers = self.select_servers( selector, @@ -294,10 +294,10 @@ def select_server( self, selector: Callable[[Selection], Selection], operation: str, - server_selection_timeout: Optional[float] = None, - address: Optional[_Address] = None, - deprioritized_servers: Optional[list[Server]] = None, - operation_id: Optional[int] = None, + server_selection_timeout: float | None = None, + address: _Address | None = None, + deprioritized_servers: list[Server] | None = None, + operation_id: int | None = None, ) -> Server: """Like select_servers, but choose a random server if several match.""" server = self._select_server( @@ -326,8 +326,8 @@ def select_server_by_address( self, address: _Address, operation: str, - server_selection_timeout: Optional[int] = None, - operation_id: Optional[int] = None, + server_selection_timeout: int | None = None, + operation_id: int | None = None, ) -> Server: """Return a Server for "address", reconnecting if necessary. @@ -450,7 +450,7 @@ def on_srv_update(self, seedlist: list[tuple[str, Any]]) -> None: if self._opened: self._process_srv_update(seedlist) - def get_primary(self) -> Optional[_Address]: + def get_primary(self) -> _Address | None: """Return primary's address or None.""" # Implemented here in Topology instead of MongoClient, so it can lock. with self._lock: @@ -481,7 +481,7 @@ def get_arbiters(self) -> set[_Address]: """Return set of arbiter addresses.""" return self._get_replica_set_members(arbiter_server_selector) - def receive_cluster_time(self, cluster_time: Optional[Mapping[str, Any]]) -> None: + def receive_cluster_time(self, cluster_time: Mapping[str, Any] | None) -> None: with self._lock: self._receive_cluster_time_no_lock(cluster_time) diff --git a/pymongo/synchronous/uri_parser.py b/pymongo/synchronous/uri_parser.py index 92608b5efe..343247a807 100644 --- a/pymongo/synchronous/uri_parser.py +++ b/pymongo/synchronous/uri_parser.py @@ -17,7 +17,7 @@ from __future__ import annotations -from typing import Any, Callable, Optional +from typing import Any, Callable from pymongo.common import SRV_SERVICE_NAME, _CaseInsensitiveDictionary from pymongo.errors import ConfigurationError, InvalidURI @@ -40,15 +40,15 @@ def parse_uri( uri: str, - default_port: Optional[int] = DEFAULT_PORT, + default_port: int | None = DEFAULT_PORT, validate: bool = True, warn: bool = False, normalize: bool = True, - connect_timeout: Optional[float] = None, - srv_service_name: Optional[str] = None, - srv_max_hosts: Optional[int] = None, - srv_allowed_hosts_suffix: Optional[str] = None, - srv_host_validator: Optional[Callable[[str], bool]] = None, + connect_timeout: float | None = None, + srv_service_name: str | None = None, + srv_max_hosts: int | None = None, + srv_allowed_hosts_suffix: str | None = None, + srv_host_validator: Callable[[str], bool] | None = None, ) -> dict[str, Any]: """Parse and validate a MongoDB URI. @@ -127,15 +127,15 @@ def parse_uri( def _parse_srv( uri: str, - default_port: Optional[int] = DEFAULT_PORT, + default_port: int | None = DEFAULT_PORT, validate: bool = True, warn: bool = False, normalize: bool = True, - connect_timeout: Optional[float] = None, - srv_service_name: Optional[str] = None, - srv_max_hosts: Optional[int] = None, - srv_allowed_hosts_suffix: Optional[str] = None, - srv_host_validator: Optional[Callable[[str], bool]] = None, + connect_timeout: float | None = None, + srv_service_name: str | None = None, + srv_max_hosts: int | None = None, + srv_allowed_hosts_suffix: str | None = None, + srv_host_validator: Callable[[str], bool] | None = None, ) -> dict[str, Any]: if uri.startswith(SCHEME): is_srv = False diff --git a/pymongo/topology_description.py b/pymongo/topology_description.py index 87966aca45..abb0b5e47e 100644 --- a/pymongo/topology_description.py +++ b/pymongo/topology_description.py @@ -25,7 +25,6 @@ Any, Callable, NamedTuple, - Optional, cast, ) @@ -64,9 +63,9 @@ def __init__( self, topology_type: int, server_descriptions: dict[_Address, ServerDescription], - replica_set_name: Optional[str], - max_set_version: Optional[int], - max_election_id: Optional[ObjectId], + replica_set_name: str | None, + max_set_version: int | None, + max_election_id: ObjectId | None, topology_settings: Any, ) -> None: """Representation of a deployment of MongoDB servers. @@ -204,22 +203,22 @@ def topology_type_name(self) -> str: return TOPOLOGY_TYPE._fields[self._topology_type] @property - def replica_set_name(self) -> Optional[str]: + def replica_set_name(self) -> str | None: """The replica set name.""" return self._replica_set_name @property - def max_set_version(self) -> Optional[int]: + def max_set_version(self) -> int | None: """Greatest setVersion seen from a primary, or None.""" return self._max_set_version @property - def max_election_id(self) -> Optional[ObjectId]: + def max_election_id(self) -> ObjectId | None: """Greatest electionId seen from a primary, or None.""" return self._max_election_id @property - def logical_session_timeout_minutes(self) -> Optional[int]: + def logical_session_timeout_minutes(self) -> int | None: """Minimum logical session timeout, or None.""" return self._ls_timeout_minutes @@ -244,7 +243,7 @@ def candidate_servers(self) -> list[ServerDescription]: return self._candidate_servers @property - def common_wire_version(self) -> Optional[int]: + def common_wire_version(self) -> int | None: """Minimum of all servers' max wire versions, or None.""" servers = self.known_servers if servers: @@ -260,7 +259,7 @@ def heartbeat_frequency(self) -> int: def srv_max_hosts(self) -> int: return self._topology_settings._srv_max_hosts - def _apply_local_threshold(self, selection: Optional[Selection]) -> list[ServerDescription]: + def _apply_local_threshold(self, selection: Selection | None) -> list[ServerDescription]: if not selection: return [] round_trip_times: list[float] = [] @@ -278,9 +277,7 @@ def _apply_local_threshold(self, selection: Optional[Selection]) -> list[ServerD if (cast(float, s.round_trip_time) - fastest) <= threshold ] - def _filter_servers( - self, deprioritized_servers: Optional[list[ServerDescription]] = None - ) -> None: + def _filter_servers(self, deprioritized_servers: list[ServerDescription] | None = None) -> None: """Filter out deprioritized servers from a list of server candidates.""" if not deprioritized_servers: self._candidate_servers = self.known_servers @@ -296,9 +293,9 @@ def _filter_servers( def apply_selector( self, selector: Any, - address: Optional[_Address] = None, - custom_selector: Optional[_ServerSelector] = None, - deprioritized_servers: Optional[list[ServerDescription]] = None, + address: _Address | None = None, + custom_selector: _ServerSelector | None = None, + deprioritized_servers: list[ServerDescription] | None = None, ) -> list[ServerDescription]: """List of servers matching the provided selector(s). @@ -563,11 +560,11 @@ def _updated_topology_description_srv_polling( def _update_rs_from_primary( sds: MutableMapping[_Address, ServerDescription], - replica_set_name: Optional[str], + replica_set_name: str | None, server_description: ServerDescription, - max_set_version: Optional[int], - max_election_id: Optional[ObjectId], -) -> tuple[int, Optional[str], Optional[int], Optional[ObjectId]]: + max_set_version: int | None, + max_election_id: ObjectId | None, +) -> tuple[int, str | None, int | None, ObjectId | None]: """Update topology description from a primary's hello response. Pass in a dict of ServerDescriptions, current replica set name, the @@ -651,7 +648,7 @@ def _update_rs_from_primary( def _update_rs_with_primary_from_member( sds: MutableMapping[_Address, ServerDescription], - replica_set_name: Optional[str], + replica_set_name: str | None, server_description: ServerDescription, ) -> int: """RS with known primary. Process a response from a non-primary. @@ -674,9 +671,9 @@ def _update_rs_with_primary_from_member( def _update_rs_no_primary_from_member( sds: MutableMapping[_Address, ServerDescription], - replica_set_name: Optional[str], + replica_set_name: str | None, server_description: ServerDescription, -) -> tuple[int, Optional[str]]: +) -> tuple[int, str | None]: """RS without known primary. Update from a non-primary's response. Pass in a dict of ServerDescriptions, current replica set name, and the diff --git a/pymongo/topology_shared.py b/pymongo/topology_shared.py index 50bf0c8ad1..34821a9c47 100644 --- a/pymongo/topology_shared.py +++ b/pymongo/topology_shared.py @@ -19,7 +19,7 @@ import queue import weakref from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, Callable, Generic, Optional, TypeVar +from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar from pymongo import _csot from pymongo._telemetry import _SdamTelemetry @@ -64,7 +64,7 @@ def __init__( max_wire_version: int, sock_generation: int, completed_handshake: bool, - service_id: Optional[ObjectId], + service_id: ObjectId | None, ): self.error = error self.max_wire_version = max_wire_version @@ -74,7 +74,7 @@ def __init__( def _is_stale_error_topology_version( - current_tv: Optional[Mapping[str, Any]], error_tv: Optional[Mapping[str, Any]] + current_tv: Mapping[str, Any] | None, error_tv: Mapping[str, Any] | None ) -> bool: """Return True if the error's topologyVersion is <= current.""" if current_tv is None or error_tv is None: @@ -137,8 +137,8 @@ def __init__(self, topology_settings: _BaseTopologySettings[Any, Any]) -> None: self._opened = False self._closed = False self._servers: dict[_Address, _ServerT] = {} - self._pid: Optional[int] = None - self._max_cluster_time: Optional[ClusterTime] = None + self._pid: int | None = None + self._max_cluster_time: ClusterTime | None = None self._session_pool = _ServerSessionPool() # The lock, condition, events executor, SRV monitor, and monitor tasks # are IO-specific and are created by the async/sync subclasses. @@ -153,7 +153,7 @@ def get_server_selection_timeout(self) -> float: return self._settings.server_selection_timeout return timeout - def get_server_by_address(self, address: _Address) -> Optional[_ServerT]: + def get_server_by_address(self, address: _Address) -> _ServerT | None: """Get a Server or None. Returns the current version of the server immediately, even if it's @@ -166,11 +166,11 @@ def get_server_by_address(self, address: _Address) -> Optional[_ServerT]: def has_server(self, address: _Address) -> bool: return address in self._servers - def max_cluster_time(self) -> Optional[ClusterTime]: + def max_cluster_time(self) -> ClusterTime | None: """Return a document, the highest seen $clusterTime.""" return self._max_cluster_time - def _receive_cluster_time_no_lock(self, cluster_time: Optional[Mapping[str, Any]]) -> None: + def _receive_cluster_time_no_lock(self, cluster_time: Mapping[str, Any] | None) -> None: # Driver Sessions Spec: "Whenever a driver receives a cluster time from # a server it MUST compare it to the current highest seen cluster time # for the deployment. If the new cluster time is higher than the @@ -202,7 +202,7 @@ def pop_all_sessions(self) -> list[_ServerSession]: """Pop all session ids from the pool.""" return self._session_pool.pop_all() - def get_server_session(self, session_timeout_minutes: Optional[int]) -> _ServerSession: + def get_server_session(self, session_timeout_minutes: int | None) -> _ServerSession: """Start or resume a server session, or raise ConfigurationError.""" return self._session_pool.get_server_session(session_timeout_minutes) @@ -329,7 +329,7 @@ def __repr__(self) -> str: msg = "CLOSED " return f"<{self.__class__.__name__} {msg}{self._description!r}>" - def eq_props(self) -> tuple[tuple[_Address, ...], Optional[str], Optional[str], str]: + def eq_props(self) -> tuple[tuple[_Address, ...], str | None, str | None, str]: """The properties to use for MongoClient/AsyncMongoClient and Topology equality checks. """ diff --git a/pymongo/typings.py b/pymongo/typings.py index aae4fbe7f5..aca6fd7da8 100644 --- a/pymongo/typings.py +++ b/pymongo/typings.py @@ -20,7 +20,6 @@ from typing import ( TYPE_CHECKING, Any, - Optional, TypeVar, Union, ) @@ -49,7 +48,7 @@ # Common Shared Types. -_Address = tuple[str, Optional[int]] +_Address = tuple[str, int | None] _CollationIn = Union[Mapping[str, Any], "Collation"] _Pipeline = Sequence[Mapping[str, Any]] ClusterTime = Mapping[str, Any] @@ -68,7 +67,7 @@ _AgnosticClientBulk = Union["_AsyncClientBulk", "_ClientBulk"] -def strip_optional(elem: Optional[_T]) -> _T: +def strip_optional(elem: _T | None) -> _T: """This function is to allow us to cast all the elements of an iterator from Optional[_T] to _T while inside a list comprehension. """ diff --git a/pymongo/uri_parser_shared.py b/pymongo/uri_parser_shared.py index 5e34c2d702..e6775b8668 100644 --- a/pymongo/uri_parser_shared.py +++ b/pymongo/uri_parser_shared.py @@ -27,8 +27,6 @@ from typing import ( TYPE_CHECKING, Any, - Optional, - Union, cast, ) from urllib.parse import unquote_plus @@ -155,9 +153,7 @@ def parse_userinfo(userinfo: str) -> tuple[str, str]: return unquote_plus(user), unquote_plus(passwd) -def parse_ipv6_literal_host( - entity: str, default_port: Optional[int] -) -> tuple[str, Optional[Union[str, int]]]: +def parse_ipv6_literal_host(entity: str, default_port: int | None) -> tuple[str, str | int | None]: """Validates an IPv6 literal host:port string. Returns a 2-tuple of IPv6 literal followed by port where @@ -178,7 +174,7 @@ def parse_ipv6_literal_host( return entity[1:i], entity[i + 2 :] -def parse_host(entity: str, default_port: Optional[int] = DEFAULT_PORT) -> _Address: +def parse_host(entity: str, default_port: int | None = DEFAULT_PORT) -> _Address: """Validates a host string Returns a 2-tuple of host followed by port where port is default_port @@ -190,7 +186,7 @@ def parse_host(entity: str, default_port: Optional[int] = DEFAULT_PORT) -> _Addr specified in entity. """ host = entity - port: Optional[Union[str, int]] = default_port + port: str | int | None = default_port if entity[0] == "[": host, port = parse_ipv6_literal_host(entity, default_port) elif entity.endswith(".sock"): @@ -237,7 +233,7 @@ def parse_host(entity: str, default_port: Optional[int] = DEFAULT_PORT) -> _Addr } -def _parse_options(opts: str, delim: Optional[str]) -> _CaseInsensitiveDictionary: +def _parse_options(opts: str, delim: str | None) -> _CaseInsensitiveDictionary: """Helper method for split_options which creates the options dict. Also handles the creation of a list for the URI tag_sets/ readpreferencetags portion, and the use of a unicode options string. @@ -433,7 +429,7 @@ def split_options( return options -def split_hosts(hosts: str, default_port: Optional[int] = DEFAULT_PORT) -> list[_Address]: +def split_hosts(hosts: str, default_port: int | None = DEFAULT_PORT) -> list[_Address]: """Takes a string of the form host1[:port],host2[:port]... and splits it into (host, port) tuples. If [:port] isn't present the default_port is used. @@ -494,7 +490,7 @@ def _check_options(nodes: Sized, options: Mapping[str, Any]) -> None: def _parse_kms_tls_options( - kms_tls_options: Optional[Mapping[str, Any]], + kms_tls_options: Mapping[str, Any] | None, is_sync: bool, ) -> dict[str, SSLContext]: """Parse KMS TLS connection options.""" @@ -531,11 +527,11 @@ def _parse_kms_tls_options( def _validate_uri( uri: str, - default_port: Optional[int] = DEFAULT_PORT, + default_port: int | None = DEFAULT_PORT, validate: bool = True, warn: bool = False, normalize: bool = True, - srv_max_hosts: Optional[int] = None, + srv_max_hosts: int | None = None, ) -> dict[str, Any]: if uri.startswith(SCHEME): is_srv = False diff --git a/pymongo/write_concern.py b/pymongo/write_concern.py index 6f3731849f..ebefe4ee4a 100644 --- a/pymongo/write_concern.py +++ b/pymongo/write_concern.py @@ -19,7 +19,7 @@ from __future__ import annotations -from typing import Any, Optional, Union +from typing import Any from pymongo.errors import ConfigurationError @@ -65,10 +65,10 @@ class WriteConcern: def __init__( self, - w: Optional[Union[int, str]] = None, - wtimeout: Optional[int] = None, - j: Optional[bool] = None, - fsync: Optional[bool] = None, + w: int | str | None = None, + wtimeout: int | None = None, + j: bool | None = None, + fsync: bool | None = None, ) -> None: self.__document: dict[str, Any] = {} self.__acknowledged = True diff --git a/pyproject.toml b/pyproject.toml index a181d9c69d..6ff8c75ede 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -222,7 +222,6 @@ select = [ ignore = [ "PLR", # Design related pylint codes "E501", # Line too long - "UP007", # Use `X | Y` for type annotation "EM101", # Exception must not use a string literal, assign to variable first "EM102", # Exception must not use an f-string literal, assign to variable first "G004", # Logging statement uses f-string" @@ -241,7 +240,6 @@ ignore = [ "EM103", # Exception must not use a `.format()` string directly, assign to variable first "C408", # Unnecessary `dict` call (rewrite as a literal) "SIM117", # Use a single `with` statement with multiple contexts instead of nested `with` statements - "UP045", # Use `X | Y` for union annotation (Enable when we drop 3.9 support) "PLC0415", # `import` should be at the top-level of a file "UP035", # Deprecated import (e.g. `typing.Callable` instead of `collections.abc.Callable`) "UP041", # `asyncio.TimeoutError` is an alias of `TimeoutError` on 3.11+ diff --git a/test/asynchronous/helpers.py b/test/asynchronous/helpers.py index bbce967767..ca7dbbfe65 100644 --- a/test/asynchronous/helpers.py +++ b/test/asynchronous/helpers.py @@ -141,7 +141,7 @@ def __init__(self, **kwargs): async def start(self): self.task = asyncio.create_task(self.run(), name=self.name) - async def join(self, timeout: Optional[float] = None): # type: ignore[override] + async def join(self, timeout: float | None = None): # type: ignore[override] if self.task is not None: await asyncio.wait([self.task], timeout=timeout) diff --git a/test/asynchronous/test_bulk.py b/test/asynchronous/test_bulk.py index 5c6bbc9764..4850e56560 100644 --- a/test/asynchronous/test_bulk.py +++ b/test/asynchronous/test_bulk.py @@ -924,7 +924,7 @@ async def test_no_remove(self): class AsyncTestBulkWriteConcern(AsyncBulkTestBase): - w: Optional[int] + w: int | None secondary: AsyncMongoClient async def asyncSetUp(self): diff --git a/test/asynchronous/test_client_metadata.py b/test/asynchronous/test_client_metadata.py index a0438fb72a..da36c9752f 100644 --- a/test/asynchronous/test_client_metadata.py +++ b/test/asynchronous/test_client_metadata.py @@ -67,10 +67,10 @@ def respond(r): async def send_ping_and_get_metadata( self, client: AsyncMongoClient, is_handshake: bool - ) -> tuple[str, Optional[str], Optional[str], dict[str, Any]]: + ) -> tuple[str, str | None, str | None, dict[str, Any]]: # reset if handshake request if is_handshake: - self.handshake_req: Optional[dict] = None + self.handshake_req: dict | None = None await client.admin.command("ping") metadata = _get_handshake_driver_info(self.handshake_req) @@ -86,8 +86,8 @@ async def check_metadata_added( self, client: AsyncMongoClient, add_name: str, - add_version: Optional[str], - add_platform: Optional[str], + add_version: str | None, + add_platform: str | None, ) -> None: # send initial metadata name, version, platform, metadata = await self.send_ping_and_get_metadata(client, True) @@ -196,7 +196,7 @@ async def test_doesnt_update_established_connections(self): add_name, add_version, add_platform = "framework", "2.0", "Framework Platform" client.append_metadata(DriverInfo(add_name, add_version, add_platform)) # check new data isn't sent - self.handshake_req: Optional[dict] = None + self.handshake_req: dict | None = None await client.admin.command("ping") self.assertIsNone(self.handshake_req) self.assertEqual(listener.event_count(ConnectionClosedEvent), 0) @@ -299,7 +299,7 @@ async def test_10_entries_in_driver_name_and_driver_version_correspond_by_index( driver_name = name0.split("|")[0] driver_version = version0.split("|")[0] - def resolve(value: Optional[str]) -> Optional[str]: + def resolve(value: str | None) -> str | None: if value is None: return None return value.format(driver_name=driver_name, driver_version=driver_version) diff --git a/test/asynchronous/test_comment.py b/test/asynchronous/test_comment.py index c681170482..c57f08ae6e 100644 --- a/test/asynchronous/test_comment.py +++ b/test/asynchronous/test_comment.py @@ -55,7 +55,7 @@ async def _test_ops( "signature of function %s" % (h.__name__), ) self.assertEqual( - inspect.signature(h).parameters["comment"].annotation, "Optional[Any]" + inspect.signature(h).parameters["comment"].annotation, "Any | None" ) if isinstance(maybe_cursor, AsyncCommandCursor): await maybe_cursor.close() diff --git a/test/asynchronous/test_database.py b/test/asynchronous/test_database.py index bbca8ff19a..b818cd45e0 100644 --- a/test/asynchronous/test_database.py +++ b/test/asynchronous/test_database.py @@ -212,7 +212,7 @@ async def test_list_collection_names_filter(self): await db.capped.insert_one({}) await db.non_capped.insert_one({}) self.addAsyncCleanup(client.drop_database, db.name) - filter: Union[None, Mapping[str, Any]] + filter: None | Mapping[str, Any] # Should not send nameOnly. for filter in ({"options.capped": True}, {"options.capped": True, "name": "capped"}): listener.reset() diff --git a/test/asynchronous/test_encryption.py b/test/asynchronous/test_encryption.py index bc7e311843..b52f1a1d80 100644 --- a/test/asynchronous/test_encryption.py +++ b/test/asynchronous/test_encryption.py @@ -323,8 +323,8 @@ def create_client_encryption( key_vault_namespace: str, key_vault_client: AsyncMongoClient, codec_options: CodecOptions, - kms_tls_options: Optional[Mapping[str, Any]] = None, - kms_connect_callback: Optional[Any] = None, + kms_tls_options: Mapping[str, Any] | None = None, + kms_connect_callback: Any | None = None, ): client_encryption = AsyncClientEncryption( kms_providers, @@ -344,8 +344,8 @@ def unmanaged_create_client_encryption( key_vault_namespace: str, key_vault_client: AsyncMongoClient, codec_options: CodecOptions, - kms_tls_options: Optional[Mapping[str, Any]] = None, - kms_connect_callback: Optional[Any] = None, + kms_tls_options: Mapping[str, Any] | None = None, + kms_connect_callback: Any | None = None, ): client_encryption = AsyncClientEncryption( kms_providers, diff --git a/test/asynchronous/unified_format.py b/test/asynchronous/unified_format.py index 31aca76cab..38e112fa1a 100644 --- a/test/asynchronous/unified_format.py +++ b/test/asynchronous/unified_format.py @@ -1280,7 +1280,7 @@ async def _testOperation_waitForPrimaryChange(self, spec: dict) -> None: old_description: TopologyDescription = self.entity_map[spec["priorTopologyDescription"]] timeout = spec["timeoutMS"] / 1000.0 - def get_primary(td: TopologyDescription) -> Optional[_Address]: + def get_primary(td: TopologyDescription) -> _Address | None: servers = writable_server_selector(Selection.from_topology_description(td)) if servers and servers[0].server_type == SERVER_TYPE.RSPrimary: return servers[0].address diff --git a/test/helpers.py b/test/helpers.py index 6814326d38..e49d861bed 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -141,7 +141,7 @@ def __init__(self, **kwargs): def start(self): self.task = asyncio.create_task(self.run(), name=self.name) - def join(self, timeout: Optional[float] = None): # type: ignore[override] + def join(self, timeout: float | None = None): # type: ignore[override] if self.task is not None: asyncio.wait([self.task], timeout=timeout) diff --git a/test/performance/async_perf_test.py b/test/performance/async_perf_test.py index e4a68e228e..88fd6faf83 100644 --- a/test/performance/async_perf_test.py +++ b/test/performance/async_perf_test.py @@ -397,7 +397,7 @@ async def do_task(self): class TestSmallDocBulkMixedOps(SmallDocMixedTest, AsyncPyMongoTestCase): async def asyncSetUp(self): await super().asyncSetUp() - self.models: list[Union[InsertOne, ReplaceOne, DeleteOne]] = [] + self.models: list[InsertOne | ReplaceOne | DeleteOne] = [] for doc in self.documents: self.models.append(InsertOne(document=doc)) self.models.append(ReplaceOne(filter={}, replacement=doc.copy(), upsert=True)) @@ -411,7 +411,7 @@ class TestSmallDocClientBulkMixedOps(SmallDocMixedTest, AsyncPyMongoTestCase): @async_client_context.require_version_min(8, 0, 0, -24) async def asyncSetUp(self): await super().asyncSetUp() - self.models: list[Union[InsertOne, ReplaceOne, DeleteOne]] = [] + self.models: list[InsertOne | ReplaceOne | DeleteOne] = [] for doc in self.documents: self.models.append(InsertOne(namespace="perftest.corpus", document=doc)) self.models.append( diff --git a/test/performance/perf_test.py b/test/performance/perf_test.py index 5ec13a2b56..d740cc5132 100644 --- a/test/performance/perf_test.py +++ b/test/performance/perf_test.py @@ -478,7 +478,7 @@ def do_task(self): class TestSmallDocBulkMixedOps(SmallDocMixedTest, unittest.TestCase): def setUp(self): super().setUp() - self.models: list[Union[InsertOne, ReplaceOne, DeleteOne]] = [] + self.models: list[InsertOne | ReplaceOne | DeleteOne] = [] for doc in self.documents: self.models.append(InsertOne(document=doc)) self.models.append(ReplaceOne(filter={}, replacement=doc.copy(), upsert=True)) @@ -492,7 +492,7 @@ class TestSmallDocClientBulkMixedOps(SmallDocMixedTest, unittest.TestCase): @client_context.require_version_min(8, 0, 0, -24) def setUp(self): super().setUp() - self.models: list[Union[InsertOne, ReplaceOne, DeleteOne]] = [] + self.models: list[InsertOne | ReplaceOne | DeleteOne] = [] for doc in self.documents: self.models.append(InsertOne(namespace="perftest.corpus", document=doc)) self.models.append( @@ -573,7 +573,7 @@ def do_task(self): self.bucket.open_download_stream(self.uploaded_id).read() -proc_client: Optional[MongoClient] = None +proc_client: MongoClient | None = None def proc_init(client_kwargs): diff --git a/test/test_bulk.py b/test/test_bulk.py index 6c612e0edb..de8561cb33 100644 --- a/test/test_bulk.py +++ b/test/test_bulk.py @@ -922,7 +922,7 @@ def test_no_remove(self): class TestBulkWriteConcern(BulkTestBase): - w: Optional[int] + w: int | None secondary: MongoClient def setUp(self): diff --git a/test/test_client_metadata.py b/test/test_client_metadata.py index b35e3f0bc7..7b9e62426c 100644 --- a/test/test_client_metadata.py +++ b/test/test_client_metadata.py @@ -67,10 +67,10 @@ def respond(r): def send_ping_and_get_metadata( self, client: MongoClient, is_handshake: bool - ) -> tuple[str, Optional[str], Optional[str], dict[str, Any]]: + ) -> tuple[str, str | None, str | None, dict[str, Any]]: # reset if handshake request if is_handshake: - self.handshake_req: Optional[dict] = None + self.handshake_req: dict | None = None client.admin.command("ping") metadata = _get_handshake_driver_info(self.handshake_req) @@ -86,8 +86,8 @@ def check_metadata_added( self, client: MongoClient, add_name: str, - add_version: Optional[str], - add_platform: Optional[str], + add_version: str | None, + add_platform: str | None, ) -> None: # send initial metadata name, version, platform, metadata = self.send_ping_and_get_metadata(client, True) @@ -196,7 +196,7 @@ def test_doesnt_update_established_connections(self): add_name, add_version, add_platform = "framework", "2.0", "Framework Platform" client.append_metadata(DriverInfo(add_name, add_version, add_platform)) # check new data isn't sent - self.handshake_req: Optional[dict] = None + self.handshake_req: dict | None = None client.admin.command("ping") self.assertIsNone(self.handshake_req) self.assertEqual(listener.event_count(ConnectionClosedEvent), 0) @@ -299,7 +299,7 @@ def test_10_entries_in_driver_name_and_driver_version_correspond_by_index(self): driver_name = name0.split("|")[0] driver_version = version0.split("|")[0] - def resolve(value: Optional[str]) -> Optional[str]: + def resolve(value: str | None) -> str | None: if value is None: return None return value.format(driver_name=driver_name, driver_version=driver_version) diff --git a/test/test_comment.py b/test/test_comment.py index 07a10b02ca..49751daa8f 100644 --- a/test/test_comment.py +++ b/test/test_comment.py @@ -55,7 +55,7 @@ def _test_ops( "signature of function %s" % (h.__name__), ) self.assertEqual( - inspect.signature(h).parameters["comment"].annotation, "Optional[Any]" + inspect.signature(h).parameters["comment"].annotation, "Any | None" ) if isinstance(maybe_cursor, CommandCursor): maybe_cursor.close() diff --git a/test/test_database.py b/test/test_database.py index b6e5170ff5..fdf1b44014 100644 --- a/test/test_database.py +++ b/test/test_database.py @@ -211,7 +211,7 @@ def test_list_collection_names_filter(self): db.capped.insert_one({}) db.non_capped.insert_one({}) self.addCleanup(client.drop_database, db.name) - filter: Union[None, Mapping[str, Any]] + filter: None | Mapping[str, Any] # Should not send nameOnly. for filter in ({"options.capped": True}, {"options.capped": True, "name": "capped"}): listener.reset() diff --git a/test/test_encryption.py b/test/test_encryption.py index c0513a3768..dcb4861141 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -323,8 +323,8 @@ def create_client_encryption( key_vault_namespace: str, key_vault_client: MongoClient, codec_options: CodecOptions, - kms_tls_options: Optional[Mapping[str, Any]] = None, - kms_connect_callback: Optional[Any] = None, + kms_tls_options: Mapping[str, Any] | None = None, + kms_connect_callback: Any | None = None, ): client_encryption = ClientEncryption( kms_providers, @@ -344,8 +344,8 @@ def unmanaged_create_client_encryption( key_vault_namespace: str, key_vault_client: MongoClient, codec_options: CodecOptions, - kms_tls_options: Optional[Mapping[str, Any]] = None, - kms_connect_callback: Optional[Any] = None, + kms_tls_options: Mapping[str, Any] | None = None, + kms_connect_callback: Any | None = None, ): client_encryption = ClientEncryption( kms_providers, diff --git a/test/test_typing.py b/test/test_typing.py index 054466e729..6698b7dfd8 100644 --- a/test/test_typing.py +++ b/test/test_typing.py @@ -174,8 +174,8 @@ def test_bulk_write(self) -> None: coll: Collection[Movie] = self.coll requests: list[InsertOne[Movie]] = [InsertOne(Movie(name="American Graffiti", year=1973))] self.assertTrue(coll.bulk_write(requests).acknowledged) - new_requests: list[Union[InsertOne[Movie], ReplaceOne[Movie]]] = [] - input_list: list[Union[InsertOne[Movie], ReplaceOne[Movie]]] = [ + new_requests: list[InsertOne[Movie] | ReplaceOne[Movie]] = [] + input_list: list[InsertOne[Movie] | ReplaceOne[Movie]] = [ InsertOne(Movie(name="American Graffiti", year=1973)), ReplaceOne({}, Movie(name="American Graffiti", year=1973)), ] @@ -187,13 +187,13 @@ def test_bulk_write(self) -> None: @only_type_check def test_bulk_write_heterogeneous(self): coll: Collection[Movie] = self.coll - requests: list[Union[InsertOne[Movie], ReplaceOne, DeleteOne]] = [ + requests: list[InsertOne[Movie] | ReplaceOne | DeleteOne] = [ InsertOne(Movie(name="American Graffiti", year=1973)), ReplaceOne({}, {"name": "American Graffiti", "year": "WRONG_TYPE"}), DeleteOne({}), ] self.assertTrue(coll.bulk_write(requests).acknowledged) - requests_two: list[Union[InsertOne[Movie], ReplaceOne[Movie], DeleteOne]] = [ + requests_two: list[InsertOne[Movie] | ReplaceOne[Movie] | DeleteOne] = [ InsertOne(Movie(name="American Graffiti", year=1973)), ReplaceOne( {}, diff --git a/test/unified_format.py b/test/unified_format.py index 2ebf7c201b..e74bed1ff4 100644 --- a/test/unified_format.py +++ b/test/unified_format.py @@ -1267,7 +1267,7 @@ def _testOperation_waitForPrimaryChange(self, spec: dict) -> None: old_description: TopologyDescription = self.entity_map[spec["priorTopologyDescription"]] timeout = spec["timeoutMS"] / 1000.0 - def get_primary(td: TopologyDescription) -> Optional[_Address]: + def get_primary(td: TopologyDescription) -> _Address | None: servers = writable_server_selector(Selection.from_topology_description(td)) if servers and servers[0].server_type == SERVER_TYPE.RSPrimary: return servers[0].address diff --git a/test/unified_format_shared.py b/test/unified_format_shared.py index 8a0a3cd46b..5eac4d2732 100644 --- a/test/unified_format_shared.py +++ b/test/unified_format_shared.py @@ -336,18 +336,18 @@ def failed(self, event): else: self.add_event(event) - def opened(self, event: Union[ServerOpeningEvent, TopologyOpenedEvent]) -> None: + def opened(self, event: ServerOpeningEvent | TopologyOpenedEvent) -> None: self.add_event(event) def description_changed( - self, event: Union[ServerDescriptionChangedEvent, TopologyDescriptionChangedEvent] + self, event: ServerDescriptionChangedEvent | TopologyDescriptionChangedEvent ) -> None: self.add_event(event) def topology_changed(self, event: TopologyDescriptionChangedEvent) -> None: self.add_event(event) - def closed(self, event: Union[ServerClosedEvent, TopologyClosedEvent]) -> None: + def closed(self, event: ServerClosedEvent | TopologyClosedEvent) -> None: self.add_event(event)