Определение отношения зависимости для пакетов Python, установленных с помощью pip


151

Когда я делаю стоп-кадр, я вижу большое количество пакетов Python, которые я не устанавливал явно, например

$ pip freeze
Cheetah==2.4.3
GnuPGInterface==0.3.2
Landscape-Client==11.01
M2Crypto==0.20.1
PAM==0.4.2
PIL==1.1.7
PyYAML==3.09
Twisted-Core==10.2.0
Twisted-Web==10.2.0
(etc.)

Есть ли способ определить, почему pip установил эти зависимые пакеты? Другими словами, как определить родительский пакет, в котором эти пакеты были зависимостями?

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

Ответы:


180

Вы можете попробовать pipdeptree, который отображает зависимости в виде древовидной структуры, например:

$ pipdeptree
Lookupy==0.1
wsgiref==0.1.2
argparse==1.2.1
psycopg2==2.5.2
Flask-Script==0.6.6
  - Flask [installed: 0.10.1]
    - Werkzeug [required: >=0.7, installed: 0.9.4]
    - Jinja2 [required: >=2.4, installed: 2.7.2]
      - MarkupSafe [installed: 0.18]
    - itsdangerous [required: >=0.21, installed: 0.23]
alembic==0.6.2
  - SQLAlchemy [required: >=0.7.3, installed: 0.9.1]
  - Mako [installed: 0.9.1]
    - MarkupSafe [required: >=0.9.2, installed: 0.18]
ipython==2.0.0
slugify==0.0.1
redis==2.9.1

Чтобы запустить это:

pip install pipdeptree


РЕДАКТИРОВАТЬ: как отметил @Esteban в комментариях, вы также можете перечислить дерево в обратном порядке -rили для одного пакета, -p <package_name>чтобы найти, какой установленный Werkzeug вы можете запустить:

$ pipdeptree -r -p Werkzeug
Werkzeug==0.11.15
  - Flask==0.12 [requires: Werkzeug>=0.7]

6
Я полагаю, что для полного ответа на вопрос @mark вам нужно будет выполнить: pipdeptree -r «Показывает дерево зависимостей в обратном порядке, т.е. подчиненные зависимости перечислены со списком пакетов, которым они нужны».
Эстебан

Как вы можете просмотреть обратное дерево для всех пакетов PyPi, а не только для локально установленных пакетов?
Tijme

2
pipdeptreeотлично. К сожалению, похоже, что он не учитывает зависимости для пакетов, установленных conda: например, в среде conda, где matplotlibи numpyбыли установлены с использованием pip, но scipyбыли установлены с использованием conda, scipyв pipdeptree отображаются как не имеющие и не имеющие зависимостей (также pip show scipyпоказывает, что требования).
DJVG

@Dennis Я не пробовал, но это может сработать для conda github.com/rvalieris/conda-tree
djsutho

1
Чтобы использовать это в виртуальной среде, вы должны сделать python -m pipdeptreeиначе (даже если исполняемый файл установлен в virtualenv), он только перечисляет системные зависимости.
Зим

81

Команда pip showпокажет, какие пакеты требуются для указанного пакета (обратите внимание, что указанный пакет уже должен быть установлен):

$ pip show specloud

Package: specloud
Version: 0.4.4
Requires:
nose
figleaf
pinocchio

pip show был представлен в версии 1.4rc5


1
pip showбыл представлен в версии 1.4rc5 и присутствует в (текущем на момент написания) 1.4.1
drevicko

10
Это не дает точного ответа на мой вопрос, потому что показывает детей (зависимости) для определенного пакета, а не родителей. Но с помощью этой команды достаточно просто собрать что-то вместе, чтобы проверить зависимости каждого пакета. Так, например, я мог определить, какой установленный пакет требует PyYAML.
Марк Чакериан

4
Согласно моему предыдущему комментарию, эта команда оболочки выводит все зависимости для каждого из моих установленных пакетов: $ pip freeze | grep -v "\ -e" | sed s /\=\=.*// | awk 'system ("pip show" $ 1) "
Марк Чакерян

Обновленная версия скрипта из моего предыдущего комментария pip freeze | grep -v "\-e" | sed s/\=\=.*// | awk 'system("pip show " $1)' | grep -E '^(Name:|Requires:)' | sed s/Name:/\\\nName:/ - но кажется, что pipdeptree - теперь лучшее решение.
Марк Чакериан

14

Как я недавно сказал в теме hn , я рекомендую следующее:

Получите закомментированный requirements.txtфайл с вашими основными зависимостями:

## this is needed for whatever reason
package1

