5

Python doesn't check types at compile time because it can't, at least in some circumstances. But has anyone come up with a mechanism to do compile-time type checking based on extra annotations by the user? Something like pylint which uses extra guarantees by the author? I'm thinking of something like:

#guarantee(argument=int, return_type=int)
def f(x):
    return x + 3

#guarantee(argument=int, return_type=str)
def g(x):
    return "%d times" % x

y = f(6)

# works, z = "9 times"
z = g(y)
# error
a = f(z)

This checker would interpret the comments above each function, realize that f(x) is only supposed to accept int but z comes from g(x) so it's a str. Is there any product which does something similar to this?

1
  • 1
    My understanding is PyPy does stuff somewhat like this, but generally, people don't try to un-Python Python, they just use a strictly typed language instead. Also, there isn't really such a thing as "compile-time" in Python anyway. You can have static or dynamic code analysis, and conversion to pyc could be looked at as compilation, but at a fundamental level, arbitrary code can change anything about the system at runtime. Commented Apr 18, 2013 at 21:52

4 Answers 4

3

PEP 3107 was recently finalized (recently being sometime in the last year) which introduces annotations to variables, and functions. Unfortunately (as you can see from the number of the pep) this only applies to Python 3.x so any checker (or even code) you write to take advantage of this will be Python 3 only (which really isn't a bad thing).

You mention pylint so I assume you don't actually want the checks run at compile time, but instead checked after compilation. This would be an awesome tool to discuss over at the code-quality mailing list.

Sign up to request clarification or add additional context in comments.

Comments

2

Just use Pydantic or such tools & take a look to singledispatch for some cases:

from functools import singledispatch

@singledispatch
def process(value):
    raise NotImplementedError(f"Unsupported type: {type(value)}")

@process.register
def _(value: int):
    return f"Processing integer: {value}"

@process.register
def _(value: float):
    return f"Processing float: {value}"

@process.register
def _(value: str):
    return f"Processing string: {value}"

if __name__ == "__main__":
    print(process(10))        # out: Processing integer: 10
    print(process(10.5))      # out: Processing float: 10.5
    print(process("Hello"))   # out: Processing string: Hello

Comments

1

I think your missing keyword is decorator.

You can write your own decorators to do stuff like:

@check(bar=int)
foo(bar):
    pass

You can see an example implementation here. Although this is of course not valid for compile check since it's done on runtime.

1 Comment

Yes, i implemented such a thing once. One less than cool problem when implementing these decorators is making the IDE aware of what you've done, and so the code suggestions might get messed up - dunno if they did it in your example.EDIT: neah, that's the problem, if you're just passing *args and **kwargs, the code suggestion gets lost in translation :P
0

I'm not sure how this is a significant improvement over existing run-time mechanisms in Python. For example,

def f(x):
    if not isinstance(x, int):
        raise TypeError("Expected integer")
    return x + 3

def g(x):
    return "%d times" % x

# Works.
y = f(6)
z = g(y)
# Fails, raises TypeError.
a = f(z)

To put it another way, without annotating every function and every method of every object in Python, it would be difficult to statically determine exactly what the return type of either f or g is. I doubt any static checker along these lines would have any value.

Even though you added return type descriptors to your functions, is this really a guarantee? It looks rather like documentation that may fail to be updated along with the code, leading to even more insidious errors caused by incorrect assumptions later on.

3 Comments

And what about objects that behave as other types when treated as such (via __str__, __nonzero__, and the like)? Python is at a fundamental level duck-typed; any attempt to impose static typing either fails or breaks the language.
The tag static-analysis implies this is not meant to be imposed at run-time.
I know that's implicit in the question, but I think it is more important to provide correct, idiomatic answers when available.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.