Неграфический вывод из pycallgraph
Я начал писать небольшую утилиту Python для кеширования функций. Доступные инструменты кеширования (lru_cache
, Мензурка) не обнаруживает изменения подфункций.
Для этого мне нужен Call Call. Существует великолепный инструмент в pycallgraph Gerald Kaszuba. Тем не менее, до сих пор я получил его только для вывода строк с именами функций. Что мне нужно, так это функциональные объекты или хеши кода функций.
Что я имею в виду с этими двумя терминами: пусть def foo(x): return x
, затем foo
является функцией-объектом, и hash(foo.__code__.co_code)
это хэш-код функции
Что я имею
Вы можете увидеть, что у меня здесь. Но ниже приведен минимальный пример. Проблема, с которой я столкнулся в этом примере, заключается в том, что я не могу снова перейти от имени функции (строки) к определению функции. Я пытаюсь с eval(func)
,
Итак, я думаю, есть два способа решения этой проблемы:
- правильный
pycallgraph.output
или каким-то другим способом получить то, что я хочу, прямо из Pycallgraph. - Динамическая загрузка функции из
function.__name__
строка.
import unittest
from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput
class Callgraph:
def __init__(self, output_file='callgraph.png'):
self.graphviz = GraphvizOutput()
self.graphviz.output_file = output_file
def execute(self, function, *args, **kwargs):
with PyCallGraph(output=self.graphviz):
ret = function(*args, **kwargs)
self.graph = dict()
for node in self.graphviz.processor.nodes():
if node.name != '__main__':
f = eval(node.name)
self.graph[node.name] = hash(f.__code__.co_code)
return ret
def unchanged(self):
'''Checks each function in the callgraph whether it has changed.
Returns True if all the function have their original code-hash. False otherwise.
'''
for func, codehash in self.graph.iteritems():
f = eval(func)
if hash(f.__code__.co_code) != codehash:
return False
return True
def func_inner(x):
return x
def func_outer(x):
return 2*func_inner(x)
class CallgraphTest(unittest.TestCase):
def testChanges(self):
cg = Callgraph()
y = cg.execute(func_outer, 3)
self.assertEqual(6, y)
self.assertTrue(cg.unchanged())
# Change one of the functions
def func_inner(x):
return 3+x
self.assertFalse(cg.unchanged())
# Change back!
def func_inner(x):
return x
self.assertTrue(cg.unchanged())
if __name__ == '__main__':
unittest.main()
1 ответ
Я решил это путем исправления tracer.py
с соответствующими хешами.
# Work out the current function or method
func_name = code.co_name
+ func_hash = hash(code.co_code)
Я рассчитываю значение только там, где имя функции сохраняется. Позже вам, очевидно, также потребуется сохранить это значение. Я делаю это со словарем, где func_name
является ключом, а хэш является значением. В функции, где создаются узлы, я назначаю это новому полю в stat_group
,