Как переопределить импорт Python?

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

проблема в том, что препроцессор проходит через файл, обрабатывает его, выводит во временный файл и exec() временный файл. Импортируемые библиотеки должны обрабатываться несколько иначе, потому что они не выполняются, а скорее загружаются и становятся доступными для вызывающего модуля.

что мне нужно сделать, так это: прервать импорт (так как препроцессор запускается в середине импорта), загрузить постпроцессорный код как tempModule и заменить исходный импорт tempModule, чтобы обмануть вызывающий скрипт с импортом, полагая, что tempModule является исходным модулем.

Я искал везде и до сих пор и нет решения.

этот вопрос переполнения стека является самым близким, что я видел до сих пор, чтобы дать ответ: переопределить пространство имен в Python

вот что у меня есть.

# Remove the bytecode file created by the first import
os.remove(moduleName + '.pyc')

# Remove the first import
del sys.modules[moduleName]

# Import the postprocessed module
tmpModule = __import__(tmpModuleName)

# Set first module's reference to point to the preprocessed module
sys.modules[moduleName] = tmpModule

moduleName-имя исходного модуля, а tmpModuleName-имя файла постпроцессорного кода.

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

надеюсь, кто-то в Stack Overflow знает намного больше об импорте, чем я, потому что это меня озадачило.

Примечание: я буду присуждать только решение, или, если это невозможно в Python; лучшее, самое подробное объяснение того, почему это не невозможно.

Update: для всех, кто заинтересован, вот рабочий код.

if imp.lock_held() is True:
    del sys.modules[moduleName]
    sys.modules[tmpModuleName] = __import__(tmpModuleName)
    sys.modules[moduleName] = __import__(tmpModuleName)

в ИМП.lock_held' часть определяет, загружается ли модуль как библиотека. Следующие строки делают все остальное.

4 ответов


это ответ на твой вопрос? Второй импорт делает трюк.

Mod_1.py

def test_function():
    print "Test Function -- Mod 1"

Mod_2.py

def test_function():
    print "Test Function -- Mod 2"

Test.py

#!/usr/bin/python

import sys

import Mod_1

Mod_1.test_function()

del sys.modules['Mod_1']

sys.modules['Mod_1'] = __import__('Mod_2')

import Mod_1

Mod_1.test_function()

чтобы определить другое поведение импорта или полностью подорвать процесс импорта, вам нужно будет написать крючки импорта. См.PEP 302.

например,

import sys

class MyImporter(object):

    def find_module(self, module_name, package_path):
        # Return a loader
        return self

    def load_module(self, module_name):
        # Return a module
        return self

sys.meta_path.append(MyImporter())

import now_you_can_import_any_name
print now_you_can_import_any_name

он выводит:

<__main__.MyImporter object at 0x009F85F0>

таким образом, в основном он возвращает новый модуль (который может быть любым объектом), в этом случае сам. Вы можете использовать его для изменения поведения импорта, возвращая processe_xxx импорт xxx.

ИМО: Python не нужен препроцессор. Все, что вы выполняете, может быть выполнено в самом Python из-за его очень динамического характера, например, в случае примера отладки, что не так с наличием поверх файла

debug = 1

и позже

if debug:
   print "wow"

?


в Python 2 есть imputil модуль, который, кажется, предоставляет функциональность, которую вы ищете, но был удален в python 3. Это не очень хорошо документировано, но содержит пример раздела, который показывает, как можно заменить стандартные функции импорта.

для Python 3 есть importlib модуль (введенный в Python 3.1), который содержит функции и классы для изменения функциональности импорта всеми способами. Это должно быть подходит для подключения препроцессора к системе импорта.


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

кстати, препроцессор C просто работает с текстовыми файлами и набором переменных препроцессора, а не с загруженными/выгруженными модулями высокого уровня.