Скомпилировать файл с двумя отдельными библиотеками в Cython

Я написал библиотеку на Cython, которая имеет два разных «режима»:

  1. При рендеринге я компилирую с использованием GLFW.
  2. Если не рендеринг, я компилирую, используя EGL, который быстрее, но я не понял, как рендерить с его помощью.

Каков рекомендуемый способ справиться с этой ситуацией?

Сейчас у меня следующая структура каталогов:

mujoco
├── __init__.py
├── simEgl.pyx
├── simGlfw.pyx
├── sim.pxd
└── sim.pyx

simEgl.pyx содержит код EGL, а simGlfw.pyx содержит код GLFW. setup.py использует переменную среды, чтобы выбрать ту или другую для сборки.

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

Обновлять

Я согласен с тем, что лучший подход - одновременно скомпилировать две разные библиотеки и использовать переключатель, чтобы выбрать, какую из них импортировать. У меня уже есть базовый класс в sim.pyx с общей функциональностью. Однако сам этот базовый класс должен быть скомпилирован с отдельными библиотеками. В частности, sim.pyx зависит от libmujoco.so, который зависит от GLFW или EGL.

Вот мой исчерпывающий поиск возможных подходов:

  1. Если я не скомпилирую расширение для sim.pyx, я получу ImportError: No module named 'mujoco.sim'
  2. Если я скомпилирую расширение для sim.pyx без включения графических библиотек в расширение, я получу ImportError: /home/ethanbro/.mujoco/mjpro150/bin/libmujoco150.so: undefined symbol: __glewBlitFramebuffer
  3. Если я скомпилирую расширение для sim.pyx и выберу один набор графических библиотек (GLFW), то, когда я попытаюсь использовать другой набор графических библиотек (EGL), это тоже не сработает, что неудивительно: ERROR: GLEW initalization error: Missing GL version
  4. Если я скомпилирую две разные версии библиотеки sim.pyx, одну с одним набором библиотек, одну с другим, я получу: TypeError: unorderable types: dict() < dict(), что не очень полезное сообщение об ошибке, но, похоже, является результатом попытки поделиться исходным файлом между двумя разными расширения.

Что-то вроде варианта 4 должно быть возможно. Фактически, если бы я работал на чистом C, я бы просто построил два общих объекта бок о бок, используя разные библиотеки. Любые советы о том, как обойти это ограничение Cython, будут очень приветствоваться.


person ethanabrooks    schedule 01.12.2017    source источник
comment
Вы, вероятно, захотите просто собрать оба как отдельные модули расширения? например stackoverflow.com/a/21826294/3657742 в качестве примера.   -  person chrisb    schedule 02.12.2017
comment
Я во многом согласен с @chrisb. Вы пишете два модуля расширения с одинаковыми интерфейсами и используете if rendering: import simGlfw as s; else: import simEgl as s. Это может измениться, если вы хотите использовать функции Cython (т.е. функции времени компиляции), но в этом случае вам нужно предоставить более подробную информацию (хотя общий базовый класс может быть хорошим решением)   -  person DavidW    schedule 02.12.2017
comment
Я согласен, что это правильный путь. Однако это требует компиляции sim.pyx файла в два разных .so файла. Я пытаюсь сделать это с помощью Extension(name='sim1', ...) и Extension(name='sim2', ...) , но это вызвало ошибку импорта. Может быть, вы можете предложить лучший способ?   -  person ethanabrooks    schedule 03.12.2017
comment
@chrisb, принципиальная разница между моей ситуацией и сообщением, на которое вы ссылаетесь, заключается в том, что для этого сообщения не нужен один и тот же источник в двух отдельных расширениях.   -  person ethanabrooks    schedule 04.12.2017
comment
@ethanabrooks (Не уверен на 100%, но ...) Модули Cython / Python получают свое имя от имени исходного файла, что, как мне кажется, вызывает у вас проблемы. Вы можете попробовать создать два почти пустых файла .pyx с правильным именем и использовать Малоиспользуемый механизм включения Cython для загрузки общего кода в каждый из них.   -  person DavidW    schedule 05.12.2017
comment
@DavidW, что сработало. Отправьте ответ, чтобы я мог отдать вам должное.   -  person ethanabrooks    schedule 06.12.2017


Ответы (1)


(Этот ответ представляет собой всего лишь краткое изложение комментариев с дополнительными пояснениями.)

Моим первоначальным предложением было создать два модуля расширения, определяющих общий интерфейс. Таким образом, вы выбираете, что импортировать в Python, но иметь возможность использовать их таким же образом после импорта:

if rendering:
   import simGlfw as s
else:
   import simEgl as s
s.do_something() # doesn't matter which you imported

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

Extension(name='sim1', sources=["sim.pyx",...)
Extension(name='sim2', sources=["sim.pyx",...)

терпит неудачу. Это связано с тем, что Cython предполагает, что имя модуля будет таким же, как имя файла, и поэтому создает функцию PyInit_sim (в Python 3 - Python 2 имеет несколько иное имя, но идея та же). Однако при импорте sim1.so он ищет функцию PyInit_sim1, не может ее найти и выдает ошибку.

Простой способ обойти это - поместить общий код в sim.pxi и использовать В основном устаревший механизм включения Cython для текстового включения этого кода в sim1.pyx и sim2.pyx

include "sim.pxi"

Хотя include, как правило, больше не рекомендуется, а cimport предпочтительнее, так как он обеспечивает более «Python-подобное» поведение, include является простым решением этой конкретной проблемы.

person DavidW    schedule 06.12.2017