Я в настоящее время нахожусь в процессе добавления способности к пользователям расширить функциональность моего настольного приложения (C++) с помощью плагинов, заданных сценарием в Python.
Наивный метод достаточно легок. Встройте Python статическая библиотека и следуйте за любым количеством десятков учебных руководств, рассеянных вокруг сети, описывающей, как инициализировать и назвать файлы Python, и Вы в значительной степени сделаны.
Однако...
То, что я ищу, больше похоже на то, что делает Блендер. Блендер абсолютно настраиваем через сценарии Python, и он требует внешнего исполняемого файла Python. (Т.е. Python на самом деле не встраивается в исполняемый файл блендера вообще.) Так, естественно, можно включать любые модули, которые Вы уже имеете в своем каталоге пакетов сайта, когда Вы пишете сценарии блендера. Не то, чтобы это рекомендуется, так как это ограничило бы мобильность Вашего сценария.
Так, что я хочу знать, то, если уже существует способ иметь Ваш пирог и съесть его также. Я хочу сменную систему, которая использует:
Встроенный интерпретатор Python.
Оборотная сторона подхода Блендера - то, что он вынуждает Вас иметь определенное, возможно устаревшая версия Python, установленного глобально в Вашей системе. Наличие встроенного интерпретатора позволяет мне управлять тем, какая версия Python используется.
Плагины брандмауэра.
Некоторый эквивалент a virtualenv
для каждого плагина; позволяя им установить все модули они нуждаются или хотят, но хранение их разделенный от возможных конфликтов в других плагинах. Возможно, zc.buildout
лучший кандидат здесь, но, снова, я очень открыт для предложения. Я немного в недоумении относительно лучшего способа выполнить это.
Максимально безболезненный...
Для пользователя. Я готов приложить дополнительные усилия, настолько долго так же, большая часть вышеупомянутого максимально очевидна для сменного устройства записи.
Если бы какой-либо из Вас, у людей там есть любой опыт с этим видом вещи, Ваша справка, очень ценился бы.:)
Править: В основном короткая версия того, что я хочу, является простотой virtualenv
, но без связанного интерпретатора Python и способа активировать определенную "виртуальную среду" программно, как zc.buildout
делает с sys.path управлением ( sys.path[0:0] = [...]
прием).
Оба virtualenv
и zc.buildout
содержите части того, что я хочу, но никакой продукт перемещаемые сборки, которые я или сменный разработчик могу просто на молнии и отправлять на другой компьютер.
Просто управляя .pth файлами или управлением sys.path
непосредственно в сценарии, выполняемом из моего приложения, получает меня на полпути там. Но это недостаточно, когда скомпилированные модули необходимы, таковы как PIL.
Одним из эффективных способов достижения этой цели является использование архитектуры передачи сообщений/обмена процессами, позволяющей достичь цели с помощью Python, но не ограничивающейся Python.
------------------------------------
| App <--> Ext. API <--> Protocol | <--> (Socket) <--> API.py <--> Script
------------------------------------
Эта диаграмма пытается показать следующее: Ваше приложение взаимодействует с внешними процессами (например, Python), используя передачу сообщений. Это эффективно на локальной машине и может быть переносимым, поскольку вы определяете свой собственный протокол. Единственное, что вы должны предоставить своим пользователям, это библиотеку Python, которая реализует ваш пользовательский API и взаимодействует с помощью цикла передачи-получения сообщений между скриптом пользователя и вашим приложением.
Внешний API вашего приложения описывает все функции, с которыми должен иметь возможность взаимодействовать внешний процесс. Например, если вы хотите, чтобы ваш скрипт Python мог нарисовать красный круг в вашем приложении, ваш внешний API может включать Draw(Object, Color, Position).
Это протокол, который внешние процессы используют для связи с вашим приложением через внешний API. Популярными вариантами для этого могут быть XML-RPC, SunRPC, JSON или ваш собственный протокол и формат данных. Выбор должен быть достаточным для вашего API. Например, если вы собираетесь передавать двоичные данные, то JSON может потребовать кодировки base64, в то время как SunRPC предполагает двоичную передачу данных.
Это просто как бесконечный цикл, принимающий сообщения в вашем коммуникационном протоколе, обслуживающий запрос в вашем приложении и отвечающий на него через тот же сокет/канал. Например, если вы выбрали JSON, то вы получите сообщение, содержащее инструкции по выполнению Draw(Object, Color, Position). После выполнения запроса вы бы ответили на него.
Это еще проще. Опять же, это цикл, посылающий и принимающий сообщения от имени пользователя библиотеки (т.е. ваших пользователей, пишущих скрипты на Python). Единственное, что должна делать эта библиотека, это предоставлять программный интерфейс к внешнему API вашего приложения и переводить запросы в ваш протокол связи, все это скрыто от ваших пользователей.
Использование Unix Sockets, например, будет чрезвычайно быстрым.
Обычная практика обнаружения подключаемых модулей приложения заключается в указании "хорошо известного" каталога, в котором должны быть размещены подключаемые модули. Это может быть, например, так:
~/.myapp/plugins
Следующим шагом будет поиск вашим приложением в этом каталоге существующих плагинов. Ваше приложение должно обладать некоторым умом, чтобы уметь различать скрипты Python, которые являются и не являются настоящими скриптами для вашего приложения.
Предположим, что ваш протокол взаимодействия определяет, что каждый скрипт будет взаимодействовать с помощью JSON через StdInput/StdOuput. Простой и эффективный подход заключается в том, чтобы указать в протоколе, что при первом запуске скрипта он посылает MAGIC_ID в стандартный выход. То есть, ваше приложение считывает первые, скажем, 8 байт и ищет определенное 64-битное значение, которое идентифицирует его как сценарий.
Дополнительно, вы должны включить в свой External API методы, позволяющие вашим скриптам идентифицировать себя. Например, сценарий должен иметь возможность сообщить приложению через External API такие вещи, как Name, Description, Capabilities, Expectations, по существу информируя приложение о том, что он собой представляет и что он будет делать.
Я не вижу проблемы во встраивании Python, например, с помощью Boost.Python. Вы получите все, о чем просите:
Я имею в виду, что вам все равно придется раскрыть и реализовать API, но 1) это хорошая вещь, 2) Blender тоже это делает и 3) я действительно не могу придумать другого способа, который отвлечет вас от этой работы...
PS: У меня мало опыта с Python / Boost.Python, но я много работал с Lua / LuaBind, что вроде как то же самое
.Если вы действительно хотите быть максимально безболезненным для вас и ваших пользователей, подумайте о расширении python, а не о встраивании.
Встраивание действительно ничего не дает вам и вашим пользователям.