summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/util/langhelpers.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-03-02 13:59:06 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2014-03-02 13:59:06 -0500
commitbf89ca2e10ff1c38e76f78e2d11d7858a50df547 (patch)
tree2ba46827eb96416e0b8db822b6495d4bc1e6f673 /lib/sqlalchemy/util/langhelpers.py
parent6750c39a4fc6be01502f3c9b1c6ba2d3f67a030e (diff)
downloadsqlalchemy-bf89ca2e10ff1c38e76f78e2d11d7858a50df547.tar.gz
- get util.get_callable_argspec() to be completely bulletproof for 2.6-3.4,
methods, classes, builtins, functools.partial(), everything known so far - use get_callable_argspec() within ColumnDefault._maybe_wrap_callable, re: #2979
Diffstat (limited to 'lib/sqlalchemy/util/langhelpers.py')
-rw-r--r--lib/sqlalchemy/util/langhelpers.py44
1 files changed, 33 insertions, 11 deletions
diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py
index 94ddb242c..0af8da381 100644
--- a/lib/sqlalchemy/util/langhelpers.py
+++ b/lib/sqlalchemy/util/langhelpers.py
@@ -260,20 +260,42 @@ def get_func_kwargs(func):
return compat.inspect_getargspec(func)[0]
-def get_callable_argspec(fn, no_self=False):
- if isinstance(fn, types.FunctionType):
- return compat.inspect_getargspec(fn)
- elif isinstance(fn, types.MethodType) and no_self:
- spec = compat.inspect_getargspec(fn.__func__)
- return compat.ArgSpec(spec.args[1:], spec.varargs, spec.keywords, spec.defaults)
+def get_callable_argspec(fn, no_self=False, _is_init=False):
+ """Return the argument signature for any callable.
+
+ All pure-Python callables are accepted, including
+ functions, methods, classes, objects with __call__;
+ builtins and other edge cases like functools.partial() objects
+ raise a TypeError.
+
+ """
+ if inspect.isbuiltin(fn):
+ raise TypeError("Can't inspect builtin: %s" % fn)
+ elif inspect.isfunction(fn):
+ if _is_init and no_self:
+ spec = compat.inspect_getargspec(fn)
+ return compat.ArgSpec(spec.args[1:], spec.varargs,
+ spec.keywords, spec.defaults)
+ else:
+ return compat.inspect_getargspec(fn)
+ elif inspect.ismethod(fn):
+ if no_self and (_is_init or fn.__self__):
+ spec = compat.inspect_getargspec(fn.__func__)
+ return compat.ArgSpec(spec.args[1:], spec.varargs,
+ spec.keywords, spec.defaults)
+ else:
+ return compat.inspect_getargspec(fn.__func__)
+ elif inspect.isclass(fn):
+ return get_callable_argspec(fn.__init__, no_self=no_self, _is_init=True)
elif hasattr(fn, '__func__'):
return compat.inspect_getargspec(fn.__func__)
- elif hasattr(fn, '__call__') and \
- not hasattr(fn.__call__, '__call__'): # functools.partial does this;
- # not much we can do
- return get_callable_argspec(fn.__call__)
+ elif hasattr(fn, '__call__'):
+ if inspect.ismethod(fn.__call__):
+ return get_callable_argspec(fn.__call__, no_self=no_self)
+ else:
+ raise TypeError("Can't inspect callable: %s" % fn)
else:
- raise ValueError("Can't inspect function: %s" % fn)
+ raise TypeError("Can't inspect callable: %s" % fn)
def format_argspec_plus(fn, grouped=True):
"""Returns a dictionary of formatted, introspected function arguments.