| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -97,17 +97,47 @@ class Restart(Exception): | |||
| 97 | 97 | __all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace", | |
| 98 | 98 | "post_mortem", "help"] | |
| 99 | 99 | ||
| 100 | + | ||
| 101 | + def find_first_executable_line(code): | ||
| 102 | + """ Try to find the first executable line of the code object. | ||
| 103 | + | ||
| 104 | + Equivalently, find the line number of the instruction that's | ||
| 105 | + after RESUME | ||
| 106 | + | ||
| 107 | + Return code.co_firstlineno if no executable line is found. | ||
| 108 | + """ | ||
| 109 | + prev = None | ||
| 110 | + for instr in dis.get_instructions(code): | ||
| 111 | + if prev is not None and prev.opname == 'RESUME': | ||
| 112 | + if instr.positions.lineno is not None: | ||
| 113 | + return instr.positions.lineno | ||
| 114 | + return code.co_firstlineno | ||
| 115 | + prev = instr | ||
| 116 | + return code.co_firstlineno | ||
| 117 | + | ||
| 100 | 118 | def find_function(funcname, filename): | |
| 101 | 119 | cre = re.compile(r'def\s+%s\s*[(]' % re.escape(funcname)) | |
| 102 | 120 | try: | |
| 103 | 121 | fp = tokenize.open(filename) | |
| 104 | 122 | except OSError: | |
| 105 | 123 | return None | |
| 124 | + funcdef = "" | ||
| 125 | + funcstart = None | ||
| 106 | 126 | # consumer of this info expects the first line to be 1 | |
| 107 | 127 | with fp: | |
| 108 | 128 | for lineno, line in enumerate(fp, start=1): | |
| 109 | 129 | if cre.match(line): | |
| 110 | - return funcname, filename, lineno | ||
| 130 | + funcstart, funcdef = lineno, line | ||
| 131 | + elif funcdef: | ||
| 132 | + funcdef += line | ||
| 133 | + | ||
| 134 | + if funcdef: | ||
| 135 | + try: | ||
| 136 | + funccode = compile(funcdef, filename, 'exec').co_consts[0] | ||
| 137 | + except SyntaxError: | ||
| 138 | + continue | ||
| 139 | + lineno_offset = find_first_executable_line(funccode) | ||
| 140 | + return funcname, filename, funcstart + lineno_offset - 1 | ||
| 111 | 141 | return None | |
| 112 | 142 | ||
| 113 | 143 | def lasti2lineno(code, lasti): | |
@@ -975,7 +1005,7 @@ def do_break(self, arg, temporary = 0): | |||
| 975 | 1005 | #use co_name to identify the bkpt (function names | |
| 976 | 1006 | #could be aliased, but co_name is invariant) | |
| 977 | 1007 | funcname = code.co_name | |
| 978 | - lineno = self._find_first_executable_line(code) | ||
| 1008 | + lineno = find_first_executable_line(code) | ||
| 979 | 1009 | filename = code.co_filename | |
| 980 | 1010 | except: | |
| 981 | 1011 | # last thing to try | |
@@ -1078,23 +1108,6 @@ def checkline(self, filename, lineno): | |||
| 1078 | 1108 | return 0 | |
| 1079 | 1109 | return lineno | |
| 1080 | 1110 | ||
| 1081 | - def _find_first_executable_line(self, code): | ||
| 1082 | - """ Try to find the first executable line of the code object. | ||
| 1083 | - | ||
| 1084 | - Equivalently, find the line number of the instruction that's | ||
| 1085 | - after RESUME | ||
| 1086 | - | ||
| 1087 | - Return code.co_firstlineno if no executable line is found. | ||
| 1088 | - """ | ||
| 1089 | - prev = None | ||
| 1090 | - for instr in dis.get_instructions(code): | ||
| 1091 | - if prev is not None and prev.opname == 'RESUME': | ||
| 1092 | - if instr.positions.lineno is not None: | ||
| 1093 | - return instr.positions.lineno | ||
| 1094 | - return code.co_firstlineno | ||
| 1095 | - prev = instr | ||
| 1096 | - return code.co_firstlineno | ||
| 1097 | - | ||
| 1098 | 1111 | def do_enable(self, arg): | |
| 1099 | 1112 | """enable bpnumber [bpnumber ...] | |
| 1100 | 1113 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2661,7 +2661,7 @@ def quux(): | |||
| 2661 | 2661 | pass | |
| 2662 | 2662 | """.encode(), | |
| 2663 | 2663 | 'bœr', | |
| 2664 | - ('bœr', 4), | ||
| 2664 | + ('bœr', 5), | ||
| 2665 | 2665 | ) | |
| 2666 | 2666 | ||
| 2667 | 2667 | def test_find_function_found_with_encoding_cookie(self): | |
@@ -2678,7 +2678,7 @@ def quux(): | |||
| 2678 | 2678 | pass | |
| 2679 | 2679 | """.encode('iso-8859-15'), | |
| 2680 | 2680 | 'bœr', | |
| 2681 | - ('bœr', 5), | ||
| 2681 | + ('bœr', 6), | ||
| 2682 | 2682 | ) | |
| 2683 | 2683 | ||
| 2684 | 2684 | def test_find_function_found_with_bom(self): | |
@@ -2688,9 +2688,34 @@ def bœr(): | |||
| 2688 | 2688 | pass | |
| 2689 | 2689 | """.encode(), | |
| 2690 | 2690 | 'bœr', | |
| 2691 | - ('bœr', 1), | ||
| 2691 | + ('bœr', 2), | ||
| 2692 | 2692 | ) | |
| 2693 | 2693 | ||
| 2694 | + def test_find_function_first_executable_line(self): | ||
| 2695 | + code = textwrap.dedent("""\ | ||
| 2696 | + def foo(): pass | ||
| 2697 | + | ||
| 2698 | + def bar(): | ||
| 2699 | + pass # line 4 | ||
| 2700 | + | ||
| 2701 | + def baz(): | ||
| 2702 | + # comment | ||
| 2703 | + pass # line 8 | ||
| 2704 | + | ||
| 2705 | + def mul(): | ||
| 2706 | + # code on multiple lines | ||
| 2707 | + code = compile( # line 12 | ||
| 2708 | + 'def f()', | ||
| 2709 | + '<string>', | ||
| 2710 | + 'exec', | ||
| 2711 | + ) | ||
| 2712 | + """).encode() | ||
| 2713 | + | ||
| 2714 | + self._assert_find_function(code, 'foo', ('foo', 1)) | ||
| 2715 | + self._assert_find_function(code, 'bar', ('bar', 4)) | ||
| 2716 | + self._assert_find_function(code, 'baz', ('baz', 8)) | ||
| 2717 | + self._assert_find_function(code, 'mul', ('mul', 12)) | ||
| 2718 | + | ||
| 2694 | 2719 | def test_issue7964(self): | |
| 2695 | 2720 | # open the file as binary so we can force \r\n newline | |
| 2696 | 2721 | with open(os_helper.TESTFN, 'wb') as f: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1 @@ | |||
| 1 | + Set breakpoint on the first executable line of the function, instead of the line of function definition when the user do ``break func`` using :mod:`pdb` | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments