diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-01-14 12:22:40 -0500 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-01-15 10:01:53 -0500 |
| commit | 150ccf1716a41439c6ed9b06ac9cbf68fa5db1c4 (patch) | |
| tree | 4792d2f737b27b18d405855b4ab3c55555839827 /lib | |
| parent | b229a50c7786d8cbe65a2bf471b57a806f4259e3 (diff) | |
| download | sqlalchemy-150ccf1716a41439c6ed9b06ac9cbf68fa5db1c4.tar.gz | |
move to inspect_getfullargspec
Replace inspect_getargspec with inspect_getfullargspec
including a compatibility fallback for Py2k and use
getfullargspec fully.
Change-Id: I92bce0aafc37ce1a360b4f61b75f5892d0911c7e
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/sqlalchemy/event/attr.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/event/legacy.py | 7 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/collections.py | 10 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/events.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/testing/exclusions.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/util/__init__.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/util/compat.py | 29 | ||||
| -rw-r--r-- | lib/sqlalchemy/util/langhelpers.py | 137 |
8 files changed, 112 insertions, 85 deletions
diff --git a/lib/sqlalchemy/event/attr.py b/lib/sqlalchemy/event/attr.py index f0f65e670..9dfa89809 100644 --- a/lib/sqlalchemy/event/attr.py +++ b/lib/sqlalchemy/event/attr.py @@ -81,9 +81,9 @@ class _ClsLevelDispatch(RefCollection): def __init__(self, parent_dispatch_cls, fn): self.name = fn.__name__ - argspec = util.inspect_getargspec(fn) + argspec = util.inspect_getfullargspec(fn) self.arg_names = argspec.args[1:] - self.has_kw = bool(argspec.keywords) + self.has_kw = bool(argspec.varkw) self.legacy_signatures = list( reversed( sorted( diff --git a/lib/sqlalchemy/event/legacy.py b/lib/sqlalchemy/event/legacy.py index 049df81aa..93b6a1a9a 100644 --- a/lib/sqlalchemy/event/legacy.py +++ b/lib/sqlalchemy/event/legacy.py @@ -32,7 +32,7 @@ def _wrap_fn_for_legacy(dispatch_collection, fn, argspec): has_kw = False if len(argnames) == len(argspec.args) and has_kw is bool( - argspec.keywords + argspec.varkw ): if conv: @@ -140,7 +140,7 @@ def _version_signature_changes(parent_dispatch_cls, dispatch_collection): " The :class:`.%(clsname)s.%(event_name)s` event now accepts the \n" " arguments ``%(named_event_arguments)s%(has_kw_arguments)s``.\n" " Support for listener functions which accept the previous \n" - " argument signature(s) listed above as \"deprecated\" will be \n" + ' argument signature(s) listed above as "deprecated" will be \n' " removed in a future release." % { "since": since, @@ -171,6 +171,7 @@ def _augment_fn_docs(dispatch_collection, parent_dispatch_cls, fn): ) text += _version_signature_changes( - parent_dispatch_cls, dispatch_collection) + parent_dispatch_cls, dispatch_collection + ) return util.inject_docstring_text(fn.__doc__, text, 1) diff --git a/lib/sqlalchemy/orm/collections.py b/lib/sqlalchemy/orm/collections.py index 427296ca2..1e561369f 100644 --- a/lib/sqlalchemy/orm/collections.py +++ b/lib/sqlalchemy/orm/collections.py @@ -106,7 +106,7 @@ through the adapter, allowing for some very sophisticated behavior. import operator import weakref -from sqlalchemy.util.compat import inspect_getargspec +from sqlalchemy.util.compat import inspect_getfullargspec from . import base from .. import exc as sa_exc from .. import util @@ -433,7 +433,7 @@ class collection(object): "The :meth:`.collection.linker` handler is deprecated and will " "be removed in a future release. Please refer to the " ":meth:`.AttributeEvents.init_collection` " - "and :meth:`.AttributeEvents.dispose_collection` event handlers. " + "and :meth:`.AttributeEvents.dispose_collection` event handlers. ", ) def linker(fn): """Tag the method as a "linked to attribute" event handler. @@ -463,7 +463,7 @@ class collection(object): "The :meth:`.collection.converter` method is deprecated and will " "be removed in a future release. Please refer to the " ":class:`.AttributeEvents.bulk_replace` listener interface in " - "conjunction with the :func:`.event.listen` function." + "conjunction with the :func:`.event.listen` function.", ) def converter(fn): """Tag the method as the collection converter. @@ -1008,7 +1008,9 @@ def _instrument_membership_mutator(method, before, argument, after): adapter.""" # This isn't smart enough to handle @adds(1) for 'def fn(self, (a, b))' if before: - fn_args = list(util.flatten_iterator(inspect_getargspec(method)[0])) + fn_args = list( + util.flatten_iterator(inspect_getfullargspec(method)[0]) + ) if isinstance(argument, int): pos_arg = argument named_arg = len(fn_args) > argument and fn_args[argument] or None diff --git a/lib/sqlalchemy/orm/events.py b/lib/sqlalchemy/orm/events.py index d9e5f87a3..e10744fc1 100644 --- a/lib/sqlalchemy/orm/events.py +++ b/lib/sqlalchemy/orm/events.py @@ -22,7 +22,7 @@ from .session import sessionmaker from .. import event from .. import exc from .. import util -from ..util.compat import inspect_getargspec +from ..util.compat import inspect_getfullargspec class InstrumentationEvents(event.Events): @@ -639,7 +639,7 @@ class MapperEvents(event.Events): meth = getattr(cls, identifier) try: target_index = ( - inspect_getargspec(meth)[0].index("target") - 1 + inspect_getfullargspec(meth)[0].index("target") - 1 ) except ValueError: target_index = None diff --git a/lib/sqlalchemy/testing/exclusions.py b/lib/sqlalchemy/testing/exclusions.py index bfb239dad..d205354cf 100644 --- a/lib/sqlalchemy/testing/exclusions.py +++ b/lib/sqlalchemy/testing/exclusions.py @@ -13,7 +13,7 @@ import re from . import config from .. import util from ..util import decorator -from ..util.compat import inspect_getargspec +from ..util.compat import inspect_getfullargspec def skip_if(predicate, reason=None): @@ -303,7 +303,7 @@ class SpecPredicate(Predicate): class LambdaPredicate(Predicate): def __init__(self, lambda_, description=None, args=None, kw=None): - spec = inspect_getargspec(lambda_) + spec = inspect_getfullargspec(lambda_) if not spec[0]: self.lambda_ = lambda db: lambda_() else: diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py index 7e9bedd83..f812671d3 100644 --- a/lib/sqlalchemy/util/__init__.py +++ b/lib/sqlalchemy/util/__init__.py @@ -53,7 +53,7 @@ from .compat import cmp # noqa from .compat import cpython # noqa from .compat import decode_backslashreplace # noqa from .compat import dottedgetter # noqa -from .compat import inspect_getargspec # noqa +from .compat import inspect_getfullargspec # noqa from .compat import int_types # noqa from .compat import iterbytes # noqa from .compat import itertools_filter # noqa diff --git a/lib/sqlalchemy/util/compat.py b/lib/sqlalchemy/util/compat.py index 05e31f7bd..abd36d9b0 100644 --- a/lib/sqlalchemy/util/compat.py +++ b/lib/sqlalchemy/util/compat.py @@ -31,8 +31,17 @@ dottedgetter = operator.attrgetter namedtuple = collections.namedtuple next = next # noqa -ArgSpec = collections.namedtuple( - "ArgSpec", ["args", "varargs", "keywords", "defaults"] +FullArgSpec = collections.namedtuple( + "FullArgSpec", + [ + "args", + "varargs", + "varkw", + "defaults", + "kwonlyargs", + "kwonlydefaults", + "annotations", + ], ) try: @@ -98,9 +107,6 @@ if py3k: def cmp(a, b): return (a > b) - (a < b) - def inspect_getargspec(func): - return ArgSpec(*inspect_getfullargspec(func)[0:4]) - def reraise(tp, value, tb=None, cause=None): if cause is not None: assert cause is not value, "Same cause emitted" @@ -130,7 +136,7 @@ else: from StringIO import StringIO # noqa from cStringIO import StringIO as byte_buffer # noqa - from inspect import getargspec as inspect_getfullargspec # noqa + from inspect import getargspec as _getargspec from itertools import izip_longest as zip_longest # noqa from urllib import quote # noqa from urllib import quote_plus # noqa @@ -149,7 +155,8 @@ else: text_type = unicode # noqa int_types = int, long # noqa - inspect_getargspec = inspect_getfullargspec + def inspect_getfullargspec(func): + return FullArgSpec(*_getargspec(func)[0:4] + ([], None, {})) callable = callable # noqa cmp = cmp # noqa @@ -286,6 +293,14 @@ if py35: return result +elif py2k: + from inspect import formatargspec as _inspect_formatargspec + + def inspect_formatargspec(*spec, **kw): + # convert for a potential FullArgSpec from compat.getfullargspec() + return _inspect_formatargspec(*spec[0:4], **kw) # noqa + + else: from inspect import formatargspec as inspect_formatargspec # noqa diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index 27c4be0ec..56e1304e3 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -121,8 +121,9 @@ def decorator(target): """A signature-matching decorator factory.""" def decorate(fn): - if not inspect.isfunction(fn): + if not inspect.isfunction(fn) and not inspect.ismethod(fn): raise Exception("not a decoratable function") + spec = compat.inspect_getfullargspec(fn) names = tuple(spec[0]) + spec[1:3] + (fn.__name__,) targ_name, fn_name = _unique_symbols(names, "target", "fn") @@ -242,6 +243,26 @@ class PluginLoader(object): self.impls[name] = load +def _inspect_func_args(fn): + try: + co_varkeywords = inspect.CO_VARKEYWORDS + except AttributeError: + # https://docs.python.org/3/library/inspect.html + # The flags are specific to CPython, and may not be defined in other + # Python implementations. Furthermore, the flags are an implementation + # detail, and can be removed or deprecated in future Python releases. + spec = compat.inspect_getfullargspec(fn) + return spec[0], bool(spec[2]) + else: + # use fn.__code__ plus flags to reduce method call overhead + co = fn.__code__ + nargs = co.co_argcount + return ( + list(co.co_varnames[:nargs]), + bool(co.co_flags & inspect.CO_VARKEYWORDS), + ) + + def get_cls_kwargs(cls, _set=None): r"""Return the full set of inherited kwargs for the given `cls`. @@ -250,7 +271,10 @@ def get_cls_kwargs(cls, _set=None): to pass along unrecognized keywords to its base classes, and the collection process is repeated recursively on each of the bases. - Uses a subset of inspect.getargspec() to cut down on method overhead. + Uses a subset of inspect.getfullargspec() to cut down on method overhead, + as this is used within the Core typing system to create copies of type + objects which is a performance-sensitive operation. + No anonymous tuple arguments please ! """ @@ -267,7 +291,7 @@ def get_cls_kwargs(cls, _set=None): ) if has_init: - names, has_kw = inspect_func_args(ctr) + names, has_kw = _inspect_func_args(ctr) _set.update(names) if not has_kw and not toplevel: @@ -282,26 +306,6 @@ def get_cls_kwargs(cls, _set=None): return _set -try: - # TODO: who doesn't have this constant? - from inspect import CO_VARKEYWORDS - - def inspect_func_args(fn): - co = fn.__code__ - nargs = co.co_argcount - names = co.co_varnames - args = list(names[:nargs]) - has_kw = bool(co.co_flags & CO_VARKEYWORDS) - return args, has_kw - - -except ImportError: - - def inspect_func_args(fn): - names, _, has_kw, _ = compat.inspect_getargspec(fn) - return names, bool(has_kw) - - def get_func_kwargs(func): """Return the set of legal kwargs for the given `func`. @@ -310,7 +314,7 @@ def get_func_kwargs(func): """ - return compat.inspect_getargspec(func)[0] + return compat.inspect_getfullargspec(func)[0] def get_callable_argspec(fn, no_self=False, _is_init=False): @@ -326,26 +330,38 @@ def get_callable_argspec(fn, no_self=False, _is_init=False): 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 + spec = compat.inspect_getfullargspec(fn) + return compat.FullArgSpec( + spec.args[1:], + spec.varargs, + spec.varkw, + spec.defaults, + spec.kwonlyargs, + spec.kwonlydefaults, + spec.annotations, ) else: - return compat.inspect_getargspec(fn) + return compat.inspect_getfullargspec(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 + spec = compat.inspect_getfullargspec(fn.__func__) + return compat.FullArgSpec( + spec.args[1:], + spec.varargs, + spec.varkw, + spec.defaults, + spec.kwonlyargs, + spec.kwonlydefaults, + spec.annotations, ) else: - return compat.inspect_getargspec(fn.__func__) + return compat.inspect_getfullargspec(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__) + return compat.inspect_getfullargspec(fn.__func__) elif hasattr(fn, "__call__"): if inspect.ismethod(fn.__call__): return get_callable_argspec(fn.__call__, no_self=no_self) @@ -390,8 +406,8 @@ def format_argspec_plus(fn, grouped=True): if compat.callable(fn): spec = compat.inspect_getfullargspec(fn) else: - # we accept an existing argspec... spec = fn + args = compat.inspect_formatargspec(*spec) if spec[0]: self_arg = spec[0][0] @@ -400,22 +416,15 @@ def format_argspec_plus(fn, grouped=True): else: self_arg = None - if compat.py3k: - apply_pos = compat.inspect_formatargspec( - spec[0], spec[1], spec[2], None, spec[4] - ) - num_defaults = 0 - if spec[3]: - num_defaults += len(spec[3]) - if spec[4]: - num_defaults += len(spec[4]) - name_args = spec[0] + spec[4] - else: - apply_pos = compat.inspect_formatargspec(spec[0], spec[1], spec[2]) - num_defaults = 0 - if spec[3]: - num_defaults += len(spec[3]) - name_args = spec[0] + apply_pos = compat.inspect_formatargspec( + spec[0], spec[1], spec[2], None, spec[4] + ) + num_defaults = 0 + if spec[3]: + num_defaults += len(spec[3]) + if spec[4]: + num_defaults += len(spec[4]) + name_args = spec[0] + spec[4] if num_defaults: defaulted_vals = name_args[0 - num_defaults :] @@ -479,7 +488,7 @@ def getargspec_init(method): """ try: - return compat.inspect_getargspec(method) + return compat.inspect_getfullargspec(method) except TypeError: if method is object.__init__: return (["self"], None, None, None) @@ -516,30 +525,30 @@ def generic_repr(obj, additional_kw=(), to_inspect=None, omit_kwarg=()): vargs = None for i, insp in enumerate(to_inspect): try: - (_args, _vargs, vkw, defaults) = compat.inspect_getargspec( - insp.__init__ - ) + spec = compat.inspect_getfullargspec(insp.__init__) except TypeError: continue else: - default_len = defaults and len(defaults) or 0 + default_len = spec.defaults and len(spec.defaults) or 0 if i == 0: - if _vargs: - vargs = _vargs + if spec.varargs: + vargs = spec.varargs if default_len: - pos_args.extend(_args[1:-default_len]) + pos_args.extend(spec.args[1:-default_len]) else: - pos_args.extend(_args[1:]) + pos_args.extend(spec.args[1:]) else: kw_args.update( - [(arg, missing) for arg in _args[1:-default_len]] + [(arg, missing) for arg in spec.args[1:-default_len]] ) if default_len: kw_args.update( [ (arg, default) - for arg, default in zip(_args[-default_len:], defaults) + for arg, default in zip( + spec.args[-default_len:], spec.defaults + ) ] ) output = [] @@ -710,7 +719,7 @@ def monkeypatch_proxied_specials( except AttributeError: continue try: - spec = compat.inspect_getargspec(fn) + spec = compat.inspect_getfullargspec(fn) fn_args = compat.inspect_formatargspec(spec[0]) d_args = compat.inspect_formatargspec(spec[0][1:]) except TypeError: @@ -1478,8 +1487,8 @@ class EnsureKWArgType(type): m = re.match(fn_reg, key) if m: fn = clsdict[key] - spec = compat.inspect_getargspec(fn) - if not spec.keywords: + spec = compat.inspect_getfullargspec(fn) + if not spec.varkw: clsdict[key] = wrapped = cls._wrap_w_kw(fn) setattr(cls, key, wrapped) super(EnsureKWArgType, cls).__init__(clsname, bases, clsdict) |
