1

I'm trying out mypy for some utils functions in my project, but I'm having trouble with this function that combines groupby and next.

This is the function code:

from itertools import groupby
from typing import Iterable, Any


def all_same(iterable: Iterable[Any]) -> bool:
    """Return True if all elements in iterable are equal
    >>> all_same([3, 3, 3])
    True
    >>> all_same([3, 3, 1])
    False
    >>> all_same([])
    True
    >>> all_same(['a', 'a'])
    True
    """
    g = groupby(iterable)
    return bool(next(g, True)) and not bool(next(g, False))

I keep getting this error about it not being able to infer the type argument 1 of "next":

$ mypy testing.py 
testing.py: note: In function "all_same":
testing.py:17: error: Cannot infer type argument 1 of "next"

I figure it means it can't infer the type of g here, right?

I'm having trouble to understand if this a problem in my type annotations or in type annotations for groupby.

For reference, this is the type annotation for groupby:

@overload
def groupby(iterable: Iterable[_T]) -> Iterator[Tuple[_T, Iterator[_T]]]: ...

So this means, "groupby takes an iterable of type T, and returns an iterator of tuples containing two items: (one item of type T, an iterator of the objects of type T)". Looks good to me, but then mypy should be able to infer the first argument of next as Iterator[Tuple[Any, Iterator[Any]]], right?

What am I missing?

max
  • 49,282
  • 56
  • 208
  • 355
Elias Dorneles
  • 22,556
  • 11
  • 85
  • 107

1 Answers1

1

The reason is due to the type annotation for next. The next function is defined to have the following type signature:

@overload
def next(i: Iterator[_T]) -> _T: ...
@overload
def next(i: Iterator[_T], default: _T) -> _T: ...

Basically, mypy expects the type of the default value to be the same as the contents of whatever you have within the iterator.

However, g is going to have the type Iterator[Tuple[Any, Iterator[Any]]], and Tuple[Any, Iterator[Any]] isn't the same type as bool.

Unfortunately, I'm not sure what the best way to repair your algorithm to typecheck is going to be, since the given type signature for next seems pretty reasonable to me + seems unlikely to be changed (though you could file an issue if you want to argue in favor for this change?). Perhaps the answers here might be useful?

Community
  • 1
  • 1
Michael0x2a
  • 58,192
  • 30
  • 175
  • 224
  • Thanks! It doesn't make sense to change my algorithm because of gradual typing though, the algorithm is sound. – Elias Dorneles Jun 29 '16 at 11:59
  • This is still weird to me, because the error is about failing to infer the type. – Elias Dorneles Jun 29 '16 at 12:01
  • 1
    @elias - Well, the thing is that according to mypy, your algorithm _isn't_ sound -- your code, for better or for worse, doesn't respect the type signature of `next`. The reason why mypy is complaining about failing to infer the type basically stems from this issue -- it's trying to reconcile the mismatch, and fails with that particular error message. But yeah, the fact that mypy is reporting _this_ particular error instead of a "your types don't match" error is most likely a bug. – Michael0x2a Jun 29 '16 at 15:12
  • well, I refuse to change the algorithm because of `next` type annotation, haha! I got it from Raymond Hettinger (Python core developer) and it's a pretty good one. :) Thanks for digging this to the end! – Elias Dorneles Jun 30 '16 at 20:25