In the following case, this is a working solution that I came up with for Python 2 and 3.
What is he doing?
99 None . PyArg_ParseTuple , - . , , , .
:
? , , . C :
static PyObject* example(PyObject *self, PyObject *args)
{
int myFirstParam;
if(!PyArg_ParseTuple(args, "i", &myFirstParam))
return NULL;
...
}
:
import inspect
import time
import re
import types
import sys
def get_parameter_count(func):
"""Count parameter of a function.
Supports Python functions (and built-in functions).
If a function takes *args, then -1 is returned
Example:
import os
arg = get_parameter_count(os.chdir)
print(arg) # Output: 1
-- For C devs:
In CPython, some built-in functions defined in C provide
no metadata about their arguments. That why we pass a
list with 999 None objects (randomly choosen) to it and
expect the underlying PyArg_ParseTuple fails with a
corresponding error message.
"""
if isinstance(func, types.BuiltinFunctionType):
try:
arg_test = 999
s = [None] * arg_test
func(*s)
except TypeError as e:
message = str(e)
found = re.match(
r"[\w]+\(\) takes ([0-9]{1,3}) positional argument[s]* but " +
str(arg_test) + " were given", message)
if found:
return int(found.group(1))
if "takes no arguments" in message:
return 0
elif "takes at most" in message:
found = re.match(
r"[\w]+\(\) takes at most ([0-9]{1,3}).+", message)
if found:
return int(found.group(1))
elif "takes exactly" in message:
found = re.match(
r"[\w]+\(\) takes exactly ([0-9]{1,3}|[\w]+).+", message)
if found:
return 1 if found.group(1) == "one" \
else int(found.group(1))
return -1
else:
try:
if (sys.version_info > (3, 0)):
argspec = inspect.getfullargspec(func)
else:
argspec = inspect.getargspec(func)
except:
raise TypeError("unable to determine parameter count")
return -1 if argspec.varargs else len(argspec.args)
def print_get_parameter_count(mod):
for x in dir(mod):
e = mod.__dict__.get(x)
if isinstance(e, types.BuiltinFunctionType):
print("{}.{} takes {} argument(s)".format(mod.__name__, e.__name__, get_parameter_count(e)))
import os
print_get_parameter_count(os)
:
os._exit takes 1 argument(s)
os.abort takes 0 argument(s)
os.access takes 2 argument(s)
os.chdir takes 1 argument(s)
os.chmod takes 2 argument(s)
os.close takes 1 argument(s)
...