+import collections
+import inspect
import io
import sys
py35 = sys.version_info >= (3, 5)
py36 = sys.version_info >= (3, 6)
+
+ArgSpec = collections.namedtuple(
+ "ArgSpec", ["args", "varargs", "keywords", "defaults"]
+)
+
+
+def inspect_getargspec(func):
+ """getargspec based on fully vendored getfullargspec from Python 3.3."""
+
+ if inspect.ismethod(func):
+ func = func.__func__
+ if not inspect.isfunction(func):
+ raise TypeError("{!r} is not a Python function".format(func))
+
+ co = func.__code__
+ if not inspect.iscode(co):
+ raise TypeError("{!r} is not a code object".format(co))
+
+ nargs = co.co_argcount
+ names = co.co_varnames
+ nkwargs = co.co_kwonlyargcount if py3k else 0
+ args = list(names[:nargs])
+
+ nargs += nkwargs
+ varargs = None
+ if co.co_flags & inspect.CO_VARARGS:
+ varargs = co.co_varnames[nargs]
+ nargs = nargs + 1
+ varkw = None
+ if co.co_flags & inspect.CO_VARKEYWORDS:
+ varkw = co.co_varnames[nargs]
+
+ return ArgSpec(args, varargs, varkw, func.__defaults__)
+
+
if py3k:
from io import StringIO
else:
else:
import collections as collections_abc # noqa
-if py3k:
- import collections
-
- ArgSpec = collections.namedtuple(
- "ArgSpec", ["args", "varargs", "keywords", "defaults"]
- )
-
- from inspect import getfullargspec as inspect_getfullargspec
-
- def inspect_getargspec(func):
- return ArgSpec(*inspect_getfullargspec(func)[0:4])
-
-
-else:
- from inspect import getargspec as inspect_getargspec # noqa
-
if py35:
from inspect import formatannotation
--- /dev/null
+.. change::
+ :tags: bug, py3k
+ :tickets: 563
+
+ Replaced the Python compatbility routines for ``getargspec()`` with a fully
+ vendored version based on ``getfullargspec()`` from Python 3.3.
+ Originally, Python was emitting deprecation warnings for this function in
+ Python 3.8 alphas. While this change was reverted, it was observed that
+ Python 3 implementations for ``getfullargspec()`` are an order of magnitude
+ slower as of the 3.4 series where it was rewritten against ``Signature``.
+ While Python plans to improve upon this situation, SQLAlchemy projects for
+ now are using a simple replacement to avoid any future issues.
+