agronholm / typeguard

Run-time type checker for Python
Other
1.5k stars 112 forks source link

Curious behavior with forward reference recursive types #449

Closed drakes00 closed 5 months ago

drakes00 commented 5 months ago

Things to check first

Typeguard version

4.2.1

Python version

3.11

What happened?

Hi and thanks a lot for the typeguard package!

I have a (to my understanding) similar issue as in #62 or maybe I'm just mistaking the entire recursive type idea (and I'd be very grateful to learn more about it).

Here is the following script:

from typing import Union, List
from typeguard import typechecked

RecType = Union[int, List['RecType']]

@typechecked
def fun(tmp: RecType):
    return tmp

fun(1)  # Call 1
fun([1, [2]])  # Call 2
fun([1, 'a'])  # Call 3
fun([1, ['a']])  # Call 4
fun('a')  # Call 5

I tried to setup RecType top be any combination of list of int. I would expect calls 3, 4, and 5 to raise but I'm only getting a typeguard.TypeCheckError for call 5:

Traceback (most recent call last):
  File "/tmp/tmppy/tmp.py", line 16, in <module>
    fun('a')
  File "/tmp/tmppy/tmp.py", line 8, in fun
    def fun(tmp: RecType):
  File "/home/dev/.cache/pypoetry/virtualenvs/tmppy-0F3G48nR-py3.11/lib/python3.11/site-packages/typeguard/_functions.py", line 136, in check_argument_types
    check_type_internal(value, annotation, memo)
  File "/home/dev/.cache/pypoetry/virtualenvs/tmppy-0F3G48nR-py3.11/lib/python3.11/site-packages/typeguard/_checkers.py", line 779, in check_type_internal
    checker(value, origin_type, args, memo)
  File "/home/dev/.cache/pypoetry/virtualenvs/tmppy-0F3G48nR-py3.11/lib/python3.11/site-packages/typeguard/_checkers.py", line 428, in check_union
    raise TypeCheckError(f"did not match any element in the union:\n{formatted_errors}")
typeguard.TypeCheckError: argument "tmp" (str) did not match any element in the union:
  int: is not an instance of int
  List[RecType]: is not a list

I got the type definition idea from https://stackoverflow.com/questions/53845024/defining-a-recursive-type-hint-in-python Am I understanding the forward type wrong? Thanks a lot in advance,

How can we reproduce the bug?

See above

drakes00 commented 5 months ago

Ok narrowing it and probably my fault here. I traced the behavior with pdb and saw that memo.config.collection_check_strategy.iterate_samples was defaulting to CollectionCheckStrategy.FIRST_ITEM.

If I use @typechecked(collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS), the behavior is as expected.