diff --git a/Lib/inspect.py b/Lib/inspect.py index 661957b5135..05bb71b81b9 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -432,7 +432,7 @@ def findsource(object): if not hasattr(object, 'co_firstlineno'): raise IOError('could not find function definition') lnum = object.co_firstlineno - 1 - pat = re.compile(r'^(\s*def\s)|(.*\slambda(:|\s))|^(\s*@)') + pat = re.compile(r'^(\s*def\s)|(.*(? 0: if pat.match(lines[lnum]): break lnum = lnum - 1 @@ -503,17 +503,28 @@ class BlockFinder: """Provide a tokeneater() method to detect the end of a code block.""" def __init__(self): self.indent = 0 - self.started = 0 + self.started = False + self.passline = False self.last = 0 def tokeneater(self, type, token, (srow, scol), (erow, ecol), line): if not self.started: - if '@' in line: pass - elif type == tokenize.NAME: self.started = 1 + if token in ("def", "class", "lambda"): + lastcolon = line.rfind(":") + if lastcolon: + oneline = re.search(r"\w", line[lastcolon:]) + if oneline and line[-2:] != "\\\n": + raise EndOfBlock, srow + self.started = True + self.passline = True elif type == tokenize.NEWLINE: + self.passline = False self.last = srow + elif self.passline: + pass elif type == tokenize.INDENT: self.indent = self.indent + 1 + self.passline = True elif type == tokenize.DEDENT: self.indent = self.indent - 1 if self.indent == 0: diff --git a/Lib/test/inspect_fodder2.py b/Lib/test/inspect_fodder2.py index 19da352185d..ce42929fffd 100644 --- a/Lib/test/inspect_fodder2.py +++ b/Lib/test/inspect_fodder2.py @@ -20,3 +20,36 @@ def wrapped(): @replace def gone(): pass + +# line 24 +oll = lambda m: m + +# line 27 +tll = lambda g: g and \ +g and \ +g + +# line 32 +tlli = lambda d: d and \ + d + +# line 36 +def onelinefunc(): pass + +# line 39 +def manyargs(arg1, arg2, +arg3, arg4): pass + +# line 43 +def twolinefunc(m): return m and \ +m + +# line 47 +a = [None, + lambda x: x, + None] + +# line 52 +def setfunc(func): + globals()["anonymous"] = func +setfunc(lambda x, y: x*y) diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 3f2da0a3315..04f22b40cd9 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -187,6 +187,48 @@ class TestDecorators(GetSourceBase): def test_replacing_decorator(self): self.assertSourceEqual(mod2.gone, 9, 10) +class TestOneliners(GetSourceBase): + fodderFile = mod2 + def test_oneline_lambda(self): + # Test inspect.getsource with a one-line lambda function. + self.assertSourceEqual(mod2.oll, 25, 25) + + def test_threeline_lambda(self): + # Test inspect.getsource with a three-line lambda function, + # where the second and third lines are _not_ indented. + self.assertSourceEqual(mod2.tll, 28, 30) + + def test_twoline_indented_lambda(self): + # Test inspect.getsource with a two-line lambda function, + # where the second line _is_ indented. + self.assertSourceEqual(mod2.tlli, 33, 34) + + def test_onelinefunc(self): + # Test inspect.getsource with a regular one-line function. + self.assertSourceEqual(mod2.onelinefunc, 37, 37) + + def test_manyargs(self): + # Test inspect.getsource with a regular function where + # the arguments are on two lines and _not_ indented and + # the body on the second line with the last arguments. + self.assertSourceEqual(mod2.manyargs, 40, 41) + + def test_twolinefunc(self): + # Test inspect.getsource with a regular function where + # the body is on two lines, following the argument list and + # continued on the next line by a \\. + self.assertSourceEqual(mod2.twolinefunc, 44, 45) + + def test_lambda_in_list(self): + # Test inspect.getsource with a one-line lambda function + # defined in a list, indented. + self.assertSourceEqual(mod2.a[1], 49, 49) + + def test_anonymous(self): + # Test inspect.getsource with a lambda function defined + # as argument to another function. + self.assertSourceEqual(mod2.anonymous, 55, 55) + # Helper for testing classify_class_attrs. def attrs_wo_objs(cls): return [t[:3] for t in inspect.classify_class_attrs(cls)] @@ -371,7 +413,7 @@ class TestClassesAndFunctions(unittest.TestCase): self.assert_(('datablob', 'data', A) in attrs, 'missing data') def test_main(): - run_unittest(TestDecorators, TestRetrievingSourceCode, + run_unittest(TestDecorators, TestRetrievingSourceCode, TestOneliners, TestInterpreterStack, TestClassesAndFunctions, TestPredicates) if __name__ == "__main__":