Skip to content

Crash in check_mapping when items() is a generator #419

@Kakadus

Description

@Kakadus

Things to check first

  • I have searched the existing issues and didn't find my bug already reported there

  • I have checked that my bug is still present in the latest release

Typeguard version

4.1.5

Python version

3.10 / 3.11

What happened?

Using django's MultiValueDictcauses a crash because its .items() is a Generator. See django source.

check_mapping passes this generator to typeguard._config.CollectionCheckStrategy.iterate_samples:

samples = memo.config.collection_check_strategy.iterate_samples(
value.items()
)

This crashes it here, because generators have no len():

def iterate_samples(self, collection: Collection[T]) -> Collection[T]:
if self is CollectionCheckStrategy.FIRST_ITEM:
if len(collection):
return [next(iter(collection))]
else:
return ()
else:
return collection

How can we reproduce the bug?

Here is a minimal sample example. You can replace GeneratorDict with django's MultiValueDict to get the same result.

from typeguard import typechecked


@typechecked
def a(b: dict[str, list[str]]):
   ...


class GeneratorDict(dict):
    def items(self):
        for key in self:
            yield key, self[key]


a(GeneratorDict())

Running this code causes the following backtrace:

Traceback (most recent call last):
File "/.../test.py", line 15, in
a(MultiValueDict())
File "/.../test.py", line 5, in a
def a(b: dict[str, list[str]]) -> None:
File "/.../venv/lib/python3.11/site-packages/typeguard/_functions.py", line 138, in check_argument_types
check_type_internal(value, annotation, memo)
File "/.../venv/lib/python3.11/site-packages/typeguard/_checkers.py", line 759, in check_type_internal
checker(value, origin_type, args, memo)
File "/.../venv/lib/python3.11/site-packages/typeguard/_checkers.py", line 220, in check_mapping
samples = memo.config.collection_check_strategy.iterate_samples(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/.../venv/lib/python3.11/site-packages/typeguard/_config.py", line 54, in iterate_samples
if len(collection):
^^^^^^^^^^^^^^^
TypeError: object of type 'generator' has no len()

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions