11# -*- coding: utf-8 -*-
2- # Copyright (C) 2009, 2012-2013 Rocky Bernstein <rocky@gnu.org>
2+ #
3+ # Copyright (C) 2009, 2012-2013, 2020, 2023-2024 Rocky Bernstein
4+ # <rocky@gnu.org>
35#
46# This program is free software: you can redistribute it and/or modify
57# it under the terms of the GNU General Public License as published by
1315#
1416# You should have received a copy of the GNU General Public License
1517# along with this program. If not, see <http://www.gnu.org/licenses/>.
16- '''Bytecode instruction routines'''
18+ """Bytecode instruction routines"""
19+
20+ import re
21+
22+ from opcode import HAVE_ARGUMENT , opname
23+ from xdis import PYTHON_VERSION_TRIPLE , get_opcode_module
1724
18- import dis , re
19- from opcode import opname , HAVE_ARGUMENT
25+ opcode_module = get_opcode_module (PYTHON_VERSION_TRIPLE )
2026
2127
2228def op_at_code_loc (code , loc ):
2329 try :
2430 op = ord (code [loc ])
2531 except IndexError :
26- return ' got IndexError'
32+ return " got IndexError"
2733 return opname [op ]
2834
2935
3036def op_at_frame (frame , loc = None ):
3137 code = frame .f_code .co_code
32- if loc is None : loc = frame .f_lasti
38+ if loc is None :
39+ loc = frame .f_lasti
3340 return op_at_code_loc (code , loc )
3441
3542
3643def next_opcode (code , offset ):
37- ''' Return the next opcode and offset as a tuple. Tuple (-100,
38- -1000) is returned when reaching the end.'''
44+ """ Return the next opcode and offset as a tuple. Tuple (-100,
45+ -1000) is returned when reaching the end."""
3946 n = len (code )
4047 while offset < n :
4148 c = code [offset ]
@@ -51,8 +58,9 @@ def next_opcode(code, offset):
5158
5259
5360def next_linestart (co , offset , count = 1 ):
54- linestarts = dict (dis .findlinestarts (co ))
5561 code = co .co_code
62+
63+ linestarts = dict (opcode_module .findlinestarts (co ))
5664 # n = len(code)
5765 # contains_cond_jump = False
5866 for op , offset in next_opcode (code , offset ):
@@ -62,13 +70,15 @@ def next_linestart(co, offset, count=1):
6270 return linestarts [offset ]
6371 pass
6472 pass
73+
6574 return - 1000
6675
6776
6877def stmt_contains_opcode (co , lineno , query_opcode ):
69- linestarts = dict (dis .findlinestarts (co ))
78+ linestarts = dict (opcode_module .findlinestarts (co ))
7079 code = co .co_code
7180 found_start = False
81+ offset = 0
7282 for offset , start_line in list (linestarts .items ()):
7383 if start_line == lineno :
7484 found_start = True
@@ -77,40 +87,50 @@ def stmt_contains_opcode(co, lineno, query_opcode):
7787 if not found_start :
7888 return False
7989 for op , offset in next_opcode (code , offset ):
80- if - 1000 == offset or linestarts .get (offset ): return False
90+ if - 1000 == offset or linestarts .get (offset ):
91+ return False
8192 opcode = opname [op ]
8293 # debug: print opcode
8394 if query_opcode == opcode :
8495 return True
8596 pass
8697 return False
8798
88- _re_def_str = r'^\s*def\s'
99+
100+ _re_def_str = r"^\s*def\s"
89101_re_def = re .compile (_re_def_str )
90102
91103
92104def is_def_stmt (line , frame ):
93105 """Return True if we are looking at a def statement"""
94106 # Should really also check that operand of 'LOAD_CONST' is a code object
95- return (line and _re_def .match (line ) and op_at_frame (frame )== 'LOAD_CONST'
96- and stmt_contains_opcode (frame .f_code , frame .f_lineno ,
97- 'MAKE_FUNCTION' ))
107+ return (
108+ line
109+ and _re_def .match (line )
110+ and op_at_frame (frame ) == "LOAD_CONST"
111+ and stmt_contains_opcode (frame .f_code , frame .f_lineno , "MAKE_FUNCTION" )
112+ )
113+
98114
99- _re_class = re .compile (r' ^\s*class\s' )
115+ _re_class = re .compile (r" ^\s*class\s" )
100116
101117
102118def is_class_def (line , frame ):
103119 """Return True if we are looking at a class definition statement"""
104- return (line and _re_class .match (line )
105- and stmt_contains_opcode (frame .f_code , frame .f_lineno ,
106- 'BUILD_CLASS' ))
120+ return (
121+ line
122+ and _re_class .match (line )
123+ and stmt_contains_opcode (frame .f_code , frame .f_lineno , "BUILD_CLASS" )
124+ )
125+
107126
108127# Demo stuff above
109- if __name__ == ' __main__' :
128+ if __name__ == " __main__" :
110129 import inspect
111130
112131 def sqr (x ):
113132 return x * x
133+
114134 frame = inspect .currentframe ()
115135 co = frame .f_code
116136 lineno = frame .f_lineno
@@ -134,13 +154,14 @@ def double(x):
134154 print ("op at frame, position 2: %s" % op_at_frame (frame , 2 ))
135155 print ("def statement: x=5?: %s" % is_def_stmt ('x=5' , frame ))
136156 # Not a "def" statement because frame is wrong spot
137- print (is_def_stmt (' def foo():' , frame ))
157+ print (is_def_stmt (" def foo():" , frame ))
138158
139159 class Foo :
140160 pass
161+
141162 lineno = frame .f_lineno
142- print (' contains BUILD_CLASS %s' % stmt_contains_opcode (co , lineno - 2 ,
163+ print (" contains BUILD_CLASS %s" % stmt_contains_opcode (co , lineno - 2 ,
143164 'BUILD_CLASS' ))
144- print (' contains BUILD_CLASS %s' % stmt_contains_opcode (co , lineno ,
165+ print (" contains BUILD_CLASS %s" % stmt_contains_opcode (co , lineno ,
145166 'BUILD_CLASS' ))
146167 pass
0 commit comments