Skip to content

Commit d763664

Browse files
Detailed error message for better debugging
1 parent 78724cd commit d763664

25 files changed

+127
-76
lines changed

bson/__init__.py

Lines changed: 1 addition & 1 deletion
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

Lines changed: 2 additions & 2 deletions
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(

bson/code.py

Lines changed: 2 additions & 2 deletions
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

Lines changed: 9 additions & 3 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 8 additions & 4 deletions
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)

pymongo/__init__.py

Lines changed: 1 addition & 1 deletion
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/asynchronous/auth.py

Lines changed: 1 addition & 1 deletion
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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 9 additions & 3 deletions
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

Lines changed: 5 additions & 5 deletions
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

Lines changed: 4 additions & 2 deletions
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

pymongo/asynchronous/cursor.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ def __init__(
146146
spec: Mapping[str, Any] = filter or {}
147147
validate_is_mapping("filter", spec)
148148
if not isinstance(skip, int):
149-
raise TypeError("skip must be an instance of int")
149+
raise TypeError(f"skip must be an instance of int, not {type(skip)}.")
150150
if not isinstance(limit, int):
151-
raise TypeError("limit must be an instance of int")
151+
raise TypeError(f"limit must be an instance of int, not {type(limit)}.")
152152
validate_boolean("no_cursor_timeout", no_cursor_timeout)
153153
if no_cursor_timeout and not self._explicit_session:
154154
warnings.warn(
@@ -171,7 +171,7 @@ def __init__(
171171
validate_boolean("allow_partial_results", allow_partial_results)
172172
validate_boolean("oplog_replay", oplog_replay)
173173
if not isinstance(batch_size, int):
174-
raise TypeError("batch_size must be an integer")
174+
raise TypeError(f"batch_size must be an integer, not {type(batch_size)}.")
175175
if batch_size < 0:
176176
raise ValueError("batch_size must be >= 0")
177177
# Only set if allow_disk_use is provided by the user, else None.
@@ -388,7 +388,7 @@ async def add_option(self, mask: int) -> AsyncCursor[_DocumentType]:
388388
cursor.add_option(2)
389389
"""
390390
if not isinstance(mask, int):
391-
raise TypeError("mask must be an int")
391+
raise TypeError(f"mask must be an int, not {type(mask)}.")
392392
self._check_okay_to_chain()
393393

394394
if mask & _QUERY_OPTIONS["exhaust"]:
@@ -408,7 +408,7 @@ def remove_option(self, mask: int) -> AsyncCursor[_DocumentType]:
408408
cursor.remove_option(2)
409409
"""
410410
if not isinstance(mask, int):
411-
raise TypeError("mask must be an int")
411+
raise TypeError(f"mask must be an int, not {type(mask)}.")
412412
self._check_okay_to_chain()
413413

414414
if mask & _QUERY_OPTIONS["exhaust"]:
@@ -432,7 +432,7 @@ def allow_disk_use(self, allow_disk_use: bool) -> AsyncCursor[_DocumentType]:
432432
.. versionadded:: 3.11
433433
"""
434434
if not isinstance(allow_disk_use, bool):
435-
raise TypeError("allow_disk_use must be a bool")
435+
raise TypeError(f"allow_disk_use must be a bool, not {type(allow_disk_use)}.")
436436
self._check_okay_to_chain()
437437

438438
self._allow_disk_use = allow_disk_use
@@ -451,7 +451,7 @@ def limit(self, limit: int) -> AsyncCursor[_DocumentType]:
451451
.. seealso:: The MongoDB documentation on `limit <https://dochub.mongodb.org/core/limit>`_.
452452
"""
453453
if not isinstance(limit, int):
454-
raise TypeError("limit must be an integer")
454+
raise TypeError(f"limit must be an integer, not {type(limit)}.")
455455
if self._exhaust:
456456
raise InvalidOperation("Can't use limit and exhaust together.")
457457
self._check_okay_to_chain()
@@ -479,7 +479,7 @@ def batch_size(self, batch_size: int) -> AsyncCursor[_DocumentType]:
479479
:param batch_size: The size of each batch of results requested.
480480
"""
481481
if not isinstance(batch_size, int):
482-
raise TypeError("batch_size must be an integer")
482+
raise TypeError(f"batch_size must be an integer, not {type(batch_size)}.")
483483
if batch_size < 0:
484484
raise ValueError("batch_size must be >= 0")
485485
self._check_okay_to_chain()
@@ -499,7 +499,7 @@ def skip(self, skip: int) -> AsyncCursor[_DocumentType]:
499499
:param skip: the number of results to skip
500500
"""
501501
if not isinstance(skip, int):
502-
raise TypeError("skip must be an integer")
502+
raise TypeError(f"skip must be an integer, not {type(skip)}.")
503503
if skip < 0:
504504
raise ValueError("skip must be >= 0")
505505
self._check_okay_to_chain()
@@ -520,7 +520,7 @@ def max_time_ms(self, max_time_ms: Optional[int]) -> AsyncCursor[_DocumentType]:
520520
:param max_time_ms: the time limit after which the operation is aborted
521521
"""
522522
if not isinstance(max_time_ms, int) and max_time_ms is not None:
523-
raise TypeError("max_time_ms must be an integer or None")
523+
raise TypeError(f"max_time_ms must be an integer or None, not {type(max_time_ms)}.")
524524
self._check_okay_to_chain()
525525

526526
self._max_time_ms = max_time_ms
@@ -543,7 +543,9 @@ def max_await_time_ms(self, max_await_time_ms: Optional[int]) -> AsyncCursor[_Do
543543
.. versionadded:: 3.2
544544
"""
545545
if not isinstance(max_await_time_ms, int) and max_await_time_ms is not None:
546-
raise TypeError("max_await_time_ms must be an integer or None")
546+
raise TypeError(
547+
f"max_await_time_ms must be an integer or None, not {type(max_await_time_ms)}."
548+
)
547549
self._check_okay_to_chain()
548550

549551
# Ignore max_await_time_ms if not tailable or await_data is False.
@@ -679,7 +681,7 @@ def max(self, spec: _Sort) -> AsyncCursor[_DocumentType]:
679681
.. versionadded:: 2.7
680682
"""
681683
if not isinstance(spec, (list, tuple)):
682-
raise TypeError("spec must be an instance of list or tuple")
684+
raise TypeError(f"spec must be an instance of list or tuple, not {type(spec)}.")
683685

684686
self._check_okay_to_chain()
685687
self._max = dict(spec)
@@ -701,7 +703,7 @@ def min(self, spec: _Sort) -> AsyncCursor[_DocumentType]:
701703
.. versionadded:: 2.7
702704
"""
703705
if not isinstance(spec, (list, tuple)):
704-
raise TypeError("spec must be an instance of list or tuple")
706+
raise TypeError(f"spec must be an instance of list or tuple, not {type(spec)}.")
705707

706708
self._check_okay_to_chain()
707709
self._min = dict(spec)

0 commit comments

Comments
 (0)