Identificando o relacionamento de dependência para pacotes python instalados com pip


151

Quando faço um congelamento de pip, vejo um grande número de pacotes Python que não instalei explicitamente, por exemplo

$ 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.)

Existe uma maneira de determinar por que o pip instalou esses pacotes dependentes específicos? Em outras palavras, como determino o pacote pai que tinha esses pacotes como dependências?

Por exemplo, talvez eu queira usar o Twisted e não quero depender de um pacote até saber mais sobre não desinstalá-lo ou atualizá-lo acidentalmente.

Respostas:


180

Você pode tentar o pipdeptree, que exibe dependências como uma estrutura em árvore, por exemplo:

$ 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

Para executá-lo:

pip install pipdeptree


EDIT: como observado por @Esteban nos comentários, você também pode listar a árvore ao contrário com -rou para um único pacote -p <package_name>, para encontrar o Werkzeug instalado que você pode executar:

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

6
Acredito que para responder totalmente à pergunta de @mark, você precisaria executar: pipdeptree -r "Mostra a árvore de dependência de maneira inversa, ou seja, as subdependências são listadas com a lista de pacotes que precisam deles sob eles".
Esteban

Como você pode visualizar a árvore reversa de todos os pacotes PyPi, não apenas dos pacotes instalados localmente?
Tijme

2
pipdeptreeé ótimo. Infelizmente, ele não parece levar em conta as dependências dos pacotes instalados pelo conda: por exemplo, em um ambiente conda onde matplotlibe numpyforam instalados usando o pip, mas scipyfoi instalado usando o conda, scipyaparece no pipdeptree como não possuindo dependências nem dependentes (também pip show scipynão mostra requisitos).
djvg 27/09/18

@ Dennis Eu não tentei, mas este trabalho poder para Conda github.com/rvalieris/conda-tree
djsutho

1
Para usar isso em um ambiente virtual, você precisa fazer o python -m pipdeptreecontrário (mesmo quando o executável está instalado na virtualenv), ele lista apenas as dependências do sistema.
Zim

81

O pip showcomando mostrará quais pacotes são necessários para o pacote especificado (observe que o pacote especificado já deve estar instalado):

$ pip show specloud

Package: specloud
Version: 0.4.4
Requires:
nose
figleaf
pinocchio

pip show foi introduzido na versão 1.4rc5 do pip


1
pip showfoi introduzido na versão 1.4rc5, e está presente no (atual como da escrita) 1.4.1
drevicko

10
Isso não responde exatamente à minha pergunta, porque mostra os filhos (dependências) de um pacote específico, em vez dos pais. Mas é fácil o bastante reunir algo para verificar as dependências de cada pacote, usando este comando. Assim, por exemplo, eu poderia determinar qual pacote instalado exigia o PyYAML.
Mark Chackerian

4
Conforme meu comentário anterior, este comando do shell elimina todas as dependências de cada um dos meus pacotes instalados: $ pip freeze | grep -v "\ -e" | sed s /\=\=.*// | awk 'system ("pip show" $ 1)'
Mark Chackerian em 21/02

Uma versão atualizada do script do meu comentário anterior é pip freeze | grep -v "\-e" | sed s/\=\=.*// | awk 'system("pip show " $1)' | grep -E '^(Name:|Requires:)' | sed s/Name:/\\\nName:/ - mas parece que o pipdeptree agora é uma solução melhor.
Mark Chackerian

14

Como eu disse recentemente em um thread hn , vou recomendar o seguinte:

Tenha um requirements.txtarquivo comentado com suas principais dependências:

## this is needed for whatever reason
package1

Instale suas dependências: pip install -r requirements.txt. Agora você obtém a lista completa de suas dependências com 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

Isso permite que você mantenha sua estrutura de arquivos com comentários, separando bem suas dependências das dependências de suas dependências. Dessa forma, você terá um tempo muito melhor no dia em que precisar remover um deles :)

Observe o seguinte:

  • Você pode ter uma limpeza requirements.rawcom o controle de versão para reconstruir sua totalidade requirements.txt.
  • Cuidado com os URLs git sendo substituídos por nomes de ovos no processo.
  • As dependências de suas dependências ainda são classificadas em ordem alfabética, para que você não saiba diretamente qual foi exigido por qual pacote, mas neste momento você realmente não precisa dele.
  • Use pip install --no-install <package_name>para listar requisitos específicos.
  • Use virtualenv se não o fizer.

1
Só não entendo por que isso pip freeze -r requirements.txtnão é amplamente usado. Muito útil para manter as dependências e subdependências.
Penkey Suresh

1
nota menor: pip installnão suporta mais --no-install.
ryan

7

Você também pode usar um comando de uma linha que canaliza os pacotes nos requisitos para o pip show.

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

1
Geralmente você não pode, pois o formato de requirements.txt é mais complexo que <package_name>==<package_version>.
Piotr Dobrogost

3

Antes de tudo, pip freezeexibe todos os pacotes atualmente instalados Python, não necessariamente usando o PIP.

Em segundo lugar, os pacotes Python contêm as informações sobre pacotes dependentes, bem como as versões necessárias . Você pode ver as dependências de um pacote específico usando os métodos descritos aqui . Ao atualizar um pacote, o script do instalador, como o PIP, manipulará a atualização de dependências para você.

Para resolver a atualização de pacotes, recomendo o uso de arquivos de requisitos PIP . Você pode definir quais pacotes e versões você precisa e instalá-los imediatamente, usando a instalação do pip.


3

Use pipupgrade !

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

O pipupgrade exibe um gráfico de dependência e destaca cada pacote para uma possível atualização (com base no controle de versão semântico). Ele também exibe dependências filho conflitantes de uma maneira bonita. pipupgradetambém garante a atualização dos pacotes presentes em vários ambientes Python. Compatível com Python2.7 +, Python3.4 + e pip9 +, pip10 +, pip18 +, pip19 +.

insira a descrição da imagem aqui


1

(solução alternativa, resposta não verdadeira)

Tive o mesmo problema, com o lxml não sendo instalado e eu querendo saber quem precisava do lxml. Não quem lxml precisava . Acabou ignorando o problema por.

  1. observando onde os pacotes do meu site estavam sendo colocados.

  2. vá lá e grep recursivo para a importação (o último --invert-match do grep serve para remover os próprios arquivos do lxml da consideração).

Sim, não é uma resposta sobre como usar o pip para fazer isso, mas não obtive sucesso com as sugestões aqui, por qualquer motivo.

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

1

Eu escrevi um script rápido para resolver esse problema. O script a seguir exibirá os pacotes pai (dependentes) de qualquer pacote especificado. Dessa forma, você pode ter certeza de que é seguro atualizar ou instalar qualquer pacote específico. Pode ser usado da seguinte maneira: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)))

Isso não funciona mais porque o get_installed_distributions()método não está mais disponível. github.com/pypa/pip/issues/5243
Phil Gyford
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.