Как установить отсутствующий пакет python из script, который ему нужен?

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

Из того, что я знаю, pip также является модулем python, поэтому решение должно выглядеть так:

try:
    import zumba
except ImportError:
    import pip
    # ... do "pip install --user zumba" or throw exception   <-- how?
    import zumba

То, что мне не хватает, это сделать "pip install --user zumba" изнутри python, я не хочу делать это с помощью os.system(), так как это может создать другие проблемы.

Я предполагаю, что это возможно...

Ответ 1

Обновлено для более новой версии пипса (> = 10.0):

try:
    import zumba
except ImportError:
    from pip._internal import main as pip
    pip(['install', '--user', 'zumba'])
    import zumba

Благодаря @Joop я смог придумать правильный ответ.

try:
    import zumba
except ImportError:
    import pip
    pip.main(['install', '--user', 'zumba'])
    import zumba

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

Не уверен, что он будет работать для двоичных модулей или модулей, требующих компиляции, но он явно работает хорошо для модулей с чистым питоном.

Теперь вы можете писать автономные скрипты и не беспокоиться о зависимостях.

Ответ 2

Начиная с версии pip> = 10.0.0, вышеприведенные решения не будут работать из-за внутренней реструктуризации пакетов. Новый способ использования pip внутри скрипта теперь выглядит следующим образом:

try: import abc
except ImportError:
    from pip._internal import main as pip
    pip(['install', '--user', 'abc'])
    import abc

Ответ 3

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

Лучший способ:

import pip

packages = [package.project_name for package in pip.get_installed_distributions()]

if 'package' not in packages:
    pip.main(['install', 'package'])