@@ -482,20 +482,35 @@ def g(frame, why, extra):
482482class JumpTracer :
483483"""Defines a trace function that jumps from one place to another."""
484484
485- def __init__ (self ,function ,jumpFrom ,jumpTo ):
486- self .function = function
485+ def __init__ (self ,function ,jumpFrom ,jumpTo ,event = 'line' ,
486+ decorated = False ):
487+ self .code = function .func_code
487488self .jumpFrom = jumpFrom
488489self .jumpTo = jumpTo
490+ self .event = event
491+ self .firstLine = None if decorated else self .code .co_firstlineno
489492self .done = False
490493
491494def trace (self ,frame ,event ,arg ):
492- if not self .done and frame .f_code == self .function .func_code :
493- firstLine = frame .f_code .co_firstlineno
494- if event == 'line' and frame .f_lineno == firstLine + self .jumpFrom :
495+ if self .done :
496+ return
497+ # frame.f_code.co_firstlineno is the first line of the decorator when
498+ # 'function' is decorated and the decorator may be written using
499+ # multiple physical lines when it is too long. Use the first line
500+ # trace event in 'function' to find the first line of 'function'.
501+ if (self .firstLine is None and frame .f_code == self .code and
502+ event == 'line' ):
503+ self .firstLine = frame .f_lineno - 1
504+ if (event == self .event and self .firstLine and
505+ frame .f_lineno == self .firstLine + self .jumpFrom ):
506+ f = frame
507+ while f is not None and f .f_code != self .code :
508+ f = f .f_back
509+ if f is not None :
495510# Cope with non-integer self.jumpTo (because of
496511# no_jump_to_non_integers below).
497512try :
498- frame .f_lineno = firstLine + self .jumpTo
513+ frame .f_lineno = self . firstLine + self .jumpTo
499514except TypeError :
500515frame .f_lineno = self .jumpTo
501516self .done = True
@@ -535,8 +550,9 @@ def compare_jump_output(self, expected, received):
535550"Expected: " + repr (expected )+ "\n " +
536551"Received: " + repr (received ))
537552
538- def run_test (self ,func ,jumpFrom ,jumpTo ,expected ,error = None ):
539- tracer = JumpTracer (func ,jumpFrom ,jumpTo )
553+ def run_test (self ,func ,jumpFrom ,jumpTo ,expected ,error = None ,
554+ event = 'line' ,decorated = False ):
555+ tracer = JumpTracer (func ,jumpFrom ,jumpTo ,event ,decorated )
540556sys .settrace (tracer .trace )
541557output = []
542558if error is None :
@@ -547,15 +563,15 @@ def run_test(self, func, jumpFrom, jumpTo, expected, error=None):
547563sys .settrace (None )
548564self .compare_jump_output (expected ,output )
549565
550- def jump_test (jumpFrom ,jumpTo ,expected ,error = None ):
566+ def jump_test (jumpFrom ,jumpTo ,expected ,error = None , event = 'line' ):
551567"""Decorator that creates a test that makes a jump
552568 from one place to another in the following code.
553569 """
554570def decorator (func ):
555571@wraps (func )
556572def test (self ):
557- # +1 to compensate a decorator line
558- self . run_test ( func , jumpFrom + 1 , jumpTo + 1 , expected , error )
573+ self . run_test ( func , jumpFrom , jumpTo , expected ,
574+ error = error , event = event , decorated = True )
559575return test
560576return decorator
561577
@@ -1018,6 +1034,36 @@ class fake_function:
10181034sys .settrace (None )
10191035self .compare_jump_output ([2 ,3 ,2 ,3 ,4 ],namespace ["output" ])
10201036
1037+ @jump_test (2 ,3 , [1 ],event = 'call' ,error = (ValueError ,"can't jump from"
1038+ " the 'call' trace event of a new frame" ))
1039+ def test_no_jump_from_call (output ):
1040+ output .append (1 )
1041+ def nested ():
1042+ output .append (3 )
1043+ nested ()
1044+ output .append (5 )
1045+
1046+ @jump_test (2 ,1 , [1 ],event = 'return' ,error = (ValueError ,
1047+ "can only jump from a 'line' trace event" ))
1048+ def test_no_jump_from_return_event (output ):
1049+ output .append (1 )
1050+ return
1051+
1052+ @jump_test (2 ,1 , [1 ],event = 'exception' ,error = (ValueError ,
1053+ "can only jump from a 'line' trace event" ))
1054+ def test_no_jump_from_exception_event (output ):
1055+ output .append (1 )
1056+ 1 / 0
1057+
1058+ @jump_test (3 ,2 , [2 ],event = 'return' ,error = (ValueError ,
1059+ "can't jump from a yield statement" ))
1060+ def test_no_jump_from_yield (output ):
1061+ def gen ():
1062+ output .append (2 )
1063+ yield 3
1064+ next (gen ())
1065+ output .append (5 )
1066+
10211067
10221068def test_main ():
10231069test_support .run_unittest (