def getargspec(func):
"""Variation of inspect.getargspec that works for more functions.
This function works for Cythonized, non-cpdef functions, which expose argspec information but
are not accepted by getargspec. It also works for Python 3 functions that use annotations, which
are simply ignored. However, keyword-only arguments are not supported.
"""
if inspect.ismethod(func):
func = func.__func__
# Cythonized functions have a .__code__, but don't pass inspect.isfunction()
try:
code = func.__code__
except AttributeError:
raise TypeError('{!r} is not a Python function'.format(func))
if hasattr(code, 'co_kwonlyargcount') and code.co_kwonlyargcount > 0:
raise ValueError('keyword-only arguments are not supported by getargspec()')
args, varargs, varkw = inspect.getargs(code)
return inspect.ArgSpec(args, varargs, varkw, func.__defaults__)
python类ArgSpec()的实例源码
def _get_argspec(func):
"""Returns an inspect.ArgSpec instance given a function object.
We prefer this implementation rather than the inspect module's getargspec
since the latter has a strict check that the passed function is an instance
of FunctionType. Cython functions do not pass this check, but they do implement
the `func_code` and `func_defaults` attributes that we need to produce an Argspec.
This implementation re-uses much of inspect.getargspec but removes the strict
check allowing interface failures to be raised as AttributeError.
See Also:
https://github.com/python/cpython/blob/2.7/Lib/inspect.py
"""
if inspect.ismethod(func):
func = func.im_func
args, varargs, varkw = inspect.getargs(func.func_code)
return inspect.ArgSpec(args, varargs, varkw, func.func_defaults)
def get_default_args(o):
"""??????????-???
"""
argspec = o
if not isinstance(o, inspect.ArgSpec):
argspec = inspect.getargspec(o)
if not argspec.defaults:
return {}
return dict(zip(argspec.args[-len(argspec.defaults):],
argspec.defaults))
# ??????
def test_getargspec():
empty = inspect.ArgSpec(args=[], varargs=None, keywords=None, defaults=None)
assert_eq(empty, qcore.inspection.getargspec(test_get_subclass_tree))
assert_eq(empty, qcore.inspection.getargspec(qcore.inspection.lazy_stack))
emptymethod = inspect.ArgSpec(args=['self'], varargs=None, keywords=None, defaults=None)
assert_eq(emptymethod, qcore.inspection.getargspec(X.myinstancemethod))
assert_eq(emptymethod, qcore.inspection.getargspec(X().myinstancemethod))
emptyclsmethod = inspect.ArgSpec(args=['cls'], varargs=None, keywords=None, defaults=None)
assert_eq(emptyclsmethod, qcore.inspection.getargspec(X.myclassmethod))
spec = inspect.ArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, keywords='f', defaults=('e',))
assert_eq(spec, qcore.inspection.getargspec(fun_with_args))
def test_getargspec_py3_only():
spec = inspect.ArgSpec(args=['a', 'b'], varargs='args', keywords=None, defaults=None)
assert_eq(spec, qcore.inspection.getargspec(fun_with_annotations))
with AssertRaises(ValueError):
qcore.inspection.getargspec(fun_with_kwonly_args)
def test_callable_args(func, args):
"""
Return True when this function can be called with the given arguments.
"""
assert isinstance(args, (list, tuple))
signature = getattr(inspect, 'signature', None)
if signature is not None:
# For Python 3, use inspect.signature.
try:
sig = _signatures_cache[func]
except KeyError:
sig = signature(func)
_signatures_cache[func] = sig
try:
sig.bind(*args)
except TypeError:
return False
else:
return True
else:
# For older Python versions, fall back to using getargspec.
spec = inspect.getargspec(func)
# Drop the 'self'
def drop_self(spec):
args, varargs, varkw, defaults = spec
if args[0:1] == ['self']:
args = args[1:]
return inspect.ArgSpec(args, varargs, varkw, defaults)
spec = drop_self(spec)
# When taking *args, always return True.
if spec.varargs is not None:
return True
# Test whether the given amount of args is between the min and max
# accepted argument counts.
return len(spec.args) - len(spec.defaults or []) <= len(args) <= len(spec.args)
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.__func__
parts = 0, ()
if type(func) is partial:
keywords = func.keywords
if keywords is None:
keywords = {}
parts = len(func.args), keywords.keys()
func = func.func
if not inspect.isfunction(func):
raise TypeError('%r is not a Python function' % func)
args, varargs, varkw = inspect.getargs(func.__code__)
func_defaults = func.__defaults__
if func_defaults is None:
func_defaults = []
else:
func_defaults = list(func_defaults)
if parts[0]:
args = args[parts[0]:]
if parts[1]:
for arg in parts[1]:
i = args.index(arg) - len(args)
del args[i]
try:
del func_defaults[i]
except IndexError:
pass
return inspect.ArgSpec(args, varargs, varkw, func_defaults)
def test_callable_args(func, args):
"""
Return True when this function can be called with the given arguments.
"""
assert isinstance(args, (list, tuple))
signature = getattr(inspect, 'signature', None)
if signature is not None:
# For Python 3, use inspect.signature.
try:
sig = _signatures_cache[func]
except KeyError:
sig = signature(func)
_signatures_cache[func] = sig
try:
sig.bind(*args)
except TypeError:
return False
else:
return True
else:
# For older Python versions, fall back to using getargspec.
spec = inspect.getargspec(func)
# Drop the 'self'
def drop_self(spec):
args, varargs, varkw, defaults = spec
if args[0:1] == ['self']:
args = args[1:]
return inspect.ArgSpec(args, varargs, varkw, defaults)
spec = drop_self(spec)
# When taking *args, always return True.
if spec.varargs is not None:
return True
# Test whether the given amount of args is between the min and max
# accepted argument counts.
return len(spec.args) - len(spec.defaults or []) <= len(args) <= len(spec.args)
def exec_params(call, *args, **kwargs):
"""Execute a callable with only the defined parameters
and not just *args, **kwargs.
:param callable call: The callable to exec with the given params as
defined by itself. call should have an inspect.ArgSpec attached
as an attribute _argspec
:returns anything:
:raises TypeError:
"""
arg_spec = getattr(call, '_argspec', None)
if arg_spec and not arg_spec.keywords:
kwargs = {key: value for key, value in kwargs.items()
if key in arg_spec.args}
return call(*args, **kwargs)
def _getargspec_init(method):
try:
return inspect.getargspec(method)
except TypeError:
if method is object.__init__:
return ArgSpec(['self'], None, None, None)
else:
return ArgSpec(['self'], 'args', 'kwargs', None)
def test_callable_args(func, args):
"""
Return True when this function can be called with the given arguments.
"""
assert isinstance(args, (list, tuple))
if _inspect_signature is not None:
# For Python 3, use inspect.signature.
try:
sig = _signatures_cache[func]
except KeyError:
sig = _inspect_signature(func)
_signatures_cache[func] = sig
try:
sig.bind(*args)
except TypeError:
return False
else:
return True
else:
# For older Python versions, fall back to using getargspec.
spec = inspect.getargspec(func)
# Drop the 'self'
def drop_self(spec):
args, varargs, varkw, defaults = spec
if args[0:1] == ['self']:
args = args[1:]
return inspect.ArgSpec(args, varargs, varkw, defaults)
spec = drop_self(spec)
# When taking *args, always return True.
if spec.varargs is not None:
return True
# Test whether the given amount of args is between the min and max
# accepted argument counts.
return len(spec.args) - len(spec.defaults or []) <= len(args) <= len(spec.args)
def getargspec_permissive(func):
"""
An `inspect.getargspec` with a relaxed sanity check to support Cython.
Motivation:
A Cython-compiled function is *not* an instance of Python's
types.FunctionType. That is the sanity check the standard Py2
library uses in `inspect.getargspec()`. So, an exception is raised
when calling `argh.dispatch_command(cythonCompiledFunc)`. However,
the CyFunctions do have perfectly usable `.func_code` and
`.func_defaults` which is all `inspect.getargspec` needs.
This function just copies `inspect.getargspec()` from the standard
library but relaxes the test to a more duck-typing one of having
both `.func_code` and `.func_defaults` attributes.
"""
if inspect.ismethod(func):
func = func.im_func
# Py2 Stdlib uses isfunction(func) which is too strict for Cython-compiled
# functions though such have perfectly usable func_code, func_defaults.
if not (hasattr(func, "func_code") and hasattr(func, "func_defaults")):
raise TypeError('{!r} missing func_code or func_defaults'.format(func))
args, varargs, varkw = inspect.getargs(func.func_code)
return inspect.ArgSpec(args, varargs, varkw, func.func_defaults)
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.__func__
parts = 0, ()
if type(func) is partial:
keywords = func.keywords
if keywords is None:
keywords = {}
parts = len(func.args), keywords.keys()
func = func.func
if not inspect.isfunction(func):
raise TypeError('%r is not a Python function' % func)
args, varargs, varkw = inspect.getargs(func.__code__)
func_defaults = func.__defaults__
if func_defaults is None:
func_defaults = []
else:
func_defaults = list(func_defaults)
if parts[0]:
args = args[parts[0]:]
if parts[1]:
for arg in parts[1]:
i = args.index(arg) - len(args)
del args[i]
try:
del func_defaults[i]
except IndexError:
pass
return inspect.ArgSpec(args, varargs, varkw, func_defaults)
def test_callable_args(func, args):
"""
Return True when this function can be called with the given arguments.
"""
assert isinstance(args, (list, tuple))
signature = getattr(inspect, 'signature', None)
if signature is not None:
# For Python 3, use inspect.signature.
try:
sig = _signatures_cache[func]
except KeyError:
sig = signature(func)
_signatures_cache[func] = sig
try:
sig.bind(*args)
except TypeError:
return False
else:
return True
else:
# For older Python versions, fall back to using getargspec.
spec = inspect.getargspec(func)
# Drop the 'self'
def drop_self(spec):
args, varargs, varkw, defaults = spec
if args[0:1] == ['self']:
args = args[1:]
return inspect.ArgSpec(args, varargs, varkw, defaults)
spec = drop_self(spec)
# When taking *args, always return True.
if spec.varargs is not None:
return True
# Test whether the given amount of args is between the min and max
# accepted argument counts.
return len(spec.args) - len(spec.defaults or []) <= len(args) <= len(spec.args)
def test_signature___enter__(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self'], varargs=None, keywords=None, defaults=None)
assert getargspec(ThermalPrinter.__enter__) == sig
def test_signature___init__(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'port', 'baudrate'], varargs=None,
keywords='kwargs', defaults=('/dev/ttyAMA0', 19200))
assert getargspec(ThermalPrinter.__init__) == sig
def test_signature___repr__(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self'], varargs=None, keywords=None, defaults=None)
assert getargspec(ThermalPrinter.__repr__) == sig
def test_signature__on_exit(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self'], varargs=None, keywords=None, defaults=None)
assert getargspec(ThermalPrinter._on_exit) == sig
def test_signature_barcode(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'data', 'barcode_type'], varargs=None,
keywords=None, defaults=None)
assert getargspec(ThermalPrinter.barcode) == sig
def test_signature_barcode_left_margin(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'margin'], varargs=None, keywords=None,
defaults=(0,))
assert getargspec(ThermalPrinter.barcode_left_margin) == sig
def test_signature_barcode_position(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'position'], varargs=None, keywords=None,
defaults=(BarCodePosition.HIDDEN,))
assert getargspec(ThermalPrinter.barcode_position) == sig
def test_signature_barcode_width(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'width'], varargs=None, keywords=None,
defaults=(3,))
assert getargspec(ThermalPrinter.barcode_width) == sig
def test_signature_bold(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'state'], varargs=None, keywords=None,
defaults=(False,))
assert getargspec(ThermalPrinter.bold) == sig
def test_signature_charset(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'charset'], varargs=None, keywords=None,
defaults=(CharSet.USA,))
assert getargspec(ThermalPrinter.charset) == sig
def test_signature_chinese(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'state'], varargs=None, keywords=None,
defaults=(False,))
assert getargspec(ThermalPrinter.chinese) == sig
def test_signature_chinese_format(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'fmt'], varargs=None, keywords=None,
defaults=(Chinese.GBK,))
assert getargspec(ThermalPrinter.chinese_format) == sig
def test_signature_codepage(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'codepage'], varargs=None, keywords=None,
defaults=(CodePage.CP437,))
assert getargspec(ThermalPrinter.codepage) == sig
def test_signature_double_height(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'state'], varargs=None, keywords=None,
defaults=(False,))
assert getargspec(ThermalPrinter.double_height) == sig
def test_signature_double_width(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'state'], varargs=None, keywords=None,
defaults=(False,))
assert getargspec(ThermalPrinter.double_width) == sig
def test_signature_flush(methods):
methods.remove(extract_stack(None, 2)[1][2].replace('test_signature_', ''))
sig = ArgSpec(args=['self', 'clear'], varargs=None, keywords=None,
defaults=(False,))
assert getargspec(ThermalPrinter.flush) == sig