Identification de la relation de dépendance pour les packages python installés avec pip


151

Quand je fais un pip freeze, je vois un grand nombre de packages Python que je n'ai pas installés explicitement, par exemple

$ 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-t-il un moyen pour moi de déterminer pourquoi pip a installé ces packages dépendants particuliers? En d'autres termes, comment déterminer le package parent qui avait ces packages comme dépendances?

Par exemple, je pourrais vouloir utiliser Twisted et je ne veux pas dépendre d'un paquet jusqu'à ce que j'en sache plus sur le fait de ne pas le désinstaller ou le mettre à niveau accidentellement.

Réponses:


180

Vous pouvez essayer pipdeptree qui affiche les dépendances sous forme d'arborescence, par exemple:

$ 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

Pour le faire fonctionner:

pip install pipdeptree


EDIT: comme indiqué par @Esteban dans les commentaires, vous pouvez également lister l'arborescence à l'envers avec -rou pour un seul paquet avec -p <package_name>afin de trouver ce que Werkzeug installé vous pouvez exécuter:

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

6
Je crois que pour répondre pleinement à la question de @mark, vous devrez exécuter: pipdeptree -r "Affiche l'arborescence des dépendances de manière inverse, c'est-à-dire que les sous-dépendances sont répertoriées avec la liste des paquets qui en ont besoin sous elles."
Esteban le

Comment afficher l'arborescence inversée de tous les packages PyPi, et pas uniquement des packages installés localement?
Tijme

2
pipdeptreec'est génial. Malheureusement, il ne semble pas prendre en compte les dépendances pour les packages installés par conda: par exemple dans un environnement conda où matplotlibet numpyont été installés à l'aide de pip, mais a scipyété installé à l'aide de conda, scipyapparaît dans le pipdeptree comme n'ayant pas de dépendances et pas de dépendants ( pip show scipymontre également non exigences).
djvg le

@Dennis Je ne l'ai pas essayé mais cela pourrait fonctionner pour conda github.com/rvalieris/conda-tree
djsutho

1
Pour l'utiliser dans un environnement virtuel, vous devez faire python -m pipdeptreeautrement (même lorsque l'exécutable est installé sur le virtualenv), il ne répertorie que les dépendances système.
Zim

81

La pip showcommande affichera quels packages sont requis pour le package spécifié (notez que le package spécifié doit déjà être installé):

$ pip show specloud

Package: specloud
Version: 0.4.4
Requires:
nose
figleaf
pinocchio

pip show a été introduit dans la version 1.4rc5 de pip


1
pip showa été introduit dans la version 1.4rc5, et est présent dans le (actuel au moment de la rédaction) 1.4.1
drevicko

10
Cela ne répond pas exactement à ma question, car il montre les enfants (dépendances) pour un package spécifique, au lieu des parents. Mais il est assez facile de jeter quelque chose ensemble pour vérifier les dépendances de chaque paquet, en utilisant cette commande. Ainsi, par exemple, je pourrais déterminer quel package installé nécessitait PyYAML.
Mark Chackerian

4
Selon mon commentaire précédent, cette commande shell vide toutes les dépendances pour chacun de mes packages installés: $ pip freeze | grep -v "\ -e" | sed s /\=\=.*// | awk 'system ("pip show" $ 1)'
Mark Chackerian

Une version mise à jour du script de mon commentaire précédent est pip freeze | grep -v "\-e" | sed s/\=\=.*// | awk 'system("pip show " $1)' | grep -E '^(Name:|Requires:)' | sed s/Name:/\\\nName:/ - mais il semble que pipdeptree est maintenant une meilleure solution.
Mark Chackerian

14

Comme je l'ai récemment dit sur un fil hn , je recommande ce qui suit:

Avoir un requirements.txtfichier commenté avec vos principales dépendances:

## this is needed for whatever reason
package1

Installer vos dépendances: pip install -r requirements.txt. Vous obtenez maintenant la liste complète de vos dépendances avec 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

Cela vous permet de conserver la structure de vos fichiers avec des commentaires, en séparant bien vos dépendances des dépendances de vos dépendances. De cette façon, vous aurez un temps beaucoup plus agréable le jour où vous devrez supprimer l'un d'eux :)

Notez ce qui suit:

  • Vous pouvez avoir un nettoyage requirements.rawavec le contrôle de version pour reconstruire votre fichier complet requirements.txt.
  • Méfiez-vous des URL git remplacées par des noms d'oeuf dans le processus.
  • Les dépendances de vos dépendances sont toujours triées par ordre alphabétique, vous ne savez donc pas directement laquelle était requise par quel package, mais à ce stade, vous n'en avez pas vraiment besoin.
  • Utilisez pip install --no-install <package_name>pour lister les exigences spécifiques.
  • Utilisez virtualenv si vous ne le faites pas.

1
Je ne comprends tout simplement pas pourquoi ce pip freeze -r requirements.txtn'est pas largement utilisé. Très utile pour maintenir les dépendances et sous-dépendances.
Penkey Suresh

1
note mineure: pip installne prend plus en charge --no-install.
ryan

7

Vous pouvez également utiliser une commande d'une ligne qui redirige les packages des exigences vers pip show.

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

1
Généralement, vous ne pouvez pas car le format de requirements.txt est plus complexe que <package_name>==<package_version>.
Piotr Dobrogost

3

Tout d'abord pip freezeaffiche tous les packages Python actuellement installés, n'utilisant pas nécessairement PIP.

Deuxièmement, les packages Python contiennent les informations sur les packages dépendants ainsi que les versions requises . Vous pouvez voir les dépendances de pkg particuliers en utilisant les méthodes décrites ici . Lorsque vous mettez à niveau un package, le script d'installation comme PIP gérera la mise à niveau des dépendances pour vous.

Pour résoudre la mise à jour des packages, je recommande d'utiliser les fichiers d'exigences PIP . Vous pouvez définir les packages et les versions dont vous avez besoin et les installer à la fois à l'aide de pip install.


3

Utilisez pipupgrade !

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

pipupgrade affiche un graphique de dépendance et met en évidence chaque package pour une éventuelle mise à jour (basée sur le versionnage sémantique). Il affiche également les dépendances enfants en conflit d'une jolie manière. pipupgradeassure également la mise à niveau des packages présents dans plusieurs environnements Python. Compatible avec Python2.7 +, Python3.4 + et pip9 +, pip10 +, pip18 +, pip19 +.

entrez la description de l'image ici


1

(solution de contournement, pas vraie réponse)

J'ai eu le même problème, avec lxml ne s'installant pas et moi voulant savoir qui avait besoin de lxml. Pas de qui lxml avait besoin . J'ai fini par contourner le problème.

  1. en notant où mes packages de site étaient placés.

  2. allez-y et grep récursif pour l'importation (le dernier --invert-match de grep sert à supprimer les propres fichiers de lxml).

Oui, pas de réponse sur la façon d'utiliser pip pour le faire, mais je n'ai pas obtenu de succès avec les suggestions ici, pour une raison quelconque.

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

1

J'ai écrit un script rapide pour résoudre ce problème. Le script suivant affichera le (s) package (s) parent (s) pour tout package donné. De cette façon, vous pouvez être sûr qu'il est sûr de mettre à niveau ou d'installer un package particulier. Il peut être utilisé comme suit: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)))

Cela ne fonctionne plus car la get_installed_distributions()méthode n'est plus disponible. github.com/pypa/pip/issues/5243
Phil Gyford
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.