Controlla se il pacchetto Python è installato


112

Qual è un buon modo per verificare se un pacchetto è installato all'interno di uno script Python? So che è facile dall'interprete, ma devo farlo all'interno di una sceneggiatura.

Immagino di poter controllare se c'è una directory sul sistema creata durante l'installazione, ma sento che c'è un modo migliore. Sto cercando di assicurarmi che il pacchetto Skype4Py sia installato e, in caso contrario, lo installerò.

Le mie idee per portare a termine il controllo

  • cercare una directory nel tipico percorso di installazione
  • prova a importare il pacchetto e se viene generata un'eccezione, installa il pacchetto

Scrivere uno script Python per automatizzare l'avvio di Skype e utilizzare tcpdump per raccogliere i dati dei pacchetti in modo da poter analizzare come funziona la rete quando si effettua una chiamata in conferenza.
Kevin

Risposte:


107

Se intendi uno script Python, fai qualcosa del genere:

Python 3.3+ usa sys.modules e find_spec :

import importlib.util
import sys

# For illustrative purposes.
name = 'itertools'

if name in sys.modules:
    print(f"{name!r} already in sys.modules")
elif (spec := importlib.util.find_spec(name)) is not None:
    # If you choose to perform the actual import ...
    module = importlib.util.module_from_spec(spec)
    sys.modules[name] = module
    spec.loader.exec_module(module)
    print(f"{name!r} has been imported")
else:
    print(f"can't find the {name!r} module")

Python 3:

try:
    import mymodule
except ImportError as e:
    pass  # module doesn't exist, deal with it.

Python 2:

try:
    import mymodule
except ImportError, e:
    pass  # module doesn't exist, deal with it.

6
Avvertimento: ho appena avuto una situazione oggi in cui ImportError è stato lanciato all'interno del modulo stesso. Ciò non dovrebbe accadere spesso, ma tieni presente che questo controllo non è affidabile in tutti i casi.
Koen Bok,

7
Questo non solo controlla ; lo importa anche; vedi anche stackoverflow.com/questions/14050281/…
Jorge Leitao

6
Sì. In genere è quello che vuoi. Puoi usare il modulo degli strumenti di importazione per fare un controllo più sofisticato, ma il più delle volte l'unica ragione per cui ti interessa se un modulo è installato è perché vuoi usarlo.
Christopher,

Questo fallirà se hai un altro file con lo stesso nome del pacchetto.
Sapphire_Brick

Certo, ma questo è un problema completamente diverso. Se hai due diversi moduli o pacchetti con lo stesso nome nel tuo percorso di ricerca, sei nei guai.
Christopher

56

Risposta aggiornata

Un modo migliore per farlo è:

import subprocess
import sys

reqs = subprocess.check_output([sys.executable, '-m', 'pip', 'freeze'])
installed_packages = [r.decode().split('==')[0] for r in reqs.split()]

Il risultato:

print(installed_packages)

[
    "Django",
    "six",
    "requests",
]

Controlla se requestsè installato:

if 'requests' in installed_packages:
    # Do something

Perché in questo modo? A volte si verificano conflitti di nomi di app. L'importazione dallo spazio dei nomi dell'app non fornisce un quadro completo di ciò che è installato nel sistema.

Nota, quella soluzione proposta funziona:

  • Quando si utilizza pip per installare da PyPI o da qualsiasi altra fonte alternativa (come pip install http://some.site/package-name.zipo qualsiasi altro tipo di archivio).
  • Durante l'installazione manuale utilizzando python setup.py install.
  • Quando si installa da repository di sistema, come sudo apt install python-requests.

Casi in cui potrebbe non funzionare:

  • Durante l'installazione in modalità di sviluppo, come python setup.py develop.
  • Durante l'installazione in modalità di sviluppo, come pip install -e /path/to/package/source/.

Vecchia risposta

Un modo migliore per farlo è:

import pip
installed_packages = pip.get_installed_distributions()

Per pip> = 10.x utilizzare:

from pip._internal.utils.misc import get_installed_distributions

Perché in questo modo? A volte si verificano conflitti di nomi di app. L'importazione dallo spazio dei nomi dell'app non fornisce un quadro completo di ciò che è installato nel sistema.

Di conseguenza, ottieni un elenco di pkg_resources.Distributionoggetti. Vedere quanto segue come esempio:

print installed_packages
[
    "Django 1.6.4 (/path-to-your-env/lib/python2.7/site-packages)",
    "six 1.6.1 (/path-to-your-env/lib/python2.7/site-packages)",
    "requests 2.5.0 (/path-to-your-env/lib/python2.7/site-packages)",
]

Fai un elenco:

flat_installed_packages = [package.project_name for package in installed_packages]

[
    "Django",
    "six",
    "requests",
]

Controlla se requestsè installato:

if 'requests' in flat_installed_packages:
    # Do something

2
Come accennato nell'altra risposta, è possibile che l'errore non venga rilevato. Pertanto penso che questa dovrebbe essere la risposta accettata.
Jorrick Sleijster

1
Questo metodo non funziona con pacchetti come "json" (restituisce False quando import jsonfunziona effettivamente). Il metodo di ice.nicer funziona in questo caso.
mountrix

1
In qualche modo avevo bisogno di utilizzare il segno if "u'requests'" in installed_packages:di spunta da Risposta aggiornata per funzionare per me. L'output di print()produce questo sul mio macOS python:[..., u'pytz', u'requests', ...]
Buju

1
@ArturBarseghyan Sono su Python 2.7.10 (sistema macOS python) ... in realtà devo usare if "requests'"(notare la virgoletta singola alla fine). non ha affatto senso.
Buju

1
Questo non risponde alla domanda: "il pacchetto 'x' è installato?". Esistono molti modi per installare un pacchetto senza utilizzare pip. Ad esempio, puoi creare e installare un pacchetto dal sorgente. Questa risposta non li troverà. La risposta di @ ice.nicer find_spec()è giusta.
Arthur

43

A partire da Python 3.3, puoi usare il metodo find_spec ()

import importlib.util

# For illustrative purposes.
package_name = 'pandas'

spec = importlib.util.find_spec(package_name)
if spec is None:
    print(package_name +" is not installed")

4
Pulito e semplice, la migliore risposta qui. Questo richiede più voti positivi.
David Parks

5
Stai attento. Se il nome del pacchetto contiene trattini, find_spec () non può trovarlo a meno che non utilizzi i punti. Esempio: importlib.util.find_spec('google-cloud-logging')non trova nulla mentre importlib.util.find_spec('google.cloud.logging')viene trovato. Il nome ufficiale e il nome mostrato da pip sono trattini, non punti. Potrebbe essere fonte di confusione, quindi utilizzare con cautela.
Dwich

27

Se vuoi avere il controllo dal terminale, puoi eseguire

pip3 show package_name

e se non viene restituito nulla, il pacchetto non viene installato.

Se forse vuoi automatizzare questo controllo, in modo che ad esempio tu possa installarlo se manca, puoi avere quanto segue nel tuo script bash:

pip3 show package_name 1>/dev/null #pip for Python 2
if [ $? == 0 ]; then
   echo "Installed" #Replace with your actions
else
   echo "Not Installed" #Replace with your actions, 'pip3 install --upgrade package_name' ?
fi

Sembra fantastico ma restituisce sempre "Non installato" per me. Perché?
Elliptica

Potete condividere l'output di "$ pip3 —version" e "$ pip3 show nome_pacchetto", dove sostituite nome_pacchetto con il pacchetto desiderato?
Ognjen Vukovic

ifif pip3 show package_name 1>/dev/null; then ...
Suggerimento

6

Come estensione di questa risposta :

Per Python 2. *, pip show <package_name>eseguirà la stessa operazione.

Ad esempio pip show numpyrestituirà quanto segue o simile:

Name: numpy
Version: 1.11.1
Summary: NumPy: array processing for numbers, strings, records, and objects.
Home-page: http://www.numpy.org
Author: NumPy Developers
Author-email: numpy-discussion@scipy.org
License: BSD
Location: /home/***/anaconda2/lib/python2.7/site-packages
Requires: 
Required-by: smop, pandas, tables, spectrum, seaborn, patsy, odo, numpy-stl, numba, nfft, netCDF4, MDAnalysis, matplotlib, h5py, GridDataFormats, dynd, datashape, Bottleneck, blaze, astropy

Non da uno script ma ottimo per il controllo della riga di comando. Grazie
Jj Rivero

3

Puoi usare il modulo pkg_resources da setuptools. Per esempio:

import pkg_resources

package_name = 'cool_package'
try:
    cool_package_dist_info = pkg_resources.get_distribution(package_name)
except pkg_resources.DistributionNotFound:
    print('{} not installed'.format(package_name))
else:
    print(cool_package_dist_info)

Nota che c'è una differenza tra il modulo python e un pacchetto python. Un pacchetto può contenere più moduli e i nomi dei moduli potrebbero non corrispondere al nome del pacchetto.



1

Vorrei aggiungere alcuni miei pensieri / scoperte a questo argomento. Sto scrivendo uno script che verifica tutti i requisiti per un programma personalizzato. Ci sono anche molti controlli con i moduli python.

C'è un piccolo problema con il

try:
   import ..
except:
   ..

soluzione. Nel mio caso viene chiamato uno dei moduli python python-nmap, ma lo importi con import nmape come vedi i nomi non corrispondono. Pertanto il test con la soluzione di cui sopra restituisce un risultato False, e importa anche il modulo all'hit, ma forse non è necessario utilizzare molta memoria per un semplice test / controllo.

L'ho trovato anche io

import pip
installed_packages = pip.get_installed_distributions()

installed_packagesavrà solo i pacchetti installati con pip . Sul mio sistema pip freezeritorna sui 40moduli python, mentre installed_packagesha solo 1quello che ho installato manualmente (python-nmap).

Un'altra soluzione di seguito che so potrebbe non essere pertinente alla domanda , ma penso che sia una buona pratica mantenere la funzione di test separata da quella che esegue l'installazione potrebbe essere utile per alcuni.

La soluzione che ha funzionato per me. Si basa su questa risposta Come verificare se esiste un modulo python senza importarlo

from imp import find_module

def checkPythonmod(mod):
    try:
        op = find_module(mod)
        return True
    except ImportError:
        return False

NOTA: anche questa soluzione non riesce a trovare il modulo con il nome python-nmap, devo usare nmapinvece (facile da convivere) ma in questo caso il modulo non verrà caricato in memoria.


0

Se desideri che il tuo script installi i pacchetti mancanti e continui, potresti fare qualcosa di simile (nell'esempio del modulo 'krbV' nel pacchetto 'python-krbV'):

import pip
import sys

for m, pkg in [('krbV', 'python-krbV')]:
    try:
        setattr(sys.modules[__name__], m, __import__(m))
    except ImportError:
        pip.main(['install', pkg])
        setattr(sys.modules[__name__], m, __import__(m))

0

Un modo rapido è usare lo strumento da riga di comando python . Basta digitare import <your module name> Si vede un errore se manca il modulo.

$ python
Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
>>> import sys
>>> import jocker
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named jocker
$

0

Hmmm ... la risposta più vicina a me è stata quella di utilizzare la riga di comando per provare l'importazione. Ma preferisco anche evitarlo.

Che ne dici di 'pip freeze | grep pkgname '? L'ho provato e funziona bene. Mostra anche la versione che ha e se è installata sotto il controllo della versione (installazione) o modificabile (sviluppo).


0

class myError (eccezione): passa # o fai qualcosa prova: importa il mymodule eccetto ImportError come e: raise myError ("si è verificato un errore")


0

Nel tipo di terminale

pip show some_package_name

Esempio

pip show matplotlib

0

Vorrei commentare la risposta di @ ice.nicer ma non posso, quindi ... La mia osservazione è che i pacchetti con trattini vengono salvati con trattini bassi, non solo con punti come sottolineato da @dwich commento

Ad esempio, lo fai pip3 install sphinx-rtd-theme, ma:

  • importlib.util.find_spec(sphinx_rtd_theme) restituisce un oggetto
  • importlib.util.find_spec(sphinx-rtd-theme) restituisce Nessuno
  • importlib.util.find_spec(sphinx.rtd.theme) solleva ModuleNotFoundError

Inoltre, alcuni nomi sono completamente cambiati. Ad esempio, lo fai pip3 install pyyamlma viene salvato semplicemente comeyaml

Sto usando python3.8


0
if pip3 list | grep -sE '^some_command\s+[0-9]' >/dev/null
  # installed ...
else
  # not installed ...
fi

-1

Vai all'opzione n. 2. Se ImportErrorviene lanciato, il pacchetto non è installato (o non è presente sys.path).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.