ExportXMLWordPrintableJSON

    • Type: Bug
    • Resolution: Unresolved
    • Priority: Major - P3
    • None
    • Affects Version/s: None
    • Component/s: None
    • None
    • Python Drivers
    • None
    • None
    • None
    • None
    • None
    • None

      PyMongo 4.18.1 and 4.18.2 / CPython 3.14.6 / mypy 2.3.1. No MongoDB server is needed.

      Both native cursor constructors declare `let: Optional[bool] = None`. Their implementations call `validate_is_document_type("let", let)` for any non-None value. A document constructs successfully; a boolean raises TypeError at construction. Mypy reports two arg-type errors for document arguments and accepts the boolean arguments.

      The affected released source is [Cursor](https://github.com/mongodb/mongo-python-driver/blob/640dd2383a1a2fb1a10e5222d50ebc0b22a61bd7/pymongo/synchronous/cursor.py#L97) and [AsyncCursor](https://github.com/mongodb/mongo-python-driver/blob/640dd2383a1a2fb1a10e5222d50ebc0b22a61bd7/pymongo/asynchronous/cursor.py#L97). The same files are byte-identical in the selected 4.18.1 source archive.

      This report concerns constructor annotations and generated API documentation. Application code normally obtains cursors from `Collection.find` / `AsyncCollection.find`; those methods use variadic Any arguments, so their callers do not receive this particular static error. Native runtime forwarding of `let` remains usable. No claim of adapter incompatibility follows from this mismatch.

      Save this as `let_typing.py`:

      ```python
      from pymongo.asynchronous.collection import AsyncCollection
      from pymongo.asynchronous.cursor import AsyncCursor
      from pymongo.synchronous.collection import Collection
      from pymongo.synchronous.cursor import Cursor

      def sync_document(collection: Collection[dict[str, int]]) -> Cursor[dict[str, int]]:
          return Cursor(collection, let={"threshold": 1})

      def async_document(
          collection: AsyncCollection[dict[str, int]],
      ) -> AsyncCursor[dict[str, int]]:
          return AsyncCursor(collection, let={"threshold": 1})

      def sync_bool(collection: Collection[dict[str, int]]) -> Cursor[dict[str, int]]:
          return Cursor(collection, let=True)

      def async_bool(
          collection: AsyncCollection[dict[str, int]],
      ) -> AsyncCursor[dict[str, int]]:
          return AsyncCursor(collection, let=True)

      ```

      Save this adjacent file as `let_runtime.py`:

      ```python
      import asyncio

      import pymongo
      from pymongo import AsyncMongoClient, MongoClient

      from let_typing import async_bool, async_document, sync_bool, sync_document

      def check(collection, make_document, make_bool):
          cursor = make_document(collection)
          try:
              make_bool(collection)
          except TypeError as error:
              print(type(cursor)._name_, 'document accepted; bool rejected:', str(error))
          else:
              raise AssertionError('Boolean let unexpectedly accepted')
          return cursor

      def synchronous():
          with MongoClient[dict[str, int]](connect=False) as client:
              cursor = check(client['let_annotation']['records'], sync_document, sync_bool)
              cursor.close()

      async def asynchronous():
          async with AsyncMongoClient[dict[str, int]](connect=False) as client:
              cursor = check(client['let_annotation']['records'], async_document, async_bool)
              await cursor.close()

      if _name_ == '_main_':
          print('PyMongo', pymongo.version)
          synchronous()
          asyncio.run(asynchronous())

      ```

      Run in this project's existing environment (or an environment with the stated PyMongo and mypy versions):

      ```bash
      uv run --frozen --no-sync mypy --no-incremental --follow-imports=silent /tmp/cqc-adapter-study-20261004/let_typing.py
      uv run --frozen --no-sync python /tmp/cqc-adapter-study-20261004/let_runtime.py
      uv run --frozen --no-sync --with pymongo==4.18.1 mypy --no-incremental --follow-imports=silent /tmp/cqc-adapter-study-20261004/let_typing.py
      uv run --frozen --no-sync --with pymongo==4.18.1 python /tmp/cqc-adapter-study-20261004/let_runtime.py
      ```

      Both mypy commands exit 1 with exactly two `arg-type` errors: the document is incompatible with `bool | None`. Both runtime commands exit 0 and report document acceptance and boolean rejection for sync and async constructors. Clients use `connect=False`; cursors are never consumed, and all handles are closed.

      Expected: annotate `let` with the document forms accepted by native validation, matching the documented map-of-parameters behavior and preserving None.

            Assignee:
            Unassigned
            Reporter:
            Alessio M. (EXT)
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: