Kaydet (Commit) dd5c023a authored tarafından Armin Rigo's avatar Armin Rigo

some more fixes and tests for inspect.getsource(), triggered by crashes

from the PyPy project as well as the SF bug #1295909.
üst e9f8ec98
...@@ -485,19 +485,6 @@ def getcomments(object): ...@@ -485,19 +485,6 @@ def getcomments(object):
comments[-1:] = [] comments[-1:] = []
return string.join(comments, '') return string.join(comments, '')
class ListReader:
"""Provide a readline() method to return lines from a list of strings."""
def __init__(self, lines):
self.lines = lines
self.index = 0
def readline(self):
i = self.index
if i < len(self.lines):
self.index = i + 1
return self.lines[i]
else: return ''
class EndOfBlock(Exception): pass class EndOfBlock(Exception): pass
class BlockFinder: class BlockFinder:
...@@ -507,40 +494,46 @@ class BlockFinder: ...@@ -507,40 +494,46 @@ class BlockFinder:
self.islambda = False self.islambda = False
self.started = False self.started = False
self.passline = False self.passline = False
self.last = 0 self.last = 1
def tokeneater(self, type, token, (srow, scol), (erow, ecol), line): def tokeneater(self, type, token, (srow, scol), (erow, ecol), line):
if not self.started: if not self.started:
# look for the first "def", "class" or "lambda"
if token in ("def", "class", "lambda"): if token in ("def", "class", "lambda"):
if token == "lambda": if token == "lambda":
self.islambda = True self.islambda = True
self.started = True self.started = True
self.passline = True self.passline = True # skip to the end of the line
elif type == tokenize.NEWLINE: elif type == tokenize.NEWLINE:
self.passline = False self.passline = False # stop skipping when a NEWLINE is seen
self.last = srow self.last = srow
if self.islambda: # lambdas always end at the first NEWLINE
raise EndOfBlock
elif self.passline: elif self.passline:
pass pass
elif self.islambda:
raise EndOfBlock, self.last
elif type == tokenize.INDENT: elif type == tokenize.INDENT:
self.indent = self.indent + 1 self.indent = self.indent + 1
self.passline = True self.passline = True
elif type == tokenize.DEDENT: elif type == tokenize.DEDENT:
self.indent = self.indent - 1 self.indent = self.indent - 1
if self.indent == 0: # the end of matching indent/dedent pairs end a block
raise EndOfBlock, self.last # (note that this only works for "def"/"class" blocks,
elif type == tokenize.NAME and scol == 0: # not e.g. for "if: else:" or "try: finally:" blocks)
raise EndOfBlock, self.last if self.indent <= 0:
raise EndOfBlock
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.
raise EndOfBlock
def getblock(lines): def getblock(lines):
"""Extract the block of code at the top of the given list of lines.""" """Extract the block of code at the top of the given list of lines."""
blockfinder = BlockFinder()
try: try:
tokenize.tokenize(ListReader(lines).readline, BlockFinder().tokeneater) tokenize.tokenize(iter(lines).next, blockfinder.tokeneater)
except EndOfBlock, eob: except (EndOfBlock, IndentationError):
return lines[:eob.args[0]] pass
# Fooling the indent/dedent logic implies a one-line definition return lines[:blockfinder.last]
return lines[:1]
def getsourcelines(object): def getsourcelines(object):
"""Return a list of source lines and starting line number for an object. """Return a list of source lines and starting line number for an object.
......
...@@ -64,3 +64,27 @@ multiline_sig = [ ...@@ -64,3 +64,27 @@ multiline_sig = [
y): x+y, y): x+y,
None, None,
] ]
# line 68
def func69():
class cls70:
def func71():
pass
return cls70
extra74 = 74
# line 76
def func77(): pass
(extra78, stuff78) = 'xy'
extra79 = 'stop'
# line 81
class cls82:
def func83(): pass
(extra84, stuff84) = 'xy'
extra85 = 'stop'
# line 87
def func88():
# comment
return 90
...@@ -238,6 +238,18 @@ class TestBuggyCases(GetSourceBase): ...@@ -238,6 +238,18 @@ class TestBuggyCases(GetSourceBase):
def test_multiline_sig(self): def test_multiline_sig(self):
self.assertSourceEqual(mod2.multiline_sig[0], 63, 64) self.assertSourceEqual(mod2.multiline_sig[0], 63, 64)
def test_nested_class(self):
self.assertSourceEqual(mod2.func69().func71, 71, 72)
def test_one_liner_followed_by_non_name(self):
self.assertSourceEqual(mod2.func77, 77, 77)
def test_one_liner_dedent_non_name(self):
self.assertSourceEqual(mod2.cls82.func83, 83, 83)
def test_with_comment_instead_of_docstring(self):
self.assertSourceEqual(mod2.func88, 88, 90)
# Helper for testing classify_class_attrs. # Helper for testing classify_class_attrs.
def attrs_wo_objs(cls): def attrs_wo_objs(cls):
return [t[:3] for t in inspect.classify_class_attrs(cls)] return [t[:3] for t in inspect.classify_class_attrs(cls)]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment