Identyfikowanie relacji zależności dla pakietów Pythona zainstalowanych z pip

151

Kiedy robię zamrożenie pip, widzę dużą liczbę pakietów Pythona, których nie zainstalowałem jawnie, np

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

Czy istnieje sposób, aby określić, dlaczego pip zainstalował te konkretne pakiety zależne? Innymi słowy, jak określić pakiet nadrzędny, który miał te pakiety jako zależności?

Na przykład mogę chcieć użyć Twisted i nie chcę polegać na pakiecie, dopóki nie dowiem się więcej o tym, jak go przypadkowo nie odinstalować lub zaktualizować.

Mark Chackerian
źródło

Odpowiedzi:

180

Możesz wypróbować pipdeptree, który wyświetla zależności jako strukturę drzewa, np .:

$ 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

Aby to uruchomić:

pip install pipdeptree


EDYCJA: jak zauważył @Esteban w komentarzach, możesz również wyświetlić drzewo w odwrotnej kolejności z -rlub dla pojedynczego pakietu za pomocą -p <package_name>tak, aby znaleźć zainstalowany Werkzeug, który możesz uruchomić:

$ pipdeptree -r -p Werkzeug
Werkzeug==0.11.15
  - Flask==0.12 [requires: Werkzeug>=0.7]
djsutho
źródło
6
Uważam, że aby w pełni odpowiedzieć na pytanie @mark, które należy uruchomić: pipdeptree -r „Pokazuje drzewo zależności w odwrotnej kolejności, tj. Pod-zależności są wymienione wraz z listą pakietów, które ich potrzebują”.
Esteban
Jak wyświetlić odwrotne drzewo dla wszystkich pakietów PyPi, a nie tylko pakietów zainstalowanych lokalnie?
Tijme,
2
pipdeptreejest świetne. Niestety wydaje się, że nie bierze pod uwagę zależności pakietów zainstalowanych przez conda: np. W conda env, gdzie matplotlibi numpyzostały zainstalowane przy użyciu pip, ale scipyzostało zainstalowane przy użyciu conda, scipypojawia się w pipdeptree jako bez zależności i bez zależności (również pip show scipypokazuje brak wymagania).
djvg
@Dennis Nie próbowałem tego, ale to może zadziałać w przypadku conda github.com/rvalieris/conda-tree
djsutho
1
Aby użyć tego w środowisku wirtualnym, musisz zrobić python -m pipdeptreeinaczej (nawet jeśli plik wykonywalny jest zainstalowany w virtualenv), wyświetla tylko zależności systemowe.
Zim
81

pip showKomenda pokaże jakie pakiety są wymagane dla określonego pakietu (zauważ, że określony pakiet musi być zainstalowany):

$ pip show specloud

Package: specloud
Version: 0.4.4
Requires:
nose
figleaf
pinocchio

pip show został wprowadzony w wersji pip 1.4rc5

BernardoBarreto
źródło
1
pip showzostał wprowadzony w wersji 1.4rc5 i jest obecny w (aktualnym w momencie pisania) 1.4.1
drevicko
10
To nie odpowiada dokładnie na moje pytanie, ponieważ pokazuje dzieci (zależności) dla określonego pakietu, zamiast rodziców. Ale łatwo jest wrzucić coś razem, aby sprawdzić zależności każdego pakietu, używając tego polecenia. Na przykład mógłbym określić, który zainstalowany pakiet wymaga PyYAML.
Mark Chackerian,
4
Zgodnie z moim poprzednim komentarzem, to polecenie powłoki zrzuca wszystkie zależności dla każdego z moich zainstalowanych pakietów: $ pip freeze | grep -v "\ -e" | sed s /\=\=.*// | awk 'system ("pip show" $ 1)'
Mark Chackerian,
Zaktualizowana wersja skryptu z mojego poprzedniego komentarza to pip freeze | grep -v "\-e" | sed s/\=\=.*// | awk 'system("pip show " $1)' | grep -E '^(Name:|Requires:)' | sed s/Name:/\\\nName:/ - ale wydaje się, że pipdeptree jest teraz lepszym rozwiązaniem.
Mark Chackerian
14

Jak niedawno powiedziałem w wątku hn , polecam:

Przygotuj skomentowany requirements.txtplik z głównymi zależnościami:

## this is needed for whatever reason
package1

