synr
synr copied to clipboard
inspect.getsourcelines is wrong
The behavior of inspect.getsourcelines
is incorrect for different classes with the same name (defined in different scopes). Observe the following excerpt from inspect.findsource
(used by getsourcelines
internally):
if isclass(object):
name = object.__name__
pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
# make some effort to find the best matching class definition:
# use the one with the least indentation, which is the one
# that's most probably not inside a function definition.
candidates = []
for i in range(len(lines)):
match = pat.match(lines[i])
if match:
# if it's at toplevel, it's already the best one
if lines[i][0] == 'c':
return lines, i
# else add whitespace to candidate list
candidates.append((match.group(1), i))
if candidates:
# this will sort by whitespace, and by line number,
# less whitespace first
candidates.sort()
return lines, candidates[0][1]
In other words, it returns the source for the "best" matching class definition instead of the correct class definition. So, code like this would break:
def foo():
class Baz: ...
ast = synr.to_ast(Baz)
def bar():
class Baz: ... # something different
ast = synr.to_ast(Baz) # this will parse the first Baz in foo
Not sure there's an easy solution. We could do our own check that there aren't multiple classes with the same name in a source file and error out if there is (to be conservative). inspect
should really have a disclaimer somewhere in the visible documentation for this, or implement a correct solution (perhaps using the __qualname__
field of the class).
this is related https://github.com/python/cpython/pull/10307