mirror of
https://github.com/python/cpython
synced 2024-09-05 08:17:45 +00:00
bpo-45017: move opcode-related logic from modulefinder to dis (GH-28246)
This commit is contained in:
parent
49acac00c0
commit
04676b6946
36
Lib/dis.py
36
Lib/dis.py
|
@ -535,6 +535,42 @@ def findlinestarts(code):
|
|||
yield start, line
|
||||
return
|
||||
|
||||
def _find_imports(co):
|
||||
"""Find import statements in the code
|
||||
|
||||
Generate triplets (name, level, fromlist) where
|
||||
name is the imported module and level, fromlist are
|
||||
the corresponding args to __import__.
|
||||
"""
|
||||
IMPORT_NAME = opmap['IMPORT_NAME']
|
||||
LOAD_CONST = opmap['LOAD_CONST']
|
||||
|
||||
consts = co.co_consts
|
||||
names = co.co_names
|
||||
opargs = [(op, arg) for _, op, arg in _unpack_opargs(co.co_code)
|
||||
if op != EXTENDED_ARG]
|
||||
for i, (op, oparg) in enumerate(opargs):
|
||||
if (op == IMPORT_NAME and i >= 2
|
||||
and opargs[i-1][0] == opargs[i-2][0] == LOAD_CONST):
|
||||
level = consts[opargs[i-2][1]]
|
||||
fromlist = consts[opargs[i-1][1]]
|
||||
yield (names[oparg], level, fromlist)
|
||||
|
||||
def _find_store_names(co):
|
||||
"""Find names of variables which are written in the code
|
||||
|
||||
Generate sequence of strings
|
||||
"""
|
||||
STORE_OPS = {
|
||||
opmap['STORE_NAME'],
|
||||
opmap['STORE_GLOBAL']
|
||||
}
|
||||
|
||||
names = co.co_names
|
||||
for _, op, arg in _unpack_opargs(co.co_code):
|
||||
if op in STORE_OPS:
|
||||
yield names[arg]
|
||||
|
||||
|
||||
class Bytecode:
|
||||
"""The bytecode operations of a piece of code
|
||||
|
|
|
@ -8,14 +8,6 @@
|
|||
import io
|
||||
import sys
|
||||
|
||||
|
||||
LOAD_CONST = dis.opmap['LOAD_CONST']
|
||||
IMPORT_NAME = dis.opmap['IMPORT_NAME']
|
||||
STORE_NAME = dis.opmap['STORE_NAME']
|
||||
STORE_GLOBAL = dis.opmap['STORE_GLOBAL']
|
||||
STORE_OPS = STORE_NAME, STORE_GLOBAL
|
||||
EXTENDED_ARG = dis.EXTENDED_ARG
|
||||
|
||||
# Old imp constants:
|
||||
|
||||
_SEARCH_ERROR = 0
|
||||
|
@ -394,24 +386,13 @@ def _safe_import_hook(self, name, caller, fromlist, level=-1):
|
|||
|
||||
def scan_opcodes(self, co):
|
||||
# Scan the code, and yield 'interesting' opcode combinations
|
||||
code = co.co_code
|
||||
names = co.co_names
|
||||
consts = co.co_consts
|
||||
opargs = [(op, arg) for _, op, arg in dis._unpack_opargs(code)
|
||||
if op != EXTENDED_ARG]
|
||||
for i, (op, oparg) in enumerate(opargs):
|
||||
if op in STORE_OPS:
|
||||
yield "store", (names[oparg],)
|
||||
continue
|
||||
if (op == IMPORT_NAME and i >= 2
|
||||
and opargs[i-1][0] == opargs[i-2][0] == LOAD_CONST):
|
||||
level = consts[opargs[i-2][1]]
|
||||
fromlist = consts[opargs[i-1][1]]
|
||||
if level == 0: # absolute import
|
||||
yield "absolute_import", (fromlist, names[oparg])
|
||||
else: # relative import
|
||||
yield "relative_import", (level, fromlist, names[oparg])
|
||||
continue
|
||||
for name in dis._find_store_names(co):
|
||||
yield "store", (name,)
|
||||
for name, level, fromlist in dis._find_imports(co):
|
||||
if level == 0: # absolute import
|
||||
yield "absolute_import", (fromlist, name)
|
||||
else: # relative import
|
||||
yield "relative_import", (level, fromlist, name)
|
||||
|
||||
def scan_code(self, co, m):
|
||||
code = co.co_code
|
||||
|
|
|
@ -1326,5 +1326,38 @@ def test_assert_not_in_with_arg_in_bytecode(self):
|
|||
with self.assertRaises(AssertionError):
|
||||
self.assertNotInBytecode(code, "LOAD_CONST", 1)
|
||||
|
||||
class TestFinderMethods(unittest.TestCase):
|
||||
def test__find_imports(self):
|
||||
cases = [
|
||||
("import a.b.c", ('a.b.c', 0, None)),
|
||||
("from a.b import c", ('a.b', 0, ('c',))),
|
||||
("from a.b import c as d", ('a.b', 0, ('c',))),
|
||||
("from a.b import *", ('a.b', 0, ('*',))),
|
||||
("from ...a.b import c as d", ('a.b', 3, ('c',))),
|
||||
("from ..a.b import c as d, e as f", ('a.b', 2, ('c', 'e'))),
|
||||
("from ..a.b import *", ('a.b', 2, ('*',))),
|
||||
]
|
||||
for src, expected in cases:
|
||||
with self.subTest(src=src):
|
||||
code = compile(src, "<string>", "exec")
|
||||
res = tuple(dis._find_imports(code))
|
||||
self.assertEqual(len(res), 1)
|
||||
self.assertEqual(res[0], expected)
|
||||
|
||||
def test__find_store_names(self):
|
||||
cases = [
|
||||
("x+y", ()),
|
||||
("x=y=1", ('x', 'y')),
|
||||
("x+=y", ('x',)),
|
||||
("global x\nx=y=1", ('x', 'y')),
|
||||
("global x\nz=x", ('z',)),
|
||||
]
|
||||
for src, expected in cases:
|
||||
with self.subTest(src=src):
|
||||
code = compile(src, "<string>", "exec")
|
||||
res = tuple(dis._find_store_names(code))
|
||||
self.assertEqual(res, expected)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in a new issue