Instalować zależności: pip install -r requirements.txt. Teraz masz pełną listę swoich zależności z 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

Pozwala to zachować strukturę plików z komentarzami, ładnie oddzielając zależności od zależności zależności. W ten sposób będziesz mieć dużo przyjemniejszy czas w dniu, w którym musisz usunąć jeden z nich :)

Zwróć uwagę na następujące kwestie:

  • Możesz mieć czysty requirements.rawz kontrolą wersji, aby odbudować pełną wersję requirements.txt.
  • Uważaj, aby w trakcie tego procesu adresy URL git zostały zastąpione nazwami jaj.
  • Zależności twoich zależności są nadal posortowane alfabetycznie, więc nie wiesz bezpośrednio, która z nich była wymagana przez który pakiet, ale w tym momencie tak naprawdę nie potrzebujesz tego.
  • Służy pip install --no-install <package_name>do wyświetlania określonych wymagań.
  • Użyj virtualenv, jeśli nie.
Maxime R.
źródło
1
Po prostu nie rozumiem, dlaczego pip freeze -r requirements.txtnie jest to powszechnie używane. Bardzo przydatne do utrzymywania zależności i zależności podrzędnych.
Penkey Suresh
1
drobna uwaga: pip installjuż nie obsługuje --no-install.
ryan
7

Możesz także użyć polecenia jednowierszowego, które przesyła pakiety w wymaganiach do programu pip show.

cut -d'=' -f1 requirements.txt | xargs pip show
biophetik
źródło
1
Generalnie nie jest to możliwe, ponieważ format Require.txt jest bardziej złożony niż <package_name>==<package_version>.
Piotr Dobrogost
3

Przede wszystkim pip freezewyświetla wszystkie aktualnie zainstalowane pakiety Python, niekoniecznie używając PIP.

Po drugie, pakiety Pythona zawierają informacje o pakietach zależnych, a także o wymaganych wersjach . Zależności poszczególnych pakietów można zobaczyć, korzystając z metod opisanych tutaj . Podczas aktualizacji pakietu skrypt instalacyjny, taki jak PIP, zajmie się aktualizacją zależności za Ciebie.

Aby rozwiązać aktualizację pakietów, zalecam używanie plików wymagań PIP . Możesz zdefiniować potrzebne pakiety i wersje i zainstalować je od razu za pomocą pip install.

Mariusz Jamro
źródło
3

Użyj pipupgrade !

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

pipupgrade wyświetla wykres zależności i wyróżnia każdy pakiet pod kątem możliwej aktualizacji (na podstawie wersji semantycznej). W ładny sposób wyświetla również sprzeczne zależności potomne. pipupgradezapewnia również aktualizację pakietów obecnych w wielu środowiskach Pythona. Kompatybilny z Python2.7 +, Python3.4 + i pip9 +, pip10 +, pip18 +, pip19 +.

wprowadź opis obrazu tutaj

Achilles Gasper Rasquinha
źródło
1

(obejście, nieprawda odpowiedź)

Miałem ten sam problem, kiedy lxml się nie instalował, a ja chciałem wiedzieć, kto potrzebuje lxml. Nie kto potrzebował LXML . Skończyło się na omijaniu problemu przez.

  1. odnotowując, gdzie są umieszczane moje pakiety witryny.

  2. idź tam i rekurencyjnie grep do importu (ostatnie polecenie --invert-match grepa służy do usunięcia z rozważenia własnych plików lxml).

Tak, nie jest to odpowiedź na pytanie, jak używać pip do tego celu, ale z jakiegoś powodu nie odniosłem żadnego sukcesu z przedstawionych tutaj sugestii.

 site-packages me$ egrep -i --include=*.py  -r -n lxml . | grep import | grep --invert-match /lxml/
JL Peyret
źródło
1

Napisałem szybki skrypt, aby rozwiązać ten problem. Poniższy skrypt wyświetli pakiet nadrzędny (zależny) dla danego pakietu. W ten sposób możesz mieć pewność, że aktualizacja lub instalacja dowolnego konkretnego pakietu jest bezpieczna. Można go używać w następujący sposób: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)))
Sześć
źródło
To już nie działa, ponieważ get_installed_distributions()metoda nie jest już dostępna. github.com/pypa/pip/issues/5243
Phil Gyford