Исходный код для eval'ed / динамически сгенерированной функции в Python - PullRequest
0 голосов
/ 12 июня 2018

Я ищу способ создания функции Python из строки, содержащей исходный код во время выполнения, таким образом, чтобы исходный код был доступен для проверки.

Мой текущий метод выглядит следующим образом:

src = 'def foo(x, y):' + '\n\t' + 'return x / y'
g = {numpy: numpy, ...}  # Modules and such required for function
l = {}
exec(src, g, l)
func = l['foo']

Работает отлично, но функция не связана с исходным кодом / файлом.Это затрудняет отладку (обратите внимание, что строка, в которой происходит ошибка в foo (), не показана):

>>> foo(1, 0)
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-85-9df128c5d862> in <module>()
----> 1 myfunc(3, 0)

<string> in foo(x, y)

ZeroDivisionError: division by zero

Если я определю функцию в интерпретаторе IPython, я смогу получить исходный код, используя inspect.getsourceи это будет напечатано в следах.inspect.getsourcefile возвращает что-то вроде '<ipython-input-19-8efed6025c6f>' для этих типов функций, что, конечно, не является реальным файлом.Есть ли способ сделать что-то подобное в неинтерактивной среде?

1 Ответ

0 голосов
/ 16 июня 2018

Так что я смог частично понять это, покопавшись в исходном коде IPython.Он использует встроенный модуль linecache , который содержит функции для чтения исходного кода из файлов и кэширования результатов.Оба модуля inspect и traceback используют этот модуль для получения источника функции.

Решение состоит в том, чтобы создать функцию так же, как в вопросе, но с использованием compile с-up и уникальное имя файла:

source = 'def foo(x, y):' + '\n\t' + 'return x / y'

filename = '<dynamic-123456>'  # Angle brackets may be required?
code = compile(source, filename, 'exec')

g = {numpy: numpy, ...}  # Modules and such required for function
l = {}
exec(src, g, l)
func = l['foo']

linecache содержит переменную cache, которая представляет собой словарь, сопоставляющий имена файлов с (size, mtime, lines, fullname) кортежами.Вы можете просто добавить запись для поддельного имени файла:

lines = [line + '\n' for line in source.splitlines()]

import linecache
linecache.cache[filename] = (len(source), None, lines, filename)

Затем функция будет работать с inspect.getsource(), синтаксисом IPython ? / ?? и трассировками IPython.Тем не менее, он все еще не работает во встроенных трассировках.Это в основном достаточно для меня, потому что я почти всегда работаю в IPython.

РЕДАКТИРОВАТЬ: см. Комментарий user2357112 ниже о том, как заставить это работать с печатью трассировки во встроенном интерпретаторе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...