Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mypy can't infer parameter types for nested function calls. #8666

Open
MSK61 opened this issue Apr 13, 2020 · 4 comments
Open

Mypy can't infer parameter types for nested function calls. #8666

MSK61 opened this issue Apr 13, 2020 · 4 comments
Labels
topic-inference When to infer types or require explicit annotations

Comments

@MSK61
Copy link

MSK61 commented Apr 13, 2020

I'm trying to run mypy against this code

import itertools
itertools.starmap(
    lambda x, y: True, filter(lambda in_deg: in_deg[1],
                              [(1, 2)]))

I get the following error message:

error: Value of type "Iterable[Any]" is not indexable

It seems like mypy is starting type inference from the outside and inward, so it starts with itertools.starmap. The first callable is considered to take arguments resulting from the unpacking of an iterable: Iterable[Any], hence the second argument is considered Iterable[Iterable[Any]]. Since the second argument to itertools.starmap here is a function call to filter, it's assumed that filter must return Iterable[Iterable[Any]] to match the second argument expected by itertools.starmap. This coerces the second argument to filter to be seen as Iterable[Iterable[Any]], which in turn coerces the first argument to filter to be seen as a callable taking Iteable[Any]. This overlooks the explicitly obvious fact here that the second argument to filter is actually List[Tuple[int, int]](or at worst Iterable[Tuple[int, int]]) which will cause the first filter argument to be a callable that takes Tuple[int, int], effectively suppressing the spurious error.

One way to overcome this misinterpretation of the argument types is to explicitly cast the result of filter to be Iterable[Tuple[int, int]], which will rightfully suppress the error.

import itertools
import typing
itertools.starmap(
    lambda x, y: True, typing.cast(typing.Iterable[typing.Tuple[int, int]],
                                   filter(lambda in_deg: in_deg[1], [(1, 2)])))

mypy version: 0.761
python version: 3.7.6

@marcospb19
Copy link

marcospb19 commented Apr 23, 2020

I'm also experiencing a similar issue.

py.py:7: error: Value of type "Iterable[Any]" is not indexable
from functools import reduce
from typing import List

def _parseShortFlags(flags: List[str]):

	# Reduce the list of strings to a set with each unique character
	options: set = set(reduce(lambda a, b: a[1:] + b[1:], flags))

	print(sorted(options)) # Shows ['a', 'l', 'o']


_parseShortFlags(['-la', '-ll', '-oa'])

I guess mypy would need to guarantee that str[1:] + str[1:] -> str is always true.

@marcospb19
Copy link

When replacing options: set with options: Set[str] the error message changes to:

py.py:7: error: Value of type "Iterable[str]" is not indexable

@marcospb19
Copy link

And this gives no errors

from functools import reduce
from typing import List

def _parseShortFlags(flags: List[str]):

	# Reduce the list of strings to a string with each character
	options: str = reduce(lambda a, b: a[1:] + b[1:], flags)

	print(options)

_parseShortFlags(['-la', '-ll', '-oa'])

So mypy already knows that str[1:] + str[1:] -> str is always true.

@MSK61
Copy link
Author

MSK61 commented Apr 23, 2020

@marcospb19 Yes, I can say it's the same issue. With your code since using the set constructor dictates that it's taking an iterable(Iterable[Any], or Iterable[str] with Set[str]), it's inferring that the return value type of reduce is also an iterable. The first argument to reduce is a callable taking two values of the same type as that of reduce return value, here iterable, and producing a value of the same type, iterable. This means that both a and b are now considered to be iterables, and of course it isn't possible to just concatenate iterables with the addition operator +.

Your code can be altered in the same way as mine to get it to work. I did a try here https://mypy-play.net/?mypy=latest&python=3.8&gist=2ac5ba0daf467cbfaf3655f704e95ac4 and it worked fine.

There can be other workarounds of course like treating a and b as really Iterable[str] and using chain and islice from itertools to accomplish the same goal you're seeking. That's very far from an elegant way though, I would say.

@AlexWaygood AlexWaygood added the topic-inference When to infer types or require explicit annotations label Apr 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic-inference When to infer types or require explicit annotations
Projects
None yet
Development

No branches or pull requests

3 participants