Я не думаю, что это когда-нибудь будет поддержано. GDI + очень специфичен для платформы Windows. Если вам нужна библиотека рисунков, вы можете взглянуть на SkiaSharp, который поддерживается Xamarin.
Вы можете использовать функцию __import__
для динамического импорта модуля, используя имя строки, переданное в командной строке.
mod = sys.argv[1]
command =__import__(mod)
# assuming your pattern has a run method defined.
command.run()
Обработка ошибок и т. Д. Оставлена в качестве упражнения для читателя
Редактировать: Это будет зависеть от пользовательских плагинов, устанавливаемых через pip. Если вы хотите, чтобы пользователи добавляли плагины в папку без установки, вам нужно добавить эту папку в путь к Python.
Самый простой ответ: если все мои команды находятся в foo.commands
:
import foo.commands
import importlib
for importer, modname, ispkg in pkgutil.iter_modules(foo.commands.__path__):
mod=importlib.import_module('foo.commands.' + cmd)
mod.run()
Это запустит все подкоманды. (Ну, в реальном коде я буду запускать только один. Это руководство.)
Хотя этот вопрос на самом деле довольно широк, в типовой установке Python по умолчанию (например, с setuptools
) доступно достаточно инструментов, чтобы это было относительно достижимо, в том смысле, что оно действительно расширяемо, так что можно создавать другие пакеты / установлен таким образом, чтобы предоставлять новые обнаруживаемые подкоманды для вашей основной программы.
Ваш базовый пакет может предоставить стандартную точку входа в виде console_scripts
, которая указывает на вашу точку входа, которая будет передавать все аргументы в экземпляр некоторого парсера аргументов (например, argparse
] ), и какой-то реестр, который вы можете реализовать по схеме, аналогичной console_scripts
, за исключением вашей конкретной группы entry_points, чтобы она перебирала каждую запись и создавала объекты, которые также предоставляют свои собственные ArgumentParser
. ] случаи, когда ваша основная точка входа будет динамически регистрироваться как подкоманда, показывая тем самым вашим пользователям, какие подкоманды действительно доступны и на что может быть похож их вызов.
Чтобы привести пример, в setup.py
вашего основного пакета может быть такая запись, как
setup(
name='my.package',
# ...
entry_points={
'console_scripts': [
'topcmd = my.package.runtime:main',
],
'my.package.subcmd': [
'subcmd1 = my.package.commands:subprog1',
'subcmd2 = my.package.commands:subprog2',
],
},
# ...
)
Внутри исходного файла my/package/runtime.py
метод main
должен будет сконструировать новый Экземпляр ArgumentParser, и, повторяя точки входа, предоставленные pkg_resources.working_set
, например:
from pkg_resources import working_set
def init_parser(argparser): # pass in the argparser provided by main
commands = argparser.add_subparsers(dest='command')
for entry_point in working_set.iter_entry_points('my.package.subcmd'):
subparser = commands.add_parser(entry_point.name)
# load can raise exception due to missing imports or error in object creation
subcommand = entry_point.load()
subcommand.init_parser(subparser)
Итак, в функции main
созданный экземпляр argparser может быть передан в функцию, подобную той, что в выше, и точка входа 'subcmd1 = my.package.commands:subprog1'
будет загружена. Внутри my/package/command.py
должен быть реализован реализованный метод init_parser
, который возьмет предоставленный подпарапетер и заполнит его необходимыми аргументами:
class SubProgram1(object):
def init_parser(self, argparser)
argparser.add_argument(...)
subprog1 = SubProgram1()
О, еще одна заключительная вещь, после передачи аргументов Основное argparser.parse_args(...)
название команды предоставляется argparser.command
. Должна быть возможность изменить это на фактический экземпляр, но это может или не может достичь того, чего вы точно хотите (потому что основная программа может захотеть выполнить дальнейшую работу / проверку перед фактическим использованием команды). Эта часть является еще одной сложной частью, но, по крайней мере, анализатор аргументов должен содержать информацию, необходимую для запуска правильной подпрограммы.
Естественно, это включает в себя абсолютно отсутствие проверки ошибок, и она должна быть реализована в некоторой форме, чтобы предотвратить сбои в работе основных программ из-за неисправных классов подкоманд. Я использовал такой шаблон (хотя и с гораздо более сложной реализацией), который может поддерживать произвольное количество вложенных подкоманд. Также пакеты, которые хотят реализовать пользовательские команды, могут просто добавить свою собственную запись в группу точек входа (в данном случае, в my.package.subcmd
) для своих собственных setup.py
. Например:
setup(
name="some.other.package",
# ...
entry_points={
'my.package.subcmd': [
'extracmd = some.other.package.commands:extracmd',
],
},
# ...
)
Приложение:
В соответствии с запросом, фактическая реализация, используемая в производстве, находится в пакете ( quietjs ), который я сейчас поддерживаю. Установка этого пакета (в virtualenv) и запуск calmjs
в командной строке должны отображать список подкоманд, идентичных записям, определенным в точках входа основного пакета . Установка дополнительного пакета, который расширяет функциональность (например, quietjs.webpack ) и повторный запуск calmjs
, теперь перечислит calmjs.webpack
в качестве дополнительной подкоманды.
Точки входа ссылаются на экземпляры подклассов к классу Runtime
, и в нем есть место, где добавляется подпарсер и, если он удовлетворяет требованиям регистрации (многие следующие утверждения относятся к различным ошибкам / проверка работоспособности, например, что делать, когда несколько пакетов определяют одно и то же имя подкоманды для экземпляров среды выполнения, регистрирует для экземпляра argparser в этом конкретном экземпляре среды выполнения и передается подпарамер в метод init_argparser
среды выполнения, которая инкапсулирует подкоманду. В качестве примера, подкомпонент подкоманды calmjs webpack
устанавливается с помощью его метода init_argparser
, и этот пакет регистрирует подкоманду webpack
в своей собственной setup.py
. (Чтобы поиграть с ними, просто используйте pip
для установки соответствующих пакетов).