Given a function object, how can I get its signature? For example, for:
def my_method(first, second, third='something'):
pass
I would like to get "my_method(first, second, third='something')"
.
Given a function object, how can I get its signature? For example, for:
def my_method(first, second, third='something'):
pass
I would like to get "my_method(first, second, third='something')"
.
import inspect
def foo(a, b, x='blah'):
pass
print(inspect.signature(foo))
# (a, b, x='blah')
Python 3.5+ recommends inspect.signature()
.
inspect.getargspec
on a module, not a function. –
Regatta getfullargspec
instead. (ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them
) –
Trimetallic Exception.__init__
but got an error TypeError: <slot wrapper '__init__' of 'exceptions.Exception' objects> is not a Python function
. Any help on how can we get the signature of functions of types. –
Ber getargspec
raises TypeError
if the input is not recognized as a Python function -- that is, a function implemented in Python. In CPython, Exception.__init__
is implemented in C, hence the TypeError
. You'll have to check the source code to understand the call signature. In Python3, getargspec
is implemented differently, and there inspect.getargspec(Exception.__init__)
returns a ArgSpec
instance. –
Riannon <function fread at 0x7f660eb6d158>
–
Helminthic Arguably the easiest way to find the signature for a function would be help(function)
:
>>> def function(arg1, arg2="foo", *args, **kwargs): pass
>>> help(function)
Help on function function in module __main__:
function(arg1, arg2='foo', *args, **kwargs)
Also, in Python 3 a method was added to the inspect
module called signature
, which is designed to represent the signature of a callable object and its return annotation:
>>> from inspect import signature
>>> def foo(a, *, b:int, **kwargs):
... pass
>>> sig = signature(foo)
>>> str(sig)
'(a, *, b:int, **kwargs)'
>>> str(sig.parameters['b'])
'b:int'
>>> sig.parameters['b'].annotation
<class 'int'>
inspect.signature
is also available for Python 2 via the funcsigs
backport project: pypi.python.org/pypi/funcsigs –
Porter inspect.signature
and typing.get_type_hints
? –
Sutphin inspect.signature(func).parameters.keys()
if you want a list of the parameters. –
Ortegal #! /usr/bin/env python
import inspect
from collections import namedtuple
DefaultArgSpec = namedtuple('DefaultArgSpec', 'has_default default_value')
def _get_default_arg(args, defaults, arg_index):
""" Method that determines if an argument has default value or not,
and if yes what is the default value for the argument
:param args: array of arguments, eg: ['first_arg', 'second_arg', 'third_arg']
:param defaults: array of default values, eg: (42, 'something')
:param arg_index: index of the argument in the argument array for which,
this function checks if a default value exists or not. And if default value
exists it would return the default value. Example argument: 1
:return: Tuple of whether there is a default or not, and if yes the default
value, eg: for index 2 i.e. for "second_arg" this function returns (True, 42)
"""
if not defaults:
return DefaultArgSpec(False, None)
args_with_no_defaults = len(args) - len(defaults)
if arg_index < args_with_no_defaults:
return DefaultArgSpec(False, None)
else:
value = defaults[arg_index - args_with_no_defaults]
if (type(value) is str):
value = '"%s"' % value
return DefaultArgSpec(True, value)
def get_method_sig(method):
""" Given a function, it returns a string that pretty much looks how the
function signature would be written in python.
:param method: a python method
:return: A string similar describing the pythong method signature.
eg: "my_method(first_argArg, second_arg=42, third_arg='something')"
"""
# The return value of ArgSpec is a bit weird, as the list of arguments and
# list of defaults are returned in separate array.
# eg: ArgSpec(args=['first_arg', 'second_arg', 'third_arg'],
# varargs=None, keywords=None, defaults=(42, 'something'))
argspec = inspect.getargspec(method)
arg_index=0
args = []
# Use the args and defaults array returned by argspec and find out
# which arguments has default
for arg in argspec.args:
default_arg = _get_default_arg(argspec.args, argspec.defaults, arg_index)
if default_arg.has_default:
args.append("%s=%s" % (arg, default_arg.default_value))
else:
args.append(arg)
arg_index += 1
return "%s(%s)" % (method.__name__, ", ".join(args))
if __name__ == '__main__':
def my_method(first_arg, second_arg=42, third_arg='something'):
pass
print get_method_sig(my_method)
# my_method(first_argArg, second_arg=42, third_arg="something")
def foo(a, *, b:int, **kwargs)
called with foo(4, b=3.3)
–
Konyn Try calling help
on an object to find out about it.
>>> foo = [1, 2, 3]
>>> help(foo.append)
Help on built-in function append:
append(...)
L.append(object) -- append object to end
Maybe a bit late to the party, but if you also want to keep the order of the arguments and their defaults, then you can use the Abstract Syntax Tree module (ast).
Here's a proof of concept (beware the code to sort the arguments and match them to their defaults can definitely be improved/made more clear):
import ast
for class_ in [c for c in module.body if isinstance(c, ast.ClassDef)]:
for method in [m for m in class_.body if isinstance(m, ast.FunctionDef)]:
args = []
if method.args.args:
[args.append([a.col_offset, a.id]) for a in method.args.args]
if method.args.defaults:
[args.append([a.col_offset, '=' + a.id]) for a in method.args.defaults]
sorted_args = sorted(args)
for i, p in enumerate(sorted_args):
if p[1].startswith('='):
sorted_args[i-1][1] += p[1]
sorted_args = [k[1] for k in sorted_args if not k[1].startswith('=')]
if method.args.vararg:
sorted_args.append('*' + method.args.vararg)
if method.args.kwarg:
sorted_args.append('**' + method.args.kwarg)
signature = '(' + ', '.join(sorted_args) + ')'
print method.name + signature
If all you're trying to do is print the function then use pydoc.
import pydoc
def foo(arg1, arg2, *args, **kwargs):
'''Some foo fn'''
pass
>>> print pydoc.render_doc(foo).splitlines()[2]
foo(arg1, arg2, *args, **kwargs)
If you're trying to actually analyze the function signature then use argspec of the inspection module. I had to do that when validating a user's hook script function into a general framework.
Use %pdef in the command line (IPython), it will print only the signature.
e.g. %pdef np.loadtxt
np.loadtxt(fname, dtype=<class 'float'>, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0, encoding='bytes')
Example code:
import inspect
from collections import OrderedDict
def get_signature(fn):
params = inspect.signature(fn).parameters
args = []
kwargs = OrderedDict()
for p in params.values():
if p.default is p.empty:
args.append(p.name)
else:
kwargs[p.name] = p.default
return args, kwargs
def test_sig():
def fn(a, b, c, d=3, e="abc"):
pass
assert get_signature(fn) == (
["a", "b", "c"], OrderedDict([("d", 3), ("e", "abc")])
)
Another late entry. My point isn't to, again, print the sig, or display help. It is to programmatically introspect function parameters (when I got to this question I was looking to check Django view functions by looking at functions with request
as a first parameter name).
The key is the Signature.parameters attribute, which is actually not that complicated (note that inspect._empty
is akin to None
in concept).
import inspect
from typing import Any, cast
def check_signature(func : "Callable") -> None:
funcname = func.__name__
try:
# class inspect.Signature(parameters=None, *, return_annotation=Signature.empty)
# see https://docs.python.org/3/library/inspect.html#inspect.Signature
sig = inspect.signature(func)
except (ValueError,) as e:
print(f"\n\n`{funcname}` has no signature")
return
print(f"\n\n`{funcname}{sig}` parameters:")
for position, (name,param) in enumerate(sig.parameters.items()):
# class inspect.Parameter(name, kind, *, default=Parameter.empty, annotation=Parameter.empty)
# see https://docs.python.org/3/library/inspect.html#inspect.Parameter
print(f" {position} {name:30.30} kind={param.kind.description.replace(' ','_')} / default={param.default if param.default is not inspect._empty else ''} / annotation={param.annotation if param.annotation is not inspect._empty else ''}")
class Foo:
def bar(self, zoom : int =2):
pass
for func in [Any, check_signature, print, Foo.bar, Foo().bar, isinstance, issubclass, cast]:
check_signature(func)
`Any(*args, **kwds)` parameters:
0 args kind=variadic_positional / default= / annotation=
1 kwds kind=variadic_keyword / default= / annotation=
`check_signature(func: 'Callable') -> None` parameters:
0 func kind=positional_or_keyword / default= / annotation=Callable
`print` has no signature
`bar(self, zoom: int = 2)` parameters:
0 self kind=positional_or_keyword / default= / annotation=
1 zoom kind=positional_or_keyword / default=2 / annotation=<class 'int'>
`bar(zoom: int = 2)` parameters:
0 zoom kind=positional_or_keyword / default=2 / annotation=<class 'int'>
`isinstance(obj, class_or_tuple, /)` parameters:
0 obj kind=positional-only / default= / annotation=
1 class_or_tuple kind=positional-only / default= / annotation=
`issubclass(cls, class_or_tuple, /)` parameters:
0 cls kind=positional-only / default= / annotation=
1 class_or_tuple kind=positional-only / default= / annotation=
`cast(typ, val)` parameters:
0 typ kind=positional_or_keyword / default= / annotation=
1 val kind=positional_or_keyword / default= / annotation=
In the OP's case you'd just check for param.default is not inspect._empty
. I'd opt for returning a dict[str, Any]
of those.
To make things a bit easier on myself, I went and added a pydantic wrapper to the whole thing:
class FuncSignature(BaseModel):
class Config:
arbitrary_types_allowed = True
funcname: str
sig : inspect.Signature
by_pos : dict[int,inspect.Parameter]
by_name: dict[str,inspect.Parameter]
undefined = inspect._empty
def get_signature(func : "Callable") -> FuncSignature:
"""return signature for a function"""
funcname = func.__name__
sig = inspect.signature(func)
by_name, by_pos = {},{}
for position, (name,param) in enumerate(sig.parameters.items()):
by_name[name] = param
by_pos[position] = param
return FuncSignature(funcname=funcname, sig=sig,by_name=by_name,by_pos=by_pos)
which allowed me to
def myview(request, rdbname):
pass
res = check_signature(myview)
print(res.by_pos[0].name == "request") # True
© 2022 - 2024 — McMap. All rights reserved.
help(yourmethod)
e.g.help(map)
– Brunn