PYTHON-5115 Type validation errors should include the invalid type name (#2085)

Co-authored-by: Iris Ho <iris.ho@mongodb.com>
This commit is contained in:
The Light 2025-02-01 01:31:58 +05:30 committed by GitHub
parent c42f3d6421
commit 44d1d40d65
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 204 additions and 129 deletions

View File

@ -1386,7 +1386,7 @@ def is_valid(bson: bytes) -> bool:
:param bson: the data to be validated
"""
if not isinstance(bson, bytes):
raise TypeError("BSON data must be an instance of a subclass of bytes")
raise TypeError(f"BSON data must be an instance of a subclass of bytes, not {type(bson)}")
try:
_bson_to_dict(bson, DEFAULT_CODEC_OPTIONS)

View File

@ -290,7 +290,7 @@ class Binary(bytes):
subtype: int = BINARY_SUBTYPE,
) -> Binary:
if not isinstance(subtype, int):
raise TypeError("subtype must be an instance of int")
raise TypeError(f"subtype must be an instance of int, not {type(subtype)}")
if subtype >= 256 or subtype < 0:
raise ValueError("subtype must be contained in [0, 256)")
# Support any type that implements the buffer protocol.
@ -321,7 +321,7 @@ class Binary(bytes):
.. versionadded:: 3.11
"""
if not isinstance(uuid, UUID):
raise TypeError("uuid must be an instance of uuid.UUID")
raise TypeError(f"uuid must be an instance of uuid.UUID, not {type(uuid)}")
if uuid_representation not in ALL_UUID_REPRESENTATIONS:
raise ValueError(
@ -470,7 +470,7 @@ class Binary(bytes):
"""
if self.subtype != VECTOR_SUBTYPE:
raise ValueError(f"Cannot decode subtype {self.subtype} as a vector.")
raise ValueError(f"Cannot decode subtype {self.subtype} as a vector")
position = 0
dtype, padding = struct.unpack_from("<sB", self, position)

View File

@ -56,7 +56,7 @@ class Code(str):
**kwargs: Any,
) -> Code:
if not isinstance(code, str):
raise TypeError("code must be an instance of str")
raise TypeError(f"code must be an instance of str, not {type(code)}")
self = str.__new__(cls, code)
@ -67,7 +67,7 @@ class Code(str):
if scope is not None:
if not isinstance(scope, _Mapping):
raise TypeError("scope must be an instance of dict")
raise TypeError(f"scope must be an instance of dict, not {type(scope)}")
if self.__scope is not None:
self.__scope.update(scope) # type: ignore
else:

View File

@ -401,17 +401,23 @@ else:
"uuid_representation must be a value from bson.binary.UuidRepresentation"
)
if not isinstance(unicode_decode_error_handler, str):
raise ValueError("unicode_decode_error_handler must be a string")
raise ValueError(
f"unicode_decode_error_handler must be a string, not {type(unicode_decode_error_handler)}"
)
if tzinfo is not None:
if not isinstance(tzinfo, datetime.tzinfo):
raise TypeError("tzinfo must be an instance of datetime.tzinfo")
raise TypeError(
f"tzinfo must be an instance of datetime.tzinfo, not {type(tzinfo)}"
)
if not tz_aware:
raise ValueError("cannot specify tzinfo without also setting tz_aware=True")
type_registry = type_registry or TypeRegistry()
if not isinstance(type_registry, TypeRegistry):
raise TypeError("type_registry must be an instance of TypeRegistry")
raise TypeError(
f"type_registry must be an instance of TypeRegistry, not {type(type_registry)}"
)
return tuple.__new__(
cls,

View File

@ -56,9 +56,9 @@ class DBRef:
.. seealso:: The MongoDB documentation on `dbrefs <https://dochub.mongodb.org/core/dbrefs>`_.
"""
if not isinstance(collection, str):
raise TypeError("collection must be an instance of str")
raise TypeError(f"collection must be an instance of str, not {type(collection)}")
if database is not None and not isinstance(database, str):
raise TypeError("database must be an instance of str")
raise TypeError(f"database must be an instance of str, not {type(database)}")
self.__collection = collection
self.__id = id

View File

@ -277,7 +277,7 @@ class Decimal128:
point in Binary Integer Decimal (BID) format).
"""
if not isinstance(value, bytes):
raise TypeError("value must be an instance of bytes")
raise TypeError(f"value must be an instance of bytes, not {type(value)}")
if len(value) != 16:
raise ValueError("value must be exactly 16 bytes")
return cls((_UNPACK_64(value[8:])[0], _UNPACK_64(value[:8])[0])) # type: ignore

View File

@ -58,9 +58,9 @@ class Timestamp:
time = time - offset
time = int(calendar.timegm(time.timetuple()))
if not isinstance(time, int):
raise TypeError("time must be an instance of int")
raise TypeError(f"time must be an instance of int, not {type(time)}")
if not isinstance(inc, int):
raise TypeError("inc must be an instance of int")
raise TypeError(f"inc must be an instance of int, not {type(inc)}")
if not 0 <= time < UPPERBOUND:
raise ValueError("time must be contained in [0, 2**32)")
if not 0 <= inc < UPPERBOUND:

View File

@ -100,7 +100,7 @@ class AsyncGridFS:
.. seealso:: The MongoDB documentation on `gridfs <https://dochub.mongodb.org/core/gridfs>`_.
"""
if not isinstance(database, AsyncDatabase):
raise TypeError("database must be an instance of Database")
raise TypeError(f"database must be an instance of Database, not {type(database)}")
database = _clear_entity_type_registry(database)
@ -503,7 +503,7 @@ class AsyncGridFSBucket:
.. seealso:: The MongoDB documentation on `gridfs <https://dochub.mongodb.org/core/gridfs>`_.
"""
if not isinstance(db, AsyncDatabase):
raise TypeError("database must be an instance of AsyncDatabase")
raise TypeError(f"database must be an instance of AsyncDatabase, not {type(db)}")
db = _clear_entity_type_registry(db)
@ -1082,7 +1082,9 @@ class AsyncGridIn:
:attr:`~pymongo.collection.AsyncCollection.write_concern`
"""
if not isinstance(root_collection, AsyncCollection):
raise TypeError("root_collection must be an instance of AsyncCollection")
raise TypeError(
f"root_collection must be an instance of AsyncCollection, not {type(root_collection)}"
)
if not root_collection.write_concern.acknowledged:
raise ConfigurationError("root_collection must use acknowledged write_concern")
@ -1436,7 +1438,9 @@ class AsyncGridOut(GRIDOUT_BASE_CLASS): # type: ignore
from the server. Metadata is fetched when first needed.
"""
if not isinstance(root_collection, AsyncCollection):
raise TypeError("root_collection must be an instance of AsyncCollection")
raise TypeError(
f"root_collection must be an instance of AsyncCollection, not {type(root_collection)}"
)
_disallow_transactions(session)
root_collection = _clear_entity_type_registry(root_collection)

View File

@ -100,7 +100,7 @@ class GridFS:
.. seealso:: The MongoDB documentation on `gridfs <https://dochub.mongodb.org/core/gridfs>`_.
"""
if not isinstance(database, Database):
raise TypeError("database must be an instance of Database")
raise TypeError(f"database must be an instance of Database, not {type(database)}")
database = _clear_entity_type_registry(database)
@ -501,7 +501,7 @@ class GridFSBucket:
.. seealso:: The MongoDB documentation on `gridfs <https://dochub.mongodb.org/core/gridfs>`_.
"""
if not isinstance(db, Database):
raise TypeError("database must be an instance of Database")
raise TypeError(f"database must be an instance of Database, not {type(db)}")
db = _clear_entity_type_registry(db)
@ -1076,7 +1076,9 @@ class GridIn:
:attr:`~pymongo.collection.Collection.write_concern`
"""
if not isinstance(root_collection, Collection):
raise TypeError("root_collection must be an instance of Collection")
raise TypeError(
f"root_collection must be an instance of Collection, not {type(root_collection)}"
)
if not root_collection.write_concern.acknowledged:
raise ConfigurationError("root_collection must use acknowledged write_concern")
@ -1426,7 +1428,9 @@ class GridOut(GRIDOUT_BASE_CLASS): # type: ignore
from the server. Metadata is fetched when first needed.
"""
if not isinstance(root_collection, Collection):
raise TypeError("root_collection must be an instance of Collection")
raise TypeError(
f"root_collection must be an instance of Collection, not {type(root_collection)}"
)
_disallow_transactions(session)
root_collection = _clear_entity_type_registry(root_collection)

View File

@ -160,7 +160,7 @@ def timeout(seconds: Optional[float]) -> ContextManager[None]:
.. versionadded:: 4.2
"""
if not isinstance(seconds, (int, float, type(None))):
raise TypeError("timeout must be None, an int, or a float")
raise TypeError(f"timeout must be None, an int, or a float, not {type(seconds)}")
if seconds and seconds < 0:
raise ValueError("timeout cannot be negative")
if seconds is not None:

View File

@ -160,7 +160,7 @@ class Lock(_ContextManagerMixin, _LoopBoundMixin):
self._locked = False
self._wake_up_first()
else:
raise RuntimeError("Lock is not acquired.")
raise RuntimeError("Lock is not acquired")
def _wake_up_first(self) -> None:
"""Ensure that the first waiter will wake up."""

View File

@ -46,7 +46,7 @@ def _get_azure_response(
try:
data = json.loads(body)
except Exception:
raise ValueError("Azure IMDS response must be in JSON format.") from None
raise ValueError("Azure IMDS response must be in JSON format") from None
for key in ["access_token", "expires_in"]:
if not data.get(key):

View File

@ -161,7 +161,7 @@ def _password_digest(username: str, password: str) -> str:
if len(password) == 0:
raise ValueError("password can't be empty")
if not isinstance(username, str):
raise TypeError("username must be an instance of str")
raise TypeError(f"username must be an instance of str, not {type(username)}")
md5hash = hashlib.md5() # noqa: S324
data = f"{username}:mongo:{password}"

View File

@ -213,7 +213,9 @@ class _OIDCAuthenticator:
)
resp = cb.fetch(context)
if not isinstance(resp, OIDCCallbackResult):
raise ValueError("Callback result must be of type OIDCCallbackResult")
raise ValueError(
f"Callback result must be of type OIDCCallbackResult, not {type(resp)}"
)
self.refresh_token = resp.refresh_token
self.access_token = resp.access_token
self.token_gen_id += 1

View File

@ -310,7 +310,9 @@ class TransactionOptions:
)
if max_commit_time_ms is not None:
if not isinstance(max_commit_time_ms, int):
raise TypeError("max_commit_time_ms must be an integer or None")
raise TypeError(
f"max_commit_time_ms must be an integer or None, not {type(max_commit_time_ms)}"
)
@property
def read_concern(self) -> Optional[ReadConcern]:
@ -902,7 +904,9 @@ class AsyncClientSession:
another `AsyncClientSession` instance.
"""
if not isinstance(cluster_time, _Mapping):
raise TypeError("cluster_time must be a subclass of collections.Mapping")
raise TypeError(
f"cluster_time must be a subclass of collections.Mapping, not {type(cluster_time)}"
)
if not isinstance(cluster_time.get("clusterTime"), Timestamp):
raise ValueError("Invalid cluster_time")
self._advance_cluster_time(cluster_time)
@ -923,7 +927,9 @@ class AsyncClientSession:
another `AsyncClientSession` instance.
"""
if not isinstance(operation_time, Timestamp):
raise TypeError("operation_time must be an instance of bson.timestamp.Timestamp")
raise TypeError(
f"operation_time must be an instance of bson.timestamp.Timestamp, not {type(operation_time)}"
)
self._advance_operation_time(operation_time)
def _process_response(self, reply: Mapping[str, Any]) -> None:

