Использование модуля Inspect для выполнения определенного кода - PullRequest
1 голос
/ 29 июня 2019

Для простоты я сузил свою задачу до следующего:

У меня есть основная функция, которая выполняет ряд функций.Из первой функции manipulate() я хочу иметь возможность запретить выходу программы test() с sys.exit().Мне также нужно иметь возможность заставить замолчать print("Silence me"), что означает, что это не должно отображаться в выходных данных моей программы.Наконец, моя программа все еще должна иметь возможность выводить print("You need to hear this") из функции test().

def main():
    manipulate()
    test()
def manipulate():

    print("Silence me")
    sys.exit()
    print("You need to hear this")

Как этого добиться, если я могу только изменить код в манипулировании ()функция?

Я пытался изучить использование модуля проверки, но я думаю, что я что-то упускаю.Я не уверен, что разбор кода для test() и запуск проанализированного кода через exec() - это правильный способ сделать это.

def manipulate():
    def filter_exc(func):
        src = inspect.getsource(func)

        lines = src.split('\n')
        out = lines[0] + "\n"

        for line in lines[1:]:
            m = re.match('(\s*)(.*)', line)
            lead, text = m.groups()
            if 'sys.exit()' in line:
                continue
            if 'Silence Me' in line:
                continue
            out += "    " + text + '\n'

        return out

    exec(filter_exc(game_on))

Ответы [ 2 ]

0 голосов
/ 30 июня 2019

Прежде всего, вам нужно знать, что то, что вы делаете, плохо.Разработанные языки программирования выполняют исходный код, а не ограничивают его выполнение.Редактирование живого байт-кода является злом и приведет к ошибкам, которые невозможно отладить.

Отказ от ответственности. Вы можете достичь этого эффекта более чисто, используя patch из стандартной библиотеки lib для замены стандартных реализаций exit и * 1006.*, но вам нужно выполнить что-то вроде

from unittest.mock import patch
import sys


def my_print(*args, **kwargs):
    # do whatever
    return


def my_exit(*args, **kwargs):
    # do whatever
    return


def perform_patch():
    patchers = [
        patch('__main__.print', my_print),
        patch('sys.exit', my_exit)
    ]
    for patcher in patchers:
        patcher.start()
    return patchers


def perform_unpatch(patchers):
    for patcher in patchers:
        patcher.stop()


def manipulate():
    print("hello")
    sys.exit(1)


def main():
    patchers = perform_patch()
    manipulate() 
    perform_unpatch(patchers)
    print("I'm done!")


if __name__ == '__main__':
    main()

Сценарий выводит только «Я закончил!».

exit и print остаются исправленными до тех пор, пока вы не вызовете patcher.stop(),patch может использоваться в блоке with, поэтому его можно сделать чище, если вы можете поместить вызов manipulate() в оператор with.

Другой способ - получить исходный код и использовать его. ast модуль, чтобы динамически переписать его.Вот разговор о AST .

Наконец, вы можете сделать что-то непосредственно с байт-кодом, вот доклад, исследующий, что .

В конце концов, ясерьезно призываем вас использовать любой другой возможный способ редактирования источников.Если это библиотека, то вам было бы гораздо лучше, если бы вы ее просто разветвляли и редактировали исходники.

0 голосов
/ 30 июня 2019

В этом простом примере я бы просто "обезьяна" затронула функциональность:

from contextlib import contextmanager
import sys

def test():
    print("Silence me")
    sys.exit()
    print("You need to hear this")

@contextmanager
def manipulate():
    global print

    try:
        _sys_exit_backup, sys.exit = sys.exit, lambda: None
        i = iter([lambda *args:None, print])
        _print_backup, print = print, lambda *args: next(i)(*args)
        yield
    finally:
        sys.exit = _sys_exit_backup
        print = _print_backup

def main():
    with manipulate():
        test()

main()

Отпечатки:

You need to hear this
...