Skip to content

A quick way to verify function signatures in Python

License

Notifications You must be signed in to change notification settings

pomponchik/sigmatch

Repository files navigation

sigmatch: check function signatures

Downloads Downloads codecov Lines of code Hits-of-Code Test-Package Python versions PyPI version Checked with mypy Ruff

This small library allows you to quickly check whether any called object matches the signature you expect. This may be useful to you, for example, if you write libraries that work with callbacks.

Install it:

pip install sigmatch

Now to check the signatures of the callable objects, you need to create a SignatureMatcher object, which will "bake" a description of the parameters you expect. You can pass the following arguments to the constructor of the SignatureMatcher class (they are all strings):

  • "." - corresponds to an ordinary positional argument without a default value.
  • "some_argument_name" - corresponds to an argument with a default value. The content of the string is the name of the argument.
  • "*" - corresponds to packing multiple positional arguments without default values (*args).
  • "**" - corresponds to packing several named arguments with default values (**kwargs).

Note that the arguments can only go in this order, that is, you cannot specify the packing before the positional argument, otherwise you will get an IncorrectArgumentsOrderError. When you have prepared a SignatureMatcher object, you can apply it to function objects and get a response (True/False) whether their signatures match the expected ones. As an example, see what a function and a SignatureMatcher object for it mights look like:

from sigmatch import SignatureMatcher

def function(a, b, c=5, *d, **e):
    ...

matcher = SignatureMatcher('.', '.', 'c', '*', '**')
print(matcher.match(function))  # True

You can also pass all the expected arguments as a single line, separated by commas. Positional arguments do not have to be separated, they can be presented as a fused set of dots. See:

matcher = SignatureMatcher('.., c, *, **')

The match() method works with both regular and coroutine functions, as well as with lambdas, generators, classes, methods, and many other callable objects. By default, the match() method returns a boolean value, but you can ask the library to immediately raise an exception if the function does not have the signature you need:

matcher.match(function, raise_exception=True)

To catch this exception, import the SignatureMismatchError:

from sigmatch import SignatureMatcher, SignatureMismatchError

try:
    SignatureMatcher('.').match(lambda: None, raise_exception=True)
except SignatureMismatchError:
    print('Deal with it (⌐■_■)')  # It'll be printed.