summaryrefslogtreecommitdiff
path: root/Lib/inspect.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/inspect.py')
-rw-r--r--Lib/inspect.py101
1 files changed, 49 insertions, 52 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py
index ac127cbd72..70c5ef7bb6 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -707,10 +707,13 @@ def getsourcefile(object):
if os.path.exists(filename):
return filename
# only return a non-existent filename if the module has a PEP 302 loader
- if getattr(getmodule(object, filename), '__loader__', None) is not None:
+ module = getmodule(object, filename)
+ if getattr(module, '__loader__', None) is not None:
+ return filename
+ elif getattr(getattr(module, "__spec__", None), "loader", None) is not None:
return filename
# or it is in the linecache
- if filename in linecache.cache:
+ elif filename in linecache.cache:
return filename
def getabsfile(object, _filename=None):
@@ -865,7 +868,12 @@ def findsource(object):
lnum = object.co_firstlineno - 1
pat = re.compile(r'^(\s*def\s)|(\s*async\s+def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
while lnum > 0:
- if pat.match(lines[lnum]): break
+ try:
+ line = lines[lnum]
+ except IndexError:
+ raise OSError('lineno is out of bounds')
+ if pat.match(line):
+ break
lnum = lnum - 1
return lines, lnum
raise OSError('could not find code object')
@@ -927,6 +935,7 @@ class BlockFinder:
self.indecorator = False
self.decoratorhasargs = False
self.last = 1
+ self.body_col0 = None
def tokeneater(self, type, token, srowcol, erowcol, line):
if not self.started and not self.indecorator:
@@ -958,6 +967,8 @@ class BlockFinder:
elif self.passline:
pass
elif type == tokenize.INDENT:
+ if self.body_col0 is None and self.started:
+ self.body_col0 = erowcol[1]
self.indent = self.indent + 1
self.passline = True
elif type == tokenize.DEDENT:
@@ -967,6 +978,10 @@ class BlockFinder:
# not e.g. for "if: else:" or "try: finally:" blocks)
if self.indent <= 0:
raise EndOfBlock
+ elif type == tokenize.COMMENT:
+ if self.body_col0 is not None and srowcol[1] >= self.body_col0:
+ # Include comments if indented at least as much as the block
+ self.last = srowcol[0]
elif self.indent == 0 and type not in (tokenize.COMMENT, tokenize.NL):
# any other token on the same indentation level end the previous
# block as well, except the pseudo-tokens COMMENT and NL.
@@ -2122,9 +2137,9 @@ def _signature_fromstr(cls, obj, s, skip_bound_arg=True):
return cls(parameters, return_annotation=cls.empty)
-def _get_type_hints(func):
+def _get_type_hints(func, **kwargs):
try:
- return typing.get_type_hints(func)
+ return typing.get_type_hints(func, **kwargs)
except Exception:
# First, try to use the get_type_hints to resolve
# annotations. But for keeping the behavior intact
@@ -2149,7 +2164,8 @@ def _signature_from_builtin(cls, func, skip_bound_arg=True):
return _signature_fromstr(cls, func, s, skip_bound_arg)
-def _signature_from_function(cls, func, skip_bound_arg=True):
+def _signature_from_function(cls, func, skip_bound_arg=True,
+ globalns=None, localns=None):
"""Private helper: constructs Signature for the given python function."""
is_duck_function = False
@@ -2175,7 +2191,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True):
positional = arg_names[:pos_count]
keyword_only_count = func_code.co_kwonlyargcount
keyword_only = arg_names[pos_count:pos_count + keyword_only_count]
- annotations = _get_type_hints(func)
+ annotations = _get_type_hints(func, globalns=globalns, localns=localns)
defaults = func.__defaults__
kwdefaults = func.__kwdefaults__
@@ -2247,23 +2263,28 @@ def _signature_from_function(cls, func, skip_bound_arg=True):
def _signature_from_callable(obj, *,
follow_wrapper_chains=True,
skip_bound_arg=True,
+ globalns=None,
+ localns=None,
sigcls):
"""Private helper function to get signature for arbitrary
callable objects.
"""
+ _get_signature_of = functools.partial(_signature_from_callable,
+ follow_wrapper_chains=follow_wrapper_chains,
+ skip_bound_arg=skip_bound_arg,
+ globalns=globalns,
+ localns=localns,
+ sigcls=sigcls)
+
if not callable(obj):
raise TypeError('{!r} is not a callable object'.format(obj))
if isinstance(obj, types.MethodType):
# In this case we skip the first parameter of the underlying
# function (usually `self` or `cls`).
- sig = _signature_from_callable(
- obj.__func__,
- follow_wrapper_chains=follow_wrapper_chains,
- skip_bound_arg=skip_bound_arg,
- sigcls=sigcls)
+ sig = _get_signature_of(obj.__func__)
if skip_bound_arg:
return _signature_bound_method(sig)
@@ -2277,11 +2298,7 @@ def _signature_from_callable(obj, *,
# If the unwrapped object is a *method*, we might want to
# skip its first parameter (self).
# See test_signature_wrapped_bound_method for details.
- return _signature_from_callable(
- obj,
- follow_wrapper_chains=follow_wrapper_chains,
- skip_bound_arg=skip_bound_arg,
- sigcls=sigcls)
+ return _get_signature_of(obj)
try:
sig = obj.__signature__
@@ -2308,11 +2325,7 @@ def _signature_from_callable(obj, *,
# (usually `self`, or `cls`) will not be passed
# automatically (as for boundmethods)
- wrapped_sig = _signature_from_callable(
- partialmethod.func,
- follow_wrapper_chains=follow_wrapper_chains,
- skip_bound_arg=skip_bound_arg,
- sigcls=sigcls)
+ wrapped_sig = _get_signature_of(partialmethod.func)
sig = _signature_get_partial(wrapped_sig, partialmethod, (None,))
first_wrapped_param = tuple(wrapped_sig.parameters.values())[0]
@@ -2331,18 +2344,15 @@ def _signature_from_callable(obj, *,
# If it's a pure Python function, or an object that is duck type
# of a Python function (Cython functions, for instance), then:
return _signature_from_function(sigcls, obj,
- skip_bound_arg=skip_bound_arg)
+ skip_bound_arg=skip_bound_arg,
+ globalns=globalns, localns=localns)
if _signature_is_builtin(obj):
return _signature_from_builtin(sigcls, obj,
skip_bound_arg=skip_bound_arg)
if isinstance(obj, functools.partial):
- wrapped_sig = _signature_from_callable(
- obj.func,
- follow_wrapper_chains=follow_wrapper_chains,
- skip_bound_arg=skip_bound_arg,
- sigcls=sigcls)
+ wrapped_sig = _get_signature_of(obj.func)
return _signature_get_partial(wrapped_sig, obj)
sig = None
@@ -2353,29 +2363,17 @@ def _signature_from_callable(obj, *,
# in its metaclass
call = _signature_get_user_defined_method(type(obj), '__call__')
if call is not None:
- sig = _signature_from_callable(
- call,
- follow_wrapper_chains=follow_wrapper_chains,
- skip_bound_arg=skip_bound_arg,
- sigcls=sigcls)
+ sig = _get_signature_of(call)
else:
# Now we check if the 'obj' class has a '__new__' method
new = _signature_get_user_defined_method(obj, '__new__')
if new is not None:
- sig = _signature_from_callable(
- new,
- follow_wrapper_chains=follow_wrapper_chains,
- skip_bound_arg=skip_bound_arg,
- sigcls=sigcls)
+ sig = _get_signature_of(new)
else:
# Finally, we should have at least __init__ implemented
init = _signature_get_user_defined_method(obj, '__init__')
if init is not None:
- sig = _signature_from_callable(
- init,
- follow_wrapper_chains=follow_wrapper_chains,
- skip_bound_arg=skip_bound_arg,
- sigcls=sigcls)
+ sig = _get_signature_of(init)
if sig is None:
# At this point we know, that `obj` is a class, with no user-
@@ -2421,11 +2419,7 @@ def _signature_from_callable(obj, *,
call = _signature_get_user_defined_method(type(obj), '__call__')
if call is not None:
try:
- sig = _signature_from_callable(
- call,
- follow_wrapper_chains=follow_wrapper_chains,
- skip_bound_arg=skip_bound_arg,
- sigcls=sigcls)
+ sig = _get_signature_of(call)
except ValueError as ex:
msg = 'no signature found for {!r}'.format(obj)
raise ValueError(msg) from ex
@@ -2877,10 +2871,12 @@ class Signature:
return _signature_from_builtin(cls, func)
@classmethod
- def from_callable(cls, obj, *, follow_wrapped=True):
+ def from_callable(cls, obj, *,
+ follow_wrapped=True, globalns=None, localns=None):
"""Constructs Signature for the given callable object."""
return _signature_from_callable(obj, sigcls=cls,
- follow_wrapper_chains=follow_wrapped)
+ follow_wrapper_chains=follow_wrapped,
+ globalns=globalns, localns=localns)
@property
def parameters(self):
@@ -3128,9 +3124,10 @@ class Signature:
return rendered
-def signature(obj, *, follow_wrapped=True):
+def signature(obj, *, follow_wrapped=True, globalns=None, localns=None):
"""Get a signature object for the passed callable."""
- return Signature.from_callable(obj, follow_wrapped=follow_wrapped)
+ return Signature.from_callable(obj, follow_wrapped=follow_wrapped,
+ globalns=globalns, localns=localns)
def _main():