April 22, 2008

Method signature type checking decorator for Python 3000

I have just published a Python 3000 decorator for method signature type checking using function annotations. Here:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/572161

It is much cleaner that the similar decorator I have previously written for Python 2.x, the used Python 3000 function annotations make it better for the following reasons:

1. The signature-related piece of syntax is right there where it belongs - next to the parameter. Where I used to write
@takes(int, str)
@returns(bool)
def foo(i, s):
...
I now write
@typecheck
def foo(i: int, s: str) -> bool:
...
2. I don't have to add checking to all the parameters simply because there was no way to skip one. Where it was
class Foo(object):
@takes("Foo", str)
def foo(self, s):
...
it is now
class Foo:
@typecheck
def foo(self, s: str):
...
3. It plays nicely with the default values. This one has no equivalent in 2.x version, but it is nice to have:
@typecheck
def foo(x: int = 10): # 10 is also checked
...

@typecheck
def foo(*, k: optional(str) = None):
...
Other than that, it is just a nice usable piece of code, extensible too. Here is a few more examples:
@typecheck
def foo(x: with_attr("write", "flush")):
...

@typecheck
def foo(*, k: by_regex("^[0-9]+$")):
...

@typecheck
def swap_tuple(x: (int, float)) -> (float, int):
...

@typecheck
def swap_list(x: [int, float]) -> [float, int]:
...

@typecheck
def extract(k: list_of(by_regex("^[a-z]+$")),
d: dict_of(str, int)) -> list_of(int):
...

2 comments:

J. Pablo said...

Is there some way to force typechecking on all methods of a class or module?

Dmitry Dvoinikov said...

For a class you can modify its metaclass so that each new instance automatically has all of its methods instrumented with type checking.

For a module you can obtain a reference in sys.modules, walk through its methods and wrap each of them.

But both would be a bad idea. Aside from introducing unexpected side effects it would be a major performance killer. And the benefits of having everything checked upon every call are negligible. I'd say type checking is a great thing to add manually to only those methods that belong to some high level interface exposed to other application components.