Установите ваши зависимости: pip install -r requirements.txt. Теперь вы получите полный список ваших зависимостей с pip freeze -r requirements.txt:

## this is needed for whatever reason
package1==1.2.3

## The following requirements were added by pip --freeze:
package1-dependency1==1.2.3
package1-dependency1==1.2.3

Это позволяет вам сохранять структуру файлов с комментариями, хорошо отделяя ваши зависимости от зависимостей ваших зависимостей. Таким образом, у вас будет намного приятнее время, когда вам нужно удалить одного из них :)

Обратите внимание на следующее:

  • Вы можете иметь чистую requirements.rawс контролем версий, чтобы восстановить свою полную requirements.txt.
  • Остерегайтесь мерзких URL, которые в процессе заменяются именами яиц.
  • Зависимости ваших зависимостей по-прежнему отсортированы в алфавитном порядке, поэтому вы не знаете, какая именно требуется для какого пакета, но на данный момент вам это не нужно.
  • Используйте pip install --no-install <package_name>для перечисления конкретных требований.
  • Используйте virtualenv, если нет.

1
Я просто не понимаю, почему это pip freeze -r requirements.txtне так широко используется. Очень полезно для поддержания зависимостей и подчиненных зависимостей.
Пенки Суреш

1
незначительное примечание: pip installбольше не поддерживает --no-install.
Райан

7

Вы также можете использовать однострочную команду, которая передает пакеты в требованиях к pip-шоу.

cut -d'=' -f1 requirements.txt | xargs pip show

1
Как правило, вы не можете, так как формат require.txt более сложный, чем <package_name>==<package_version>.
Петр Доброгост

3

Прежде всего pip freezeотображает все установленные на данный момент пакеты Python, необязательно использующие PIP.

Во-вторых, пакеты Python содержат информацию о зависимых пакетах, а также необходимые версии . Вы можете увидеть зависимости конкретного pkg, используя методы, описанные здесь . Когда вы обновляете пакет, установочный скрипт, такой как PIP, будет обрабатывать обновление зависимостей для вас.

Для решения проблемы обновления пакетов я рекомендую использовать файлы требований PIP . Вы можете определить, какие пакеты и версии вам нужны, и установить их сразу, используя pip install.


3

Используйте pipupgrade !

$ pip install pipupgrade
$ pipupgrade --format tree --all --check

pipupgrade отображает график зависимости и выделяет каждый пакет для возможного обновления (на основе семантического контроля версий). Он также отображает конфликтующие дочерние зависимости красивым способом. pipupgradeтакже обеспечивает обновление пакетов, представленных в нескольких средах Python. Совместим с Python2.7 +, Python3.4 + и pip9 +, pip10 +, pip18 +, pip19 +.

введите описание изображения здесь


1

(обходной путь, не верный ответ)

У меня была такая же проблема: lxml не устанавливался, и я хотел знать, кому нужен lxml. Не кому нужен lxml . Закончилось обходом вопроса.

  1. отмечая, где мои пакеты сайта были помещены.

  2. перейдите туда и рекурсивный grep для импорта (последний - grep --invert-match служит для удаления из рассмотрения собственных файлов lxml).

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

 site-packages me$ egrep -i --include=*.py  -r -n lxml . | grep import | grep --invert-match /lxml/

1

Я написал быстрый сценарий, чтобы решить эту проблему. Следующий скрипт отобразит родительский (зависимый) пакет (ы) для любого данного пакета. Таким образом, вы можете быть уверены, что безопасно обновить или установить какой-либо конкретный пакет. Может использоваться следующим образом:dependants.py PACKAGENAME

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Find dependants of a Python package"""

import logging
import pip
import pkg_resources
import sys

__program__ = 'dependants.py'


def get_dependants(target_name):
    for package in pip._internal.utils.misc.get_installed_distributions():
        for requirement_package in package.requires():
            requirement_name = requirement_package.project_name
            if requirement_name == target_name:
                yield package.project_name


# configure logging
logging.basicConfig(format='%(levelname)s: %(message)s',
                    level=logging.INFO)

try:
    target_name = sys.argv[1]
except IndexError:
    logging.error('missing package name')
    sys.exit(1)

try:
    pkg_resources.get_distribution(target_name)
except pkg_resources.DistributionNotFound:
    logging.error("'%s' is not a valid package", target_name)
    sys.exit(1)

print(list(get_dependants(target_name)))

Это больше не работает, потому что get_installed_distributions()метод больше не доступен. github.com/pypa/pip/issues/5243
Фил Гифорд,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.