Skip to content

Commit 44d1d40

Browse files
PYTHON-5115 Type validation errors should include the invalid type name (mongodb#2085)
Co-authored-by: Iris Ho <[email protected]>
1 parent c42f3d6 commit 44d1d40

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+204
-129
lines changed

bson/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1386,7 +1386,7 @@ def is_valid(bson: bytes) -> bool:
13861386
:param bson: the data to be validated
13871387
"""
13881388
if not isinstance(bson, bytes):
1389-
raise TypeError("BSON data must be an instance of a subclass of bytes")
1389+
raise TypeError(f"BSON data must be an instance of a subclass of bytes, not {type(bson)}")
13901390

13911391
try:
13921392
_bson_to_dict(bson, DEFAULT_CODEC_OPTIONS)

bson/binary.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def __new__(
290290
subtype: int = BINARY_SUBTYPE,
291291
) -> Binary:
292292
if not isinstance(subtype, int):
293-
raise TypeError("subtype must be an instance of int")
293+
raise TypeError(f"subtype must be an instance of int, not {type(subtype)}")
294294
if subtype >= 256 or subtype < 0:
295295
raise ValueError("subtype must be contained in [0, 256)")
296296
# Support any type that implements the buffer protocol.
@@ -321,7 +321,7 @@ def from_uuid(
321321
.. versionadded:: 3.11
322322
"""
323323
if not isinstance(uuid, UUID):
324-
raise TypeError("uuid must be an instance of uuid.UUID")
324+
raise TypeError(f"uuid must be an instance of uuid.UUID, not {type(uuid)}")
325325

326326
if uuid_representation not in ALL_UUID_REPRESENTATIONS:
327327
raise ValueError(
@@ -470,7 +470,7 @@ def as_vector(self) -> BinaryVector:
470470
"""
471471

472472
if self.subtype != VECTOR_SUBTYPE:
473-
raise ValueError(f"Cannot decode subtype {self.subtype} as a vector.")
473+
raise ValueError(f"Cannot decode subtype {self.subtype} as a vector")
474474

475475
position = 0
476476
dtype, padding = struct.unpack_from("<sB", self, position)

bson/code.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def __new__(
5656
**kwargs: Any,
5757
) -> Code:
5858
if not isinstance(code, str):
59-
raise TypeError("code must be an instance of str")
59+
raise TypeError(f"code must be an instance of str, not {type(code)}")
6060

6161
self = str.__new__(cls, code)
6262

@@ -67,7 +67,7 @@ def __new__(
6767

6868
if scope is not None:
6969
if not isinstance(scope, _Mapping):
70-
raise TypeError("scope must be an instance of dict")
70+
raise TypeError(f"scope must be an instance of dict, not {type(scope)}")
7171
if self.__scope is not None:
7272
self.__scope.update(scope) # type: ignore
7373
else:

bson/codec_options.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -401,17 +401,23 @@ def __new__(
401401
"uuid_representation must be a value from bson.binary.UuidRepresentation"
402402
)
403403
if not isinstance(unicode_decode_error_handler, str):
404-
raise ValueError("unicode_decode_error_handler must be a string")
404+
raise ValueError(
405+
f"unicode_decode_error_handler must be a string, not {type(unicode_decode_error_handler)}"
406+
)
405407
if tzinfo is not None:
406408
if not isinstance(tzinfo, datetime.tzinfo):
407-
raise TypeError("tzinfo must be an instance of datetime.tzinfo")
409+
raise TypeError(
410+
f"tzinfo must be an instance of datetime.tzinfo, not {type(tzinfo)}"
411+
)
408412
if not tz_aware:
409413
raise ValueError("cannot specify tzinfo without also setting tz_aware=True")
410414

411415
type_registry = type_registry or TypeRegistry()
412416

413417
if not isinstance(type_registry, TypeRegistry):
414-
raise TypeError("type_registry must be an instance of TypeRegistry")
418+
raise TypeError(
419+
f"type_registry must be an instance of TypeRegistry, not {type(type_registry)}"
420+
)
415421

416422
return tuple.__new__(
417423
cls,

bson/dbref.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ def __init__(
5656
.. seealso:: The MongoDB documentation on `dbrefs <https://dochub.mongodb.org/core/dbrefs>`_.
5757
"""
5858
if not isinstance(collection, str):
59-
raise TypeError("collection must be an instance of str")
59+
raise TypeError(f"collection must be an instance of str, not {type(collection)}")
6060
if database is not None and not isinstance(database, str):
61-
raise TypeError("database must be an instance of str")
61+
raise TypeError(f"database must be an instance of str, not {type(database)}")
6262

6363
self.__collection = collection
6464
self.__id = id

bson/decimal128.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ def from_bid(cls: Type[Decimal128], value: bytes) -> Decimal128:
277277
point in Binary Integer Decimal (BID) format).
278278
"""
279279
if not isinstance(value, bytes):
280-
raise TypeError("value must be an instance of bytes")
280+
raise TypeError(f"value must be an instance of bytes, not {type(value)}")
281281
if len(value) != 16:
282282
raise ValueError("value must be exactly 16 bytes")
283283
return cls((_UNPACK_64(value[8:])[0], _UNPACK_64(value[:8])[0])) # type: ignore

bson/timestamp.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ def __init__(self, time: Union[datetime.datetime, int], inc: int) -> None:
5858
time = time - offset
5959
time = int(calendar.timegm(time.timetuple()))
6060
if not isinstance(time, int):
61-
raise TypeError("time must be an instance of int")
61+
raise TypeError(f"time must be an instance of int, not {type(time)}")
6262
if not isinstance(inc, int):
63-
raise TypeError("inc must be an instance of int")
63+
raise TypeError(f"inc must be an instance of int, not {type(inc)}")
6464
if not 0 <= time < UPPERBOUND:
6565
raise ValueError("time must be contained in [0, 2**32)")
6666
if not 0 <= inc < UPPERBOUND:

gridfs/asynchronous/grid_file.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def __init__(self, database: AsyncDatabase, collection: str = "fs"):
100100
.. seealso:: The MongoDB documentation on `gridfs <https://dochub.mongodb.org/core/gridfs>`_.
101101
"""
102102
if not isinstance(database, AsyncDatabase):
103-
raise TypeError("database must be an instance of Database")
103+
raise TypeError(f"database must be an instance of Database, not {type(database)}")
104104

105105
database = _clear_entity_type_registry(database)
106106

@@ -503,7 +503,7 @@ def __init__(
503503
.. seealso:: The MongoDB documentation on `gridfs <https://dochub.mongodb.org/core/gridfs>`_.
504504
"""
505505
if not isinstance(db, AsyncDatabase):
506-
raise TypeError("database must be an instance of AsyncDatabase")
506+
raise TypeError(f"database must be an instance of AsyncDatabase, not {type(db)}")
507507

508508
db = _clear_entity_type_registry(db)
509509

@@ -1082,7 +1082,9 @@ def __init__(
10821082
:attr:`~pymongo.collection.AsyncCollection.write_concern`
10831083
"""
10841084
if not isinstance(root_collection, AsyncCollection):
1085-
raise TypeError("root_collection must be an instance of AsyncCollection")
1085+
raise TypeError(
1086+
f"root_collection must be an instance of AsyncCollection, not {type(root_collection)}"
1087+
)
10861088

10871089
if not root_collection.write_concern.acknowledged:
10881090
raise ConfigurationError("root_collection must use acknowledged write_concern")
@@ -1436,7 +1438,9 @@ def __init__(
14361438
from the server. Metadata is fetched when first needed.
14371439
"""
14381440
if not isinstance(root_collection, AsyncCollection):
1439-
raise TypeError("root_collection must be an instance of AsyncCollection")
1441+
raise TypeError(
1442+
f"root_collection must be an instance of AsyncCollection, not {type(root_collection)}"
1443+
)
14401444
_disallow_transactions(session)
14411445

14421446
root_collection = _clear_entity_type_registry(root_collection)

gridfs/synchronous/grid_file.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def __init__(self, database: Database, collection: str = "fs"):
100100
.. seealso:: The MongoDB documentation on `gridfs <https://dochub.mongodb.org/core/gridfs>`_.
101101
"""
102102
if not isinstance(database, Database):
103-
raise TypeError("database must be an instance of Database")
103+
raise TypeError(f"database must be an instance of Database, not {type(database)}")
104104

105105
database = _clear_entity_type_registry(database)
106106

@@ -501,7 +501,7 @@ def __init__(
501501
.. seealso:: The MongoDB documentation on `gridfs <https://dochub.mongodb.org/core/gridfs>`_.
502502
"""
503503
if not isinstance(db, Database):
504-
raise TypeError("database must be an instance of Database")
504+
raise TypeError(f"database must be an instance of Database, not {type(db)}")
505505

506506
db = _clear_entity_type_registry(db)
507507

@@ -1076,7 +1076,9 @@ def __init__(
10761076
:attr:`~pymongo.collection.Collection.write_concern`
10771077
"""
10781078
if not isinstance(root_collection, Collection):
1079-
raise TypeError("root_collection must be an instance of Collection")
1079+
raise TypeError(
1080+
f"root_collection must be an instance of Collection, not {type(root_collection)}"
1081+
)
10801082

10811083
if not root_collection.write_concern.acknowledged:
10821084
raise ConfigurationError("root_collection must use acknowledged write_concern")
@@ -1426,7 +1428,9 @@ def __init__(
14261428
from the server. Metadata is fetched when first needed.
14271429
"""
14281430
if not isinstance(root_collection, Collection):
1429-
raise TypeError("root_collection must be an instance of Collection")
1431+
raise TypeError(
1432+
f"root_collection must be an instance of Collection, not {type(root_collection)}"
1433+
)
14301434
_disallow_transactions(session)
14311435

14321436
root_collection = _clear_entity_type_registry(root_collection)

pymongo/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def timeout(seconds: Optional[float]) -> ContextManager[None]:
160160
.. versionadded:: 4.2
161161
"""
162162
if not isinstance(seconds, (int, float, type(None))):
163-
raise TypeError("timeout must be None, an int, or a float")
163+
raise TypeError(f"timeout must be None, an int, or a float, not {type(seconds)}")
164164
if seconds and seconds < 0:
165165
raise ValueError("timeout cannot be negative")
166166
if seconds is not None:

pymongo/_asyncio_lock.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def release(self) -> None:
160160
self._locked = False
161161
self._wake_up_first()
162162
else:
163-
raise RuntimeError("Lock is not acquired.")
163+
raise RuntimeError("Lock is not acquired")
164164

165165
def _wake_up_first(self) -> None:
166166
"""Ensure that the first waiter will wake up."""

pymongo/_azure_helpers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def _get_azure_response(
4646
try:
4747
data = json.loads(body)
4848
except Exception:
49-
raise ValueError("Azure IMDS response must be in JSON format.") from None
49+
raise ValueError("Azure IMDS response must be in JSON format") from None
5050

5151
for key in ["access_token", "expires_in"]:
5252
if not data.get(key):

pymongo/asynchronous/auth.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def _password_digest(username: str, password: str) -> str:
161161
if len(password) == 0:
162162
raise ValueError("password can't be empty")
163163
if not isinstance(username, str):
164-
raise TypeError("username must be an instance of str")
164+
raise TypeError(f"username must be an instance of str, not {type(username)}")
165165

166166
md5hash = hashlib.md5() # noqa: S324
167167
data = f"{username}:mongo:{password}"

pymongo/asynchronous/auth_oidc.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,9 @@ def _get_access_token(self) -> Optional[str]:
213213
)
214214
resp = cb.fetch(context)
215215
if not isinstance(resp, OIDCCallbackResult):
216-
raise ValueError("Callback result must be of type OIDCCallbackResult")
216+
raise ValueError(
217+
f"Callback result must be of type OIDCCallbackResult, not {type(resp)}"
218+
)
217219
self.refresh_token = resp.refresh_token
218220
self.access_token = resp.access_token
219221
self.token_gen_id += 1

pymongo/asynchronous/client_session.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,9 @@ def __init__(
310310
)
311311
if max_commit_time_ms is not None:
312312
if not isinstance(max_commit_time_ms, int):
313-
raise TypeError("max_commit_time_ms must be an integer or None")
313+
raise TypeError(
314+
f"max_commit_time_ms must be an integer or None, not {type(max_commit_time_ms)}"
315+
)
314316

315317
@property
316318
def read_concern(self) -> Optional[ReadConcern]:
@@ -902,7 +904,9 @@ def advance_cluster_time(self, cluster_time: Mapping[str, Any]) -> None:
902904
another `AsyncClientSession` instance.
903905
"""
904906
if not isinstance(cluster_time, _Mapping):
905-
raise TypeError("cluster_time must be a subclass of collections.Mapping")
907+
raise TypeError(
908+
f"cluster_time must be a subclass of collections.Mapping, not {type(cluster_time)}"
909+
)
906910
if not isinstance(cluster_time.get("clusterTime"), Timestamp):
907911
raise ValueError("Invalid cluster_time")
908912
self._advance_cluster_time(cluster_time)
@@ -923,7 +927,9 @@ def advance_operation_time(self, operation_time: Timestamp) -> None:
923927
another `AsyncClientSession` instance.
924928
"""
925929
if not isinstance(operation_time, Timestamp):
926-
raise TypeError("operation_time must be an instance of bson.timestamp.Timestamp")
930+
raise TypeError(
931+
f"operation_time must be an instance of bson.timestamp.Timestamp, not {type(operation_time)}"
932+
)
927933
self._advance_operation_time(operation_time)
928934

929935
def _process_response(self, reply: Mapping[str, Any]) -> None:

pymongo/asynchronous/collection.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def __init__(
228228
read_concern or database.read_concern,
229229
)
230230
if not isinstance(name, str):
231-
raise TypeError("name must be an instance of str")
231+
raise TypeError(f"name must be an instance of str, not {type(name)}")
232232
from pymongo.asynchronous.database import AsyncDatabase
233233

234234
if not isinstance(database, AsyncDatabase):
@@ -2475,7 +2475,7 @@ async def _drop_index(
24752475
name = helpers_shared._gen_index_name(index_or_name)
24762476

24772477
if not isinstance(name, str):
2478-
raise TypeError("index_or_name must be an instance of str or list")
2478+
raise TypeError(f"index_or_name must be an instance of str or list, not {type(name)}")
24792479

24802480
cmd = {"dropIndexes": self._name, "index": name}
24812481
cmd.update(kwargs)
@@ -3078,7 +3078,7 @@ async def rename(
30783078
30793079
"""
30803080
if not isinstance(new_name, str):
3081-
raise TypeError("new_name must be an instance of str")
3081+
raise TypeError(f"new_name must be an instance of str, not {type(new_name)}")
30823082

30833083
if not new_name or ".." in new_name:
30843084
raise InvalidName("collection names cannot be empty")
@@ -3148,7 +3148,7 @@ async def distinct(
31483148
31493149
"""
31503150
if not isinstance(key, str):
3151-
raise TypeError("key must be an instance of str")
3151+
raise TypeError(f"key must be an instance of str, not {type(key)}")
31523152
cmd = {"distinct": self._name, "key": key}
31533153
if filter is not None:
31543154
if "query" in kwargs:
@@ -3196,7 +3196,7 @@ async def _find_and_modify(
31963196
common.validate_is_mapping("filter", filter)
31973197
if not isinstance(return_document, bool):
31983198
raise ValueError(
3199-
"return_document must be ReturnDocument.BEFORE or ReturnDocument.AFTER"
3199+
f"return_document must be ReturnDocument.BEFORE or ReturnDocument.AFTER, not {type(return_document)}"
32003200
)
32013201
collation = validate_collation_or_none(kwargs.pop("collation", None))
32023202
cmd = {"findAndModify": self._name, "query": filter, "new": return_document}

pymongo/asynchronous/command_cursor.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ def __init__(
9494
self.batch_size(batch_size)
9595

9696
if not isinstance(max_await_time_ms, int) and max_await_time_ms is not None:
97-
raise TypeError("max_await_time_ms must be an integer or None")
97+
raise TypeError(
98+
f"max_await_time_ms must be an integer or None, not {type(max_await_time_ms)}"
99+
)
98100

99101
def __del__(self) -> None:
100102
self._die_no_lock()
@@ -115,7 +117,7 @@ def batch_size(self, batch_size: int) -> AsyncCommandCursor[_DocumentType]:
115117
:param batch_size: The size of each batch of results requested.
116118
"""
117119
if not isinstance(batch_size, int):
118-
raise TypeError("batch_size must be an integer")
120+
raise TypeError(f"batch_size must be an integer, not {type(batch_size)}")
119121
if batch_size < 0:
120122
raise ValueError("batch_size must be >= 0")
121123

0 commit comments

Comments
 (0)