Hacker News new | ask | show | jobs
by yegle 3140 days ago
(work for Google and uses pytype daily)

Pytype is similar to mypy that it can do type checking with proper annotations. In addition to use annotations, pytype can also do inference based on static analysis.

I don't have much experience with mypy but the last time I used it, it cannot infer from `return x == y` that the function returns a bool. Pytype can correctly infer many simple forms of function argument types and return type, and even some more complex form.

From reading the project, PyAnnotate completely rely on runtime profiling info to _help_ you get to the first round of annotations. We also have similar project that gathers types from runtime and help people to annotate the code. The type information gathered this way has its limitations (PyAnnotate project called this out as well, that you should only use it on legacy code but not on newly written code).

To give an example: if PyAnnotate observe a function below to accept a list of ints and returns an int, it may conclude that the type of this function is `Callable[[List[int]], int]`

``` def foo(xs): ret = 0 for x in xs: ret += x return ret ```

But it can actually work on any iterable (because of the for-in loop), and the item in `xs` is number (because the `__iadd__` call on integer 0). With static analysis, the correct inferred type might be `Callable[[Iterable[Union[int, float]]], Union[int, float]]`

2 comments

It's not possible to infer that the result `return x == y` is a bool, because python has rich comparisons (e.g. if x an y are numpy arrays you'll get an array of the same shape (after broadcasting) back). So either pytype uses additional information, or it's sometimes just wrong.
Technically true, since you'll need to check the return type of `__eq__()`. But the following code doesn't trigger any error using `mypy test.py --check-untyped-defs`.

  def foo():
    x = 1
    return x == 2


  def bar(x: bool):
    print(x)


  def baz(x: int):
    print(x)


  if __name__ == '__main__':
    bar(foo())
    baz(foo())
I think another good example of "observed types are not necessarily the intended types" is a Text parameter (unicode in python2, str in python3). The actual intent might be Iterable[Text].