Как перезагрузить модуль расширения Python3 C? - PullRequest
5 голосов
/ 28 ноября 2011

Я написал расширение C (mycext.c) для Python 3.2.Расширение опирается на постоянные данные, хранящиеся в заголовке C (myconst.h).Заголовочный файл генерируется скриптом Python.В этом же сценарии я использую недавно скомпилированный модуль.Рабочий процесс в Python3 myscript (не показан полностью) выглядит следующим образом:

configure_C_header_constants() 
write_constants_to_C_header() # write myconst.h
os.system('python3 setup.py install --user') # compile mycext
import mycext
mycext.do_stuff()

Впервые он отлично работает в сеансе Python.Если я повторяю процедуру в том же сеансе (например, в двух разных тестовых случаях юнит-теста), первая (с) скомпилированная версия mycext всегда (повторно) загружается.

Как эффективно перезагрузить модуль расширения с последней скомпилированной версией?

Ответы [ 2 ]

5 голосов
/ 28 ноября 2011

Вы можете перезагрузить модули в Python 3.x, используя функцию imp.reload(). (Раньше эта функция была встроенной в Python 2.x. Обязательно прочитайте документацию - есть несколько предостережений!)

Механизм импорта Python никогда не будет dlclose() разделяемой библиотекой. После загрузки библиотека останется до завершения процесса.

Ваши параметры (отсортированы по убыванию полезности):

  1. Переместите импорт модуля в подпроцесс и снова вызовите подпроцесс после перекомпиляции, т. Е. У вас есть скрипт Python do_stuff.py, который просто выполняет

    import mycext
    mycext.do_stuff()
    

    и вы вызываете этот скрипт, используя

    subprocess.call([sys.executable, "do_stuff.py"])
    
  2. Превратите константы времени компиляции в вашем заголовке в переменные, которые можно изменить из Python, избавляя от необходимости перезагружать модуль.

  3. Вручную dlclose() библиотека после удаления всех ссылок на модуль (немного хрупкая, поскольку вы не храните все ссылки самостоятельно).

  4. Сверните свой собственный механизм импорта.

    Вот пример, как это можно сделать. Я написал минимальное расширение Python C mini.so, экспортировав только целое число с именем version.

    >>> import ctypes
    >>> libdl = ctypes.CDLL("libdl.so")
    >>> libdl.dlclose.argtypes = [ctypes.c_void_p]
    >>> so = ctypes.PyDLL("./mini.so")
    >>> so.PyInit_mini.argtypes = []
    >>> so.PyInit_mini.restype = ctypes.py_object 
    >>> mini = so.PyInit_mini()
    >>> mini.version
    1
    >>> del mini
    >>> libdl.dlclose(so._handle)
    0
    >>> del so
    

    В этот момент я увеличил номер версии в mini.c и перекомпилировал.

    >>> so = ctypes.PyDLL("./mini.so")
    >>> so.PyInit_mini.argtypes = []
    >>> so.PyInit_mini.restype = ctypes.py_object 
    >>> mini = so.PyInit_mini()
    >>> mini.version
    2
    

    Вы видите, что используется новая версия модуля.

    Для справки и экспериментов, вот mini.c:

    #include <Python.h>
    
    static struct PyModuleDef minimodule = {
       PyModuleDef_HEAD_INIT, "mini", NULL, -1, NULL
    };
    
    PyMODINIT_FUNC
    PyInit_mini()
    {
        PyObject *m = PyModule_Create(&minimodule);
        PyModule_AddObject(m, "version", PyLong_FromLong(1));
        return m;
    }
    
0 голосов
/ 19 июля 2013

есть другой способ, установить новое имя модуля, импортировать его и изменить ссылку на него.

...