Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Python
Branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
external
.gitignore
README.md
sample.json
signature.py
test_module.py

README.md

signature

Signature is a simple utility for doing signature checking of Python code.

This is useful for comparing method/function signatures of mocked unit test code against production code. If the signatures no longer match up, you can catch these errors early without having to resort to complicated integration tests.

(For more background on the problem, read my blog post here: http://www.sandywalsh.com/2011/08/pain-of-unit-tests-and-dynamically.html )

For example: Let's say we had a class that had a method with a specific signature:

class Foo(object):
    def replace_me(self, a, b=1, c="hello"):
        pass

and another object that could take the place of Foo (like a "driver" or "plugin" mechanism):

class Blah(object):
    def real_implementation(self, a, b=1, c="hello"):
        pass

Everything is fine so long as the signatures for replace_me and real_implementation are the same. Unit tests generally won't find these sorts of bugs, but Signature can.

Signature takes a dict-based configuration file. The primary key is called mates. Mates contains a list of lists of matching methods or functions with matching signatures.

The format of a method/function signature is:

<path/to/python/file.py|>path.to.namespace:<ClassName.>method_or_function_name

For example, lets say we need to ensure ./impl_1.py Impl1.method_a needs to match ./drivers/impl2.py Impl2.method_b your configuration dict would look like:

{"mates": [['./impl1.py|impl1:Impl1.method_a', './drivers/impl2.py|impl2:Impl2.method_b']]}

It's a list of lists because you likely have lots of different Signature mates you need to validate.

Other examples are:

./extensions/foobar.py|extensions.foobar:Foobar.method_name
my_module:MyClass.my_method
my_file.py|my_module:MyClass.my_method

You can call Signature from existing Python code with:

import signature
signature.check(dict(mates=[['__main__:Foo.replace_me', '__main__:Blah.real_implementation']]),
      raise_on_error=True)

or, you can put this configuration in a JSON file and run Signature from the cmdline:

 # python signature.py sample.json 
 -----Mismatch-----
 __main__:function_a ArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, keywords=None, defaults=None)
 __main__:function_b ArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, keywords=None, defaults=None)
 __main__:function_c ArgSpec(args=['a', 'b', 'c', 'e'], varargs=None, keywords=None, defaults=None)
 -----Mismatch-----
 __main__:Foo.method_a ArgSpec(args=['self', 'a', 'b', 'c', 'd'], varargs=None, keywords=None, defaults=None)
 __main__:Blah.method_a ArgSpec(args=['self', 'a', 'b', 'c', 'd'], varargs=None, keywords=None, defaults=None)
 __main__:Blah.method_b ArgSpec(args=['self', 'a', 'b', 'c', 'e'], varargs=None, keywords=None, defaults=None)
 -----Mismatch-----
 __main__:Foo.method_a ArgSpec(args=['self', 'a', 'b', 'c', 'd'], varargs=None, keywords=None, defaults=None)
 test_module:Blah.method_a ArgSpec(args=['self', 'a', 'b', 'c', 'd'], varargs=None, keywords=None, defaults=None)
 ./external/test_module.py|external.test_module:Blah.method_b ArgSpec(args=['self', 'a', 'b', 'c', 'e'], varargs=None, keywords=None, defaults=None)
Something went wrong with that request. Please try again.