Skip to content

Commit 018b760

Browse files
committed
First draft of functionInspector for requirements.json creation
1 parent 1108ccf commit 018b760

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed

src/sasctl/utils/functionInspector.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#!/usr/bin/env python
2+
# encoding: utf-8
3+
#
4+
# Copyright © 2022, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
5+
# SPDX-License-Identifier: Apache-2.0
6+
import logging
7+
import sys
8+
9+
def findPackageBytecodes(inst):
10+
from types import CodeType, FunctionType, ModuleType
11+
from importlib import import_module
12+
from functools import reduce
13+
14+
if inst.opname == "IMPORT_NAME":
15+
path = inst.argval.split(".")
16+
path[0] = [import_module(path[0])]
17+
result = reduce(lambda x, a: x + [getattr(x[-1], a)], path)
18+
return ("modules", result)
19+
if inst.opname == "LOAD_GLOBAL":
20+
if inst.argval in globals() and type(globals()[inst.argval]) in [CodeType, FunctionType]:
21+
return ("code", globals()[inst.argval])
22+
if inst.argval in globals() and type(globals()[inst.argval]) == ModuleType:
23+
return ("modules", [globals()[inst.argval]])
24+
else:
25+
return None
26+
if "LOAD_" in inst.opname and type(inst.argval) in [CodeType, FunctionType]:
27+
return ("code", inst.argval)
28+
return None
29+
30+
def getFunctionPackages(func):
31+
import inspect
32+
from dis import Bytecode
33+
workList = [func]
34+
seen = set()
35+
mods = set()
36+
logging.debug("workList type = %s, len = %d" % (type(workList), len(workList)))
37+
logging.debug(workList)
38+
for fn in workList:
39+
if (hasattr(fn,"__name__")):
40+
if fn.__name__ == "getFunctionPackages":
41+
logging.debug("Preemptively skip %s " % fn.__name__)
42+
workList.remove(fn)
43+
elif fn.__name__ == "findPackageBytecodes":
44+
logging.debug("Preemptively skip %s " % fn.__name__)
45+
workList.remove(fn)
46+
else:
47+
logging.debug("Letting through name %s" % fn.__name__)
48+
else:
49+
print("Letting through %s" % fn)
50+
for fn in workList:
51+
codeworkList = [fn]
52+
logging.debug("In workList :", end='')
53+
logging.debug(fn)
54+
try:
55+
closureVars = inspect.getclosurevars(fn).globals
56+
for var, ref in closureVars.items():
57+
if (var != 'getFunctionPackages'):
58+
if inspect.ismodule(ref):
59+
mods.add(ref.__name__)
60+
elif inspect.isfunction(ref) and id(ref) not in seen:
61+
seen.add(id(ref))
62+
mods.add(ref.__module__)
63+
if (hasattr(ref, '__name__')):
64+
if fn.__name__ == "getFunctionPackages":
65+
logging.debug("A- Not adding to workList %s " % fn.__name__)
66+
elif fn.__name__ == "findPackageBytecodes":
67+
logging.debug("A- Not adding to workList %s " % fn.__name__)
68+
else:
69+
logging.debug("A- Adding to workList: %s" % ref)
70+
workList.append(ref)
71+
else:
72+
logging.debug("A- Adding to workList: %s" % ref)
73+
workList.append(ref)
74+
elif hasattr(ref, "__module__"):
75+
logging.debug("1 fn=%s, k=%s, v=%s" % (fn, var, ref.__module__))
76+
mods.add(ref.__module__)
77+
for block in codeworkList:
78+
for (bType, bRef) in [findPackageBytecodes(inst) for inst in Bytecode(block) if findPackageBytecodes(inst)]:
79+
if bType == "modules":
80+
newmods = [mod.__name__ for mod in bRef if hasattr(mod, "__name__")]
81+
for mo in newmods:
82+
if mo == "getFunctionPackages":
83+
logging.debug("Also skip %s " % mo)
84+
newmods.remove(mo)
85+
elif mo == "findPackageBytecodes":
86+
logging.debug("Also skip %s " % mo)
87+
newmods.remove(mo)
88+
else:
89+
logging.debug("2 fn=%s, k=%s, bRef=%s" % (fn, bType, mo))
90+
mods.update(set(newmods))
91+
elif bType == "code" and id(bRef) not in seen:
92+
seen.add(id(bRef))
93+
if hasattr(bRef, "__module__"):
94+
if bRef.__name__ != "getFunctionPackages" or bRef.__name__ != "findPackageBytecodes" :
95+
logging.debug("3 fn=%s, bType=%s, bRef=%s" % (fn, bType, bRef))
96+
mods.add(bRef.__module__)
97+
if(inspect.isfunction(bRef)):
98+
if (hasattr(bRef, '__name__')):
99+
if bRef.__name__.find("getFunctionPackages") != -1:
100+
logging.debug("B- Not adding to workList %s " % bRef.__name__)
101+
elif bRef.__name__.find("findPackageBytecodes") != -1:
102+
logging.debug("B- Not adding to workList %s " % bRef.__name__)
103+
else:
104+
logging.debug("B- Adding to workList 1: %s" % bRef.__name__)
105+
workList.append(bRef)
106+
else:
107+
logging.debug("B- Adding to workList 2: %s" % bRef)
108+
logging.debug(dir(bRef))
109+
workList.append(bRef)
110+
elif(inspect.iscode(bRef)):
111+
codeworkList.append(bRef)
112+
except TypeError:
113+
continue
114+
result = list(mods)
115+
result.sort()
116+
return result
117+
118+
def findDependencies():
119+
dependencies = ""
120+
mySymbols = []
121+
logging.debug(globals().keys())
122+
try:
123+
mySymbols = list(globals().keys())
124+
mySymbols.remove("getFunctionPackages")
125+
mySymbols.remove("findPackageBytecodes")
126+
for symbol in mySymbols:
127+
if symbol.startswith("__"):
128+
logging.debug("removing %s" % symbol)
129+
mySymbols.remove(symbol)
130+
if symbol.startswith("'"):
131+
logging.debug("removing %s" % symbol)
132+
mySymbols.remove(symbol)
133+
allList = []
134+
for symbol in mySymbols:
135+
logging.debu("Symbol=%s" % symbol)
136+
subList = getFunctionPackages(globals()[symbol])
137+
allList = allList + subList
138+
logging.debug('%s : %s' % (symbol, allList))
139+
uniqueList = set(allList)
140+
dependencies = ",".join(uniqueList)
141+
except:
142+
logging.debug(sys.exc_info()[0] + " occurred.")
143+
dependencies = ",".join(sys.exc_info())
144+
symbols = ",".join(mySymbols)
145+
return symbols, dependencies

0 commit comments

Comments
 (0)