View File

@ -228,7 +228,7 @@ class AsyncCollection(common.BaseObject, Generic[_DocumentType]):
read_concern or database.read_concern,
)
if not isinstance(name, str):
raise TypeError("name must be an instance of str")
raise TypeError(f"name must be an instance of str, not {type(name)}")
from pymongo.asynchronous.database import AsyncDatabase
if not isinstance(database, AsyncDatabase):
@ -2475,7 +2475,7 @@ class AsyncCollection(common.BaseObject, Generic[_DocumentType]):
name = helpers_shared._gen_index_name(index_or_name)
if not isinstance(name, str):
raise TypeError("index_or_name must be an instance of str or list")
raise TypeError(f"index_or_name must be an instance of str or list, not {type(name)}")
cmd = {"dropIndexes": self._name, "index": name}
cmd.update(kwargs)
@ -3078,7 +3078,7 @@ class AsyncCollection(common.BaseObject, Generic[_DocumentType]):
"""
if not isinstance(new_name, str):
raise TypeError("new_name must be an instance of str")
raise TypeError(f"new_name must be an instance of str, not {type(new_name)}")
if not new_name or ".." in new_name:
raise InvalidName("collection names cannot be empty")
@ -3148,7 +3148,7 @@ class AsyncCollection(common.BaseObject, Generic[_DocumentType]):
"""
if not isinstance(key, str):
raise TypeError("key must be an instance of str")
raise TypeError(f"key must be an instance of str, not {type(key)}")
cmd = {"distinct": self._name, "key": key}
if filter is not None:
if "query" in kwargs:
@ -3196,7 +3196,7 @@ class AsyncCollection(common.BaseObject, Generic[_DocumentType]):
common.validate_is_mapping("filter", filter)
if not isinstance(return_document, bool):
raise ValueError(
"return_document must be ReturnDocument.BEFORE or ReturnDocument.AFTER"
f"return_document must be ReturnDocument.BEFORE or ReturnDocument.AFTER, not {type(return_document)}"
)
collation = validate_collation_or_none(kwargs.pop("collation", None))
cmd = {"findAndModify": self._name, "query": filter, "new": return_document}

View File

@ -94,7 +94,9 @@ class AsyncCommandCursor(Generic[_DocumentType]):
self.batch_size(batch_size)
if not isinstance(max_await_time_ms, int) and max_await_time_ms is not None:
raise TypeError("max_await_time_ms must be an integer or None")
raise TypeError(
f"max_await_time_ms must be an integer or None, not {type(max_await_time_ms)}"
)
def __del__(self) -> None:
self._die_no_lock()
@ -115,7 +117,7 @@ class AsyncCommandCursor(Generic[_DocumentType]):
:param batch_size: The size of each batch of results requested.
"""
if not isinstance(batch_size, int):
raise TypeError("batch_size must be an integer")
raise TypeError(f"batch_size must be an integer, not {type(batch_size)}")
if batch_size < 0:
raise ValueError("batch_size must be >= 0")

View File

@ -146,9 +146,9 @@ class AsyncCursor(Generic[_DocumentType]):
spec: Mapping[str, Any] = filter or {}
validate_is_mapping("filter", spec)
if not isinstance(skip, int):
raise TypeError("skip must be an instance of int")
raise TypeError(f"skip must be an instance of int, not {type(skip)}")
if not isinstance(limit, int):
raise TypeError("limit must be an instance of int")
raise TypeError(f"limit must be an instance of int, not {type(limit)}")
validate_boolean("no_cursor_timeout", no_cursor_timeout)
if no_cursor_timeout and not self._explicit_session:
warnings.warn(
@ -171,7 +171,7 @@ class AsyncCursor(Generic[_DocumentType]):
validate_boolean("allow_partial_results", allow_partial_results)
validate_boolean("oplog_replay", oplog_replay)
if not isinstance(batch_size, int):
raise TypeError("batch_size must be an integer")
raise TypeError(f"batch_size must be an integer, not {type(batch_size)}")
if batch_size < 0:
raise ValueError("batch_size must be >= 0")
# Only set if allow_disk_use is provided by the user, else None.
@ -388,7 +388,7 @@ class AsyncCursor(Generic[_DocumentType]):
cursor.add_option(2)
"""
if not isinstance(mask, int):
raise TypeError("mask must be an int")
raise TypeError(f"mask must be an int, not {type(mask)}")
self._check_okay_to_chain()
if mask & _QUERY_OPTIONS["exhaust"]:
@ -408,7 +408,7 @@ class AsyncCursor(Generic[_DocumentType]):
cursor.remove_option(2)
"""
if not isinstance(mask, int):
raise TypeError("mask must be an int")
raise TypeError(f"mask must be an int, not {type(mask)}")
self._check_okay_to_chain()
if mask & _QUERY_OPTIONS["exhaust"]:
@ -432,7 +432,7 @@ class AsyncCursor(Generic[_DocumentType]):
.. versionadded:: 3.11
"""
if not isinstance(allow_disk_use, bool):
raise TypeError("allow_disk_use must be a bool")
raise TypeError(f"allow_disk_use must be a bool, not {type(allow_disk_use)}")
self._check_okay_to_chain()
self._allow_disk_use = allow_disk_use
@ -451,7 +451,7 @@ class AsyncCursor(Generic[_DocumentType]):
.. seealso:: The MongoDB documentation on `limit <https://dochub.mongodb.org/core/limit>`_.
"""
if not isinstance(limit, int):
raise TypeError("limit must be an integer")
raise TypeError(f"limit must be an integer, not {type(limit)}")
if self._exhaust:
raise InvalidOperation("Can't use limit and exhaust together.")
self._check_okay_to_chain()
@ -479,7 +479,7 @@ class AsyncCursor(Generic[_DocumentType]):
:param batch_size: The size of each batch of results requested.
"""
if not isinstance(batch_size, int):
raise TypeError("batch_size must be an integer")
raise TypeError(f"batch_size must be an integer, not {type(batch_size)}")
if batch_size < 0:
raise ValueError("batch_size must be >= 0")
self._check_okay_to_chain()
@ -499,7 +499,7 @@ class AsyncCursor(Generic[_DocumentType]):
:param skip: the number of results to skip
"""
if not isinstance(skip, int):
raise TypeError("skip must be an integer")
raise TypeError(f"skip must be an integer, not {type(skip)}")
if skip < 0:
raise ValueError("skip must be >= 0")
self._check_okay_to_chain()
@ -520,7 +520,7 @@ class AsyncCursor(Generic[_DocumentType]):
:param max_time_ms: the time limit after which the operation is aborted
"""
if not isinstance(max_time_ms, int) and max_time_ms is not None:
raise TypeError("max_time_ms must be an integer or None")
raise TypeError(f"max_time_ms must be an integer or None, not {type(max_time_ms)}")
self._check_okay_to_chain()
self._max_time_ms = max_time_ms
@ -543,7 +543,9 @@ class AsyncCursor(Generic[_DocumentType]):
.. versionadded:: 3.2
"""
if not isinstance(max_await_time_ms, int) and max_await_time_ms is not None:
raise TypeError("max_await_time_ms must be an integer or None")
raise TypeError(
f"max_await_time_ms must be an integer or None, not {type(max_await_time_ms)}"
)
self._check_okay_to_chain()
# Ignore max_await_time_ms if not tailable or await_data is False.
@ -679,7 +681,7 @@ class AsyncCursor(Generic[_DocumentType]):
.. versionadded:: 2.7
"""
if not isinstance(spec, (list, tuple)):
raise TypeError("spec must be an instance of list or tuple")
raise TypeError(f"spec must be an instance of list or tuple, not {type(spec)}")
self._check_okay_to_chain()
self._max = dict(spec)
@ -701,7 +703,7 @@ class AsyncCursor(Generic[_DocumentType]):
.. versionadded:: 2.7
"""
if not isinstance(spec, (list, tuple)):
raise TypeError("spec must be an instance of list or tuple")
raise TypeError(f"spec must be an instance of list or tuple, not {type(spec)}")
self._check_okay_to_chain()
self._min = dict(spec)

View File

@ -122,7 +122,7 @@ class AsyncDatabase(common.BaseObject, Generic[_DocumentType]):
from pymongo.asynchronous.mongo_client import AsyncMongoClient
if not isinstance(name, str):
raise TypeError("name must be an instance of str")
raise TypeError(f"name must be an instance of str, not {type(name)}")
if not isinstance(client, AsyncMongoClient):
# This is for compatibility with mocked and subclassed types, such as in Motor.
@ -1310,7 +1310,7 @@ class AsyncDatabase(common.BaseObject, Generic[_DocumentType]):
name = name.name
if not isinstance(name, str):
raise TypeError("name_or_collection must be an instance of str")
raise TypeError(f"name_or_collection must be an instance of str, not {type(name)}")
encrypted_fields = await self._get_encrypted_fields(
{"encryptedFields": encrypted_fields},
name,
@ -1374,7 +1374,9 @@ class AsyncDatabase(common.BaseObject, Generic[_DocumentType]):
name = name.name
if not isinstance(name, str):
raise TypeError("name_or_collection must be an instance of str or AsyncCollection")
raise TypeError(
f"name_or_collection must be an instance of str or AsyncCollection, not {type(name)}"
)
cmd = {"validate": name, "scandata": scandata, "full": full}
if comment is not None:
cmd["comment"] = comment

View File

@ -322,7 +322,9 @@ class _EncryptionIO(AsyncMongoCryptCallback): # type: ignore[misc]
raw_doc = RawBSONDocument(data_key, _KEY_VAULT_OPTS)
data_key_id = raw_doc.get("_id")
if not isinstance(data_key_id, Binary) or data_key_id.subtype != UUID_SUBTYPE:
raise TypeError("data_key _id must be Binary with a UUID subtype")
raise TypeError(
f"data_key _id must be Binary with a UUID subtype, not {type(data_key_id)}"
)
assert self.key_vault_coll is not None
await self.key_vault_coll.insert_one(raw_doc)
@ -644,7 +646,9 @@ class AsyncClientEncryption(Generic[_DocumentType]):
)
if not isinstance(codec_options, CodecOptions):
raise TypeError("codec_options must be an instance of bson.codec_options.CodecOptions")
raise TypeError(
f"codec_options must be an instance of bson.codec_options.CodecOptions, not {type(codec_options)}"
)
if not isinstance(key_vault_client, AsyncMongoClient):
# This is for compatibility with mocked and subclassed types, such as in Motor.

View File

@ -750,7 +750,7 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]):
if port is None:
port = self.PORT
if not isinstance(port, int):
raise TypeError("port must be an instance of int")
raise TypeError(f"port must be an instance of int, not {type(port)}")
# _pool_class, _monitor_class, and _condition_class are for deep
# customization of PyMongo, e.g. Motor.
@ -1971,7 +1971,7 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]):
The cursor is closed synchronously on the current thread.
"""
if not isinstance(cursor_id, int):
raise TypeError("cursor_id must be an instance of int")
raise TypeError(f"cursor_id must be an instance of int, not {type(cursor_id)}")
try:
if conn_mgr:
@ -2093,7 +2093,9 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]):
"""If provided session is None, lend a temporary session."""
if session is not None:
if not isinstance(session, client_session.AsyncClientSession):
raise ValueError("'session' argument must be an AsyncClientSession or None.")
raise ValueError(
f"'session' argument must be an AsyncClientSession or None, not {type(session)}"
)
# Don't call end_session.
yield session
return
@ -2247,7 +2249,9 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]):
name = name.name
if not isinstance(name, str):
raise TypeError("name_or_database must be an instance of str or a AsyncDatabase")
raise TypeError(
f"name_or_database must be an instance of str or a AsyncDatabase, not {type(name)}"
)
async with await self._conn_for_writes(session, operation=_Op.DROP_DATABASE) as conn:
await self[name]._command(

View File

@ -107,7 +107,7 @@ def _build_credentials_tuple(
) -> MongoCredential:
"""Build and return a mechanism specific credentials tuple."""
if mech not in ("MONGODB-X509", "MONGODB-AWS", "MONGODB-OIDC") and user is None:
raise ConfigurationError(f"{mech} requires a username.")
raise ConfigurationError(f"{mech} requires a username")
if mech == "GSSAPI":
if source is not None and source != "$external":
raise ValueError("authentication source must be $external or None for GSSAPI")
@ -219,7 +219,7 @@ def _build_credentials_tuple(
else:
source_database = source or database or "admin"
if passwd is None:
raise ConfigurationError("A password is required.")
raise ConfigurationError("A password is required")
return MongoCredential(mech, source_database, user, passwd, None, _Cache())

View File

@ -223,4 +223,4 @@ def validate_collation_or_none(
return value.document
if isinstance(value, dict):
return value
raise TypeError("collation must be a dict, an instance of collation.Collation, or None.")
raise TypeError("collation must be a dict, an instance of collation.Collation, or None")

View File

@ -202,7 +202,7 @@ def validate_integer(option: str, value: Any) -> int:
return int(value)
except ValueError:
raise ValueError(f"The value of {option} must be an integer") from None
raise TypeError(f"Wrong type for {option}, value must be an integer")
raise TypeError(f"Wrong type for {option}, value must be an integer, not {type(value)}")
def validate_positive_integer(option: str, value: Any) -> int:
@ -250,7 +250,7 @@ def validate_string(option: str, value: Any) -> str:
"""Validates that 'value' is an instance of `str`."""
if isinstance(value, str):
return value
raise TypeError(f"Wrong type for {option}, value must be an instance of 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]:
@ -269,7 +269,9 @@ def validate_int_or_basestring(option: str, value: Any) -> Union[int, str]:
return int(value)
except ValueError:
return value
raise TypeError(f"Wrong type for {option}, value must be an integer or a string")
raise TypeError(
f"Wrong type for {option}, value must be an integer or a string, not {type(value)}"
)
def validate_non_negative_int_or_basestring(option: Any, value: Any) -> Union[int, str]:
@ -282,7 +284,9 @@ def validate_non_negative_int_or_basestring(option: Any, value: Any) -> Union[in
except ValueError:
return value
return validate_non_negative_integer(option, val)
raise TypeError(f"Wrong type for {option}, value must be an non negative integer or a string")
raise TypeError(
f"Wrong type for {option}, value must be an non negative integer or a string, not {type(value)}"
)
def validate_positive_float(option: str, value: Any) -> float:
@ -365,7 +369,7 @@ def validate_max_staleness(option: str, value: Any) -> int:
def validate_read_preference(dummy: Any, value: Any) -> _ServerMode:
"""Validate a read preference."""
if not isinstance(value, _ServerMode):
raise TypeError(f"{value!r} is not a read preference.")
raise TypeError(f"{value!r} is not a read preference")
return value
@ -441,7 +445,9 @@ def validate_auth_mechanism_properties(option: str, value: Any) -> dict[str, Uni
props: dict[str, Any] = {}
if not isinstance(value, str):
if not isinstance(value, dict):
raise ValueError("Auth mechanism properties must be given as a string or a dictionary")
raise ValueError(
f"Auth mechanism properties must be given as a string or a dictionary, not {type(value)}"
)
for key, value in value.items(): # noqa: B020
if isinstance(value, str):
props[key] = value
@ -453,7 +459,7 @@ def validate_auth_mechanism_properties(option: str, value: Any) -> dict[str, Uni
from pymongo.auth_oidc_shared import OIDCCallback
if not isinstance(value, OIDCCallback):
raise ValueError("callback must be an OIDCCallback object")
raise ValueError(f"callback must be an OIDCCallback object, not {type(value)}")
props[key] = value
else:
raise ValueError(f"Invalid type for auth mechanism property {key}, {type(value)}")
@ -476,7 +482,7 @@ def validate_auth_mechanism_properties(option: str, value: Any) -> dict[str, Uni
raise ValueError(
f"{key} is not a supported auth "
"mechanism property. Must be one of "
f"{tuple(_MECHANISM_PROPS)}."
f"{tuple(_MECHANISM_PROPS)}"
)
if key == "CANONICALIZE_HOST_NAME":
@ -520,7 +526,7 @@ def validate_type_registry(option: Any, value: Any) -> Optional[TypeRegistry]:
def validate_list(option: str, value: Any) -> list:
"""Validates that 'value' is a list."""
if not isinstance(value, list):
raise TypeError(f"{option} must be a list")
raise TypeError(f"{option} must be a list, not {type(value)}")
return value
@ -587,7 +593,7 @@ def validate_server_api_or_none(option: Any, value: Any) -> Optional[ServerApi]:
if value is None:
return value
if not isinstance(value, ServerApi):
raise TypeError(f"{option} must be an instance of ServerApi")
raise TypeError(f"{option} must be an instance of ServerApi, not {type(value)}")
return value
@ -596,7 +602,7 @@ def validate_is_callable_or_none(option: Any, value: Any) -> Optional[Callable]:
if value is None:
return value
if not callable(value):
raise ValueError(f"{option} must be a callable")
raise ValueError(f"{option} must be a callable, not {type(value)}")
return value
@ -651,7 +657,7 @@ def validate_auto_encryption_opts_or_none(option: Any, value: Any) -> Optional[A
from pymongo.encryption_options import AutoEncryptionOpts
if not isinstance(value, AutoEncryptionOpts):
raise TypeError(f"{option} must be an instance of AutoEncryptionOpts")
raise TypeError(f"{option} must be an instance of AutoEncryptionOpts, not {type(value)}")
return value
@ -668,7 +674,9 @@ def validate_datetime_conversion(option: Any, value: Any) -> Optional[DatetimeCo
elif isinstance(value, int):
return DatetimeConversion(value)
raise TypeError(f"{option} must be a str or int representing DatetimeConversion")
raise TypeError(
f"{option} must be a str or int representing DatetimeConversion, not {type(value)}"
)
def validate_server_monitoring_mode(option: str, value: str) -> str:
@ -928,12 +936,14 @@ class BaseObject:
if not isinstance(write_concern, WriteConcern):
raise TypeError(
"write_concern must be an instance of pymongo.write_concern.WriteConcern"
f"write_concern must be an instance of pymongo.write_concern.WriteConcern, not {type(write_concern)}"
)
self._write_concern = write_concern
if not isinstance(read_concern, ReadConcern):
raise TypeError("read_concern must be an instance of pymongo.read_concern.ReadConcern")
raise TypeError(
f"read_concern must be an instance of pymongo.read_concern.ReadConcern, not {type(read_concern)}"
)
self._read_concern = read_concern
@property

View File

@ -91,7 +91,7 @@ def validate_zlib_compression_level(option: str, value: Any) -> int:
try:
level = int(value)
except Exception:
raise TypeError(f"{option} must be an integer, not {value!r}.") from None
raise TypeError(f"{option} must be an integer, not {value!r}") from None
if level < -1 or level > 9:
raise ValueError("%s must be between -1 and 9, not %d." % (option, level))
return level

View File

@ -39,7 +39,7 @@ class DriverInfo(namedtuple("DriverInfo", ["name", "version", "platform"])):
for key, value in self._asdict().items():
if value is not None and not isinstance(value, str):
raise TypeError(
f"Wrong type for DriverInfo {key} option, value must be an instance of str"
f"Wrong type for DriverInfo {key} option, value must be an instance of str, not {type(value)}"
)
return self

View File

@ -225,7 +225,9 @@ class AutoEncryptionOpts:
mongocryptd_spawn_args = ["--idleShutdownTimeoutSecs=60"]
self._mongocryptd_spawn_args = mongocryptd_spawn_args
if not isinstance(self._mongocryptd_spawn_args, list):
raise TypeError("mongocryptd_spawn_args must be a list")
raise TypeError(
f"mongocryptd_spawn_args must be a list, not {type(self._mongocryptd_spawn_args)}"
)
if not any("idleShutdownTimeoutSecs" in s for s in self._mongocryptd_spawn_args):
self._mongocryptd_spawn_args.append("--idleShutdownTimeoutSecs=60")
# Maps KMS provider name to a SSLContext.

View File

@ -122,7 +122,7 @@ def _index_list(
"""
if direction is not None:
if not isinstance(key_or_list, str):
raise TypeError("Expected a string and a direction")
raise TypeError(f"Expected a string and a direction, not {type(key_or_list)}")
return [(key_or_list, direction)]
else:
if isinstance(key_or_list, str):
@ -132,7 +132,9 @@ def _index_list(
elif isinstance(key_or_list, abc.Mapping):
return list(key_or_list.items())
elif not isinstance(key_or_list, (list, tuple)):
raise TypeError("if no direction is specified, key_or_list must be an instance of list")
raise TypeError(
f"if no direction is specified, key_or_list must be an instance of list, not {type(key_or_list)}"
)
values: list[tuple[str, int]] = []
for item in key_or_list:
if isinstance(item, str):
@ -172,11 +174,12 @@ def _index_document(index_list: _IndexList) -> dict[str, Any]:
def _validate_index_key_pair(key: Any, value: Any) -> None:
if not isinstance(key, str):
raise TypeError("first item in each key pair must be an instance of str")
raise TypeError(f"first item in each key pair must be an instance of str, not {type(key)}")
if not isinstance(value, (str, int, abc.Mapping)):
raise TypeError(
"second item in each key pair must be 1, -1, "
"'2d', or another valid MongoDB index specifier."
f", not {type(value)}"
)

View File

@ -472,14 +472,15 @@ def _validate_event_listeners(
) -> Sequence[_EventListeners]:
"""Validate event listeners"""
if not isinstance(listeners, abc.Sequence):
raise TypeError(f"{option} must be a list or tuple")
raise TypeError(f"{option} must be a list or tuple, not {type(listeners)}")
for listener in listeners:
if not isinstance(listener, _EventListener):
raise TypeError(
f"Listeners for {option} must be either a "
"CommandListener, ServerHeartbeatListener, "
"ServerListener, TopologyListener, or "
"ConnectionPoolListener."
"ConnectionPoolListener,"
f"not {type(listener)}"
)
return listeners
@ -496,7 +497,8 @@ def register(listener: _EventListener) -> None:
f"Listeners for {listener} must be either a "
"CommandListener, ServerHeartbeatListener, "
"ServerListener, TopologyListener, or "
"ConnectionPoolListener."
"ConnectionPoolListener,"
f"not {type(listener)}"
)
if isinstance(listener, CommandListener):
_LISTENERS.command_listeners.append(listener)

View File

@ -38,7 +38,7 @@ class ReadConcern:
if level is None or isinstance(level, str):
self.__level = level
else:
raise TypeError("level must be a string or None.")
raise TypeError(f"level must be a string or None, not {type(level)}")
@property
def level(self) -> Optional[str]:

View File

@ -115,4 +115,4 @@ else:
def get_ssl_context(*dummy): # type: ignore
"""No ssl module, raise ConfigurationError."""
raise ConfigurationError("The ssl module is not available.")
raise ConfigurationError("The ssl module is not available")

View File

@ -158,7 +158,7 @@ def _password_digest(username: str, password: str) -> str:
if len(password) == 0:
raise ValueError("password can't be empty")
if not isinstance(username, str):
raise TypeError("username must be an instance of str")
raise TypeError(f"username must be an instance of str, not {type(username)}")
md5hash = hashlib.md5() # noqa: S324
data = f"{username}:mongo:{password}"

View File

@ -213,7 +213,9 @@ class _OIDCAuthenticator:
)
resp = cb.fetch(context)
if not isinstance(resp, OIDCCallbackResult):
raise ValueError("Callback result must be of type OIDCCallbackResult")
raise ValueError(
f"Callback result must be of type OIDCCallbackResult, not {type(resp)}"
)
self.refresh_token = resp.refresh_token
self.access_token = resp.access_token
self.token_gen_id += 1

View File

@ -309,7 +309,9 @@ class TransactionOptions:
)
if max_commit_time_ms is not None:
if not isinstance(max_commit_time_ms, int):
raise TypeError("max_commit_time_ms must be an integer or None")
raise TypeError(
f"max_commit_time_ms must be an integer or None, not {type(max_commit_time_ms)}"
)
@property
def read_concern(self) -> Optional[ReadConcern]:
@ -897,7 +899,9 @@ class ClientSession:
another `ClientSession` instance.
"""
if not isinstance(cluster_time, _Mapping):
raise TypeError("cluster_time must be a subclass of collections.Mapping")
raise TypeError(
f"cluster_time must be a subclass of collections.Mapping, not {type(cluster_time)}"
)
if not isinstance(cluster_time.get("clusterTime"), Timestamp):
raise ValueError("Invalid cluster_time")
self._advance_cluster_time(cluster_time)
@ -918,7 +922,9 @@ class ClientSession:
another `ClientSession` instance.
"""
if not isinstance(operation_time, Timestamp):
raise TypeError("operation_time must be an instance of bson.timestamp.Timestamp")
raise TypeError(
f"operation_time must be an instance of bson.timestamp.Timestamp, not {type(operation_time)}"
)
self._advance_operation_time(operation_time)
def _process_response(self, reply: Mapping[str, Any]) -> None:

View File

@ -231,7 +231,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
read_concern or database.read_concern,
)
if not isinstance(name, str):
raise TypeError("name must be an instance of str")
raise TypeError(f"name must be an instance of str, not {type(name)}")
from pymongo.synchronous.database import Database
if not isinstance(database, Database):
@ -2472,7 +2472,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
name = helpers_shared._gen_index_name(index_or_name)
if not isinstance(name, str):
raise TypeError("index_or_name must be an instance of str or list")
raise TypeError(f"index_or_name must be an instance of str or list, not {type(name)}")
cmd = {"dropIndexes": self._name, "index": name}
cmd.update(kwargs)
@ -3071,7 +3071,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
"""
if not isinstance(new_name, str):
raise TypeError("new_name must be an instance of str")
raise TypeError(f"new_name must be an instance of str, not {type(new_name)}")
if not new_name or ".." in new_name:
raise InvalidName("collection names cannot be empty")
@ -3141,7 +3141,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
"""
if not isinstance(key, str):
raise TypeError("key must be an instance of str")
raise TypeError(f"key must be an instance of str, not {type(key)}")
cmd = {"distinct": self._name, "key": key}
if filter is not None:
if "query" in kwargs:
@ -3189,7 +3189,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
common.validate_is_mapping("filter", filter)
if not isinstance(return_document, bool):
raise ValueError(
"return_document must be ReturnDocument.BEFORE or ReturnDocument.AFTER"
f"return_document must be ReturnDocument.BEFORE or ReturnDocument.AFTER, not {type(return_document)}"
)
collation = validate_collation_or_none(kwargs.pop("collation", None))
cmd = {"findAndModify": self._name, "query": filter, "new": return_document}

View File

@ -94,7 +94,9 @@ class CommandCursor(Generic[_DocumentType]):
self.batch_size(batch_size)
if not isinstance(max_await_time_ms, int) and max_await_time_ms is not None:
raise TypeError("max_await_time_ms must be an integer or None")
raise TypeError(
f"max_await_time_ms must be an integer or None, not {type(max_await_time_ms)}"
)
def __del__(self) -> None:
self._die_no_lock()
@ -115,7 +117,7 @@ class CommandCursor(Generic[_DocumentType]):
:param batch_size: The size of each batch of results requested.
"""
if not isinstance(batch_size, int):
raise TypeError("batch_size must be an integer")
raise TypeError(f"batch_size must be an integer, not {type(batch_size)}")
if batch_size < 0:
raise ValueError("batch_size must be >= 0")

View File

@ -146,9 +146,9 @@ class Cursor(Generic[_DocumentType]):
spec: Mapping[str, Any] = filter or {}
validate_is_mapping("filter", spec)
if not isinstance(skip, int):
raise TypeError("skip must be an instance of int")
raise TypeError(f"skip must be an instance of int, not {type(skip)}")
if not isinstance(limit, int):
raise TypeError("limit must be an instance of int")
raise TypeError(f"limit must be an instance of int, not {type(limit)}")
validate_boolean("no_cursor_timeout", no_cursor_timeout)
if no_cursor_timeout and not self._explicit_session:
warnings.warn(
@ -171,7 +171,7 @@ class Cursor(Generic[_DocumentType]):
validate_boolean("allow_partial_results", allow_partial_results)
validate_boolean("oplog_replay", oplog_replay)
if not isinstance(batch_size, int):
raise TypeError("batch_size must be an integer")
raise TypeError(f"batch_size must be an integer, not {type(batch_size)}")
if batch_size < 0:
raise ValueError("batch_size must be >= 0")
# Only set if allow_disk_use is provided by the user, else None.
@ -388,7 +388,7 @@ class Cursor(Generic[_DocumentType]):
cursor.add_option(2)
"""
if not isinstance(mask, int):
raise TypeError("mask must be an int")
raise TypeError(f"mask must be an int, not {type(mask)}")
self._check_okay_to_chain()
if mask & _QUERY_OPTIONS["exhaust"]:
@ -408,7 +408,7 @@ class Cursor(Generic[_DocumentType]):
cursor.remove_option(2)
"""
if not isinstance(mask, int):
raise TypeError("mask must be an int")
raise TypeError(f"mask must be an int, not {type(mask)}")
self._check_okay_to_chain()
if mask & _QUERY_OPTIONS["exhaust"]:
@ -432,7 +432,7 @@ class Cursor(Generic[_DocumentType]):
.. versionadded:: 3.11
"""
if not isinstance(allow_disk_use, bool):
raise TypeError("allow_disk_use must be a bool")
raise TypeError(f"allow_disk_use must be a bool, not {type(allow_disk_use)}")
self._check_okay_to_chain()
self._allow_disk_use = allow_disk_use
@ -451,7 +451,7 @@ class Cursor(Generic[_DocumentType]):
.. seealso:: The MongoDB documentation on `limit <https://dochub.mongodb.org/core/limit>`_.
"""
if not isinstance(limit, int):
raise TypeError("limit must be an integer")
raise TypeError(f"limit must be an integer, not {type(limit)}")
if self._exhaust:
raise InvalidOperation("Can't use limit and exhaust together.")
self._check_okay_to_chain()
@ -479,7 +479,7 @@ class Cursor(Generic[_DocumentType]):
:param batch_size: The size of each batch of results requested.
"""
if not isinstance(batch_size, int):
raise TypeError("batch_size must be an integer")
raise TypeError(f"batch_size must be an integer, not {type(batch_size)}")
if batch_size < 0:
raise ValueError("batch_size must be >= 0")
self._check_okay_to_chain()
@ -499,7 +499,7 @@ class Cursor(Generic[_DocumentType]):
:param skip: the number of results to skip
"""
if not isinstance(skip, int):
raise TypeError("skip must be an integer")
raise TypeError(f"skip must be an integer, not {type(skip)}")
if skip < 0:
raise ValueError("skip must be >= 0")
self._check_okay_to_chain()
@ -520,7 +520,7 @@ class Cursor(Generic[_DocumentType]):
:param max_time_ms: the time limit after which the operation is aborted
"""
if not isinstance(max_time_ms, int) and max_time_ms is not None:
raise TypeError("max_time_ms must be an integer or None")
raise TypeError(f"max_time_ms must be an integer or None, not {type(max_time_ms)}")
self._check_okay_to_chain()
self._max_time_ms = max_time_ms
@ -543,7 +543,9 @@ class Cursor(Generic[_DocumentType]):
.. versionadded:: 3.2
"""
if not isinstance(max_await_time_ms, int) and max_await_time_ms is not None:
raise TypeError("max_await_time_ms must be an integer or None")
raise TypeError(
f"max_await_time_ms must be an integer or None, not {type(max_await_time_ms)}"
)
self._check_okay_to_chain()
# Ignore max_await_time_ms if not tailable or await_data is False.
@ -677,7 +679,7 @@ class Cursor(Generic[_DocumentType]):
.. versionadded:: 2.7
"""
if not isinstance(spec, (list, tuple)):
raise TypeError("spec must be an instance of list or tuple")
raise TypeError(f"spec must be an instance of list or tuple, not {type(spec)}")
self._check_okay_to_chain()
self._max = dict(spec)
@ -699,7 +701,7 @@ class Cursor(Generic[_DocumentType]):
.. versionadded:: 2.7
"""
if not isinstance(spec, (list, tuple)):
raise TypeError("spec must be an instance of list or tuple")
raise TypeError(f"spec must be an instance of list or tuple, not {type(spec)}")
self._check_okay_to_chain()
self._min = dict(spec)

View File

@ -122,7 +122,7 @@ class Database(common.BaseObject, Generic[_DocumentType]):
from pymongo.synchronous.mongo_client import MongoClient
if not isinstance(name, str):
raise TypeError("name must be an instance of str")
raise TypeError(f"name must be an instance of str, not {type(name)}")
if not isinstance(client, MongoClient):
# This is for compatibility with mocked and subclassed types, such as in Motor.
@ -1303,7 +1303,7 @@ class Database(common.BaseObject, Generic[_DocumentType]):
name = name.name
if not isinstance(name, str):
raise TypeError("name_or_collection must be an instance of str")
raise TypeError(f"name_or_collection must be an instance of str, not {type(name)}")
encrypted_fields = self._get_encrypted_fields(
{"encryptedFields": encrypted_fields},
name,
@ -1367,7 +1367,9 @@ class Database(common.BaseObject, Generic[_DocumentType]):
name = name.name
if not isinstance(name, str):
raise TypeError("name_or_collection must be an instance of str or Collection")
raise TypeError(
f"name_or_collection must be an instance of str or Collection, not {type(name)}"
)
cmd = {"validate": name, "scandata": scandata, "full": full}
if comment is not None:
cmd["comment"] = comment

View File

@ -320,7 +320,9 @@ class _EncryptionIO(MongoCryptCallback): # type: ignore[misc]
raw_doc = RawBSONDocument(data_key, _KEY_VAULT_OPTS)
data_key_id = raw_doc.get("_id")
if not isinstance(data_key_id, Binary) or data_key_id.subtype != UUID_SUBTYPE:
raise TypeError("data_key _id must be Binary with a UUID subtype")
raise TypeError(
f"data_key _id must be Binary with a UUID subtype, not {type(data_key_id)}"
)
assert self.key_vault_coll is not None
self.key_vault_coll.insert_one(raw_doc)
@ -642,7 +644,9 @@ class ClientEncryption(Generic[_DocumentType]):
)
if not isinstance(codec_options, CodecOptions):
raise TypeError("codec_options must be an instance of bson.codec_options.CodecOptions")
raise TypeError(
f"codec_options must be an instance of bson.codec_options.CodecOptions, not {type(codec_options)}"
)
if not isinstance(key_vault_client, MongoClient):
# This is for compatibility with mocked and subclassed types, such as in Motor.

View File

@ -748,7 +748,7 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
if port is None:
port = self.PORT
if not isinstance(port, int):
raise TypeError("port must be an instance of int")
raise TypeError(f"port must be an instance of int, not {type(port)}")
# _pool_class, _monitor_class, and _condition_class are for deep
# customization of PyMongo, e.g. Motor.
@ -1965,7 +1965,7 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
The cursor is closed synchronously on the current thread.
"""
if not isinstance(cursor_id, int):
raise TypeError("cursor_id must be an instance of int")
raise TypeError(f"cursor_id must be an instance of int, not {type(cursor_id)}")
try:
if conn_mgr:
@ -2087,7 +2087,9 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
"""If provided session is None, lend a temporary session."""
if session is not None:
if not isinstance(session, client_session.ClientSession):
raise ValueError("'session' argument must be a ClientSession or None.")
raise ValueError(
f"'session' argument must be a ClientSession or None, not {type(session)}"
)
# Don't call end_session.
yield session
return
@ -2235,7 +2237,9 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
name = name.name
if not isinstance(name, str):
raise TypeError("name_or_database must be an instance of str or a Database")
raise TypeError(
f"name_or_database must be an instance of str or a Database, not {type(name)}"
)
with self._conn_for_writes(session, operation=_Op.DROP_DATABASE) as conn:
self[name]._command(

View File

@ -91,7 +91,7 @@ def parse_userinfo(userinfo: str) -> tuple[str, str]:
user, _, passwd = userinfo.partition(":")
# No password is expected with GSSAPI authentication.
if not user:
raise InvalidURI("The empty string is not valid username.")
raise InvalidURI("The empty string is not valid username")
return unquote_plus(user), unquote_plus(passwd)
@ -347,7 +347,7 @@ def split_options(
semi_idx = opts.find(";")
try:
if and_idx >= 0 and semi_idx >= 0:
raise InvalidURI("Can not mix '&' and ';' for option separators.")
raise InvalidURI("Can not mix '&' and ';' for option separators")
elif and_idx >= 0:
options = _parse_options(opts, "&")
elif semi_idx >= 0:
@ -357,7 +357,7 @@ def split_options(
else:
raise ValueError
except ValueError:
raise InvalidURI("MongoDB URI options are key=value pairs.") from None
raise InvalidURI("MongoDB URI options are key=value pairs") from None
options = _handle_security_options(options)
@ -389,7 +389,7 @@ def split_hosts(hosts: str, default_port: Optional[int] = DEFAULT_PORT) -> list[
nodes = []
for entity in hosts.split(","):
if not entity:
raise ConfigurationError("Empty host (or extra comma in host list).")
raise ConfigurationError("Empty host (or extra comma in host list)")
port = default_port
# Unix socket entities don't have ports
if entity.endswith(".sock"):
@ -502,7 +502,7 @@ def parse_uri(
raise InvalidURI(f"Invalid URI scheme: URI must begin with '{SCHEME}' or '{SRV_SCHEME}'")
if not scheme_free:
raise InvalidURI("Must provide at least one hostname or IP.")
raise InvalidURI("Must provide at least one hostname or IP")
user = None
passwd = None

View File

@ -74,7 +74,7 @@ class WriteConcern:
if wtimeout is not None:
if not isinstance(wtimeout, int):
raise TypeError("wtimeout must be an integer")
raise TypeError(f"wtimeout must be an integer, not {type(wtimeout)}")
if wtimeout < 0:
raise ValueError("wtimeout cannot be less than 0")
self.__document["wtimeout"] = wtimeout
@ -98,7 +98,7 @@ class WriteConcern:
raise ValueError("w cannot be less than 0")
self.__acknowledged = w > 0
elif not isinstance(w, str):
raise TypeError("w must be an integer or string")
raise TypeError(f"w must be an integer or string, not {type(w)}")
self.__document["w"] = w
self.__server_default = not self.__document