21

I'm looking for a way to check the number of arguments that a given function takes in Python. The purpose is to achieve a more robust method of patching my classes for tests. So, I want to do something like this:

class MyClass (object):
    def my_function(self, arg1, arg2):
        result = ... # Something complicated
        return result

def patch(object, func_name, replacement_func):
    import new

    orig_func = getattr(object, func_name)
    replacement_func = new.instancemethod(replacement_func, 
                           object, object.__class__)

    # ...
    # Verify that orig_func and replacement_func have the 
    # same signature.  If not, raise an error.
    # ...

    setattr(object, func_name, replacement_func)

my_patched_object = MyClass()
patch(my_patched_object, "my_function", lambda self, arg1: "dummy result")
# The above line should raise an error!

Thanks.

mjumbewu
  • 1,104
  • 5
  • 14
  • 28
  • "patching my classes for tests"? Why aren't you using mock objects? http://python-mock.sourceforge.net/? – S.Lott Aug 20 '10 at 21:34
  • 2
    I'm new to using mocks. I "grew up" stubbing and patching. I'm getting practice and figuring out when to use which, but in the mean time, I still have projects to finish, and tests to write :). – mjumbewu Aug 21 '10 at 22:40

5 Answers5

19

inspect.getargspec is deprecated in Python 3. Consider something like:

import inspect
len(inspect.signature(foo_func).parameters)
Tahlor
  • 1,642
  • 1
  • 17
  • 21
16

You can use:

import inspect
len(inspect.getargspec(foo_func)[0])

This won't acknowledge variable-length parameters, like:

def foo(a, b, *args, **kwargs):
    pass
carl
  • 49,756
  • 17
  • 74
  • 82
5

You should use inspect.getargspec.

hwiechers
  • 14,583
  • 8
  • 53
  • 62
2

The inspect module allows you to examine a function's arguments. This has been asked a few times on Stack Overflow; try searching for some of those answers. For example:

Getting method parameter names in python

Community
  • 1
  • 1
Richard Fearn
  • 25,073
  • 7
  • 56
  • 55
0

Using the accepted answer you can even construct the entire type like this:

import inspect
from typing import Callable, Any, Type, List

def ftype(func: Callable[[Any], Any]) -> Type[Callable[[Any], Any]]:
    assert callable(func)
    ret = inspect.signature(func).return_annotation
    params = list(inspect.signature(func).parameters.values())
    param_types: List[Type[Any]] = [param.annotation for param in params]
    return Callable[[*param_types], ret]

In your code you can then check the type with the is keyword like this:

def equal_strings(text1: str, text2: str) -> bool:
    return text1 == text2

# ...

>>> my_fuction is Callable[[str, str], bool] 
True

>>> ftype(equal_strings)
typing.Callable[[str, str], bool]

However mypy doesn't like any of this, which is probably what lead you here in the first place.

I just thought this piece of magic was interesting

DeepBlue
  • 591
  • 9
  • 18