Installare e usare Python su Windows (parte 4/10).

Attenzione!

Queste guide non sono più aggiornate, e in alcuni punti sono proprio superate.
Il mio libro Python in Windows tratta questi temi, e molto altro, in modo molto più completo e aggiornato. 

Installare pacchetti esterni con Pip.

Dopo aver capito come si creano i venv, è il momento di usarli per lo scopo per cui sono nati: mantenere “ambienti” separati e isolati in cui installare tutte le “dependencies” necessarie al progetto a cui state lavorando.

PyPI: la repository ufficiale delle librerie Python.

Per prima cosa, dovete sapere che cosa è PyPI (Python Package Index): si tratta della più grande e “ufficiale” repository di software scritto in Python. È ufficiale nel senso che è mantenuta dalla stessa Python Software Foundation che gestisce lo sviluppo di Python, ed è sicuramente il punto di riferimento per tutti gli sviluppatori Python.
In pratica coloro che, dopo aver scritto una libreria (che può essere un semplice modulo come un complesso framework) desiderano distribuirla, possono registrarla e caricarla su PyPI come un pacchetto installabile. Chi desidera poi installare il pacchetto può cercarlo e scaricarlo su PyPI.
PyPI ha un’interfaccia web che offre varie possibilità di ricerca e, volendo, di scaricamento diretto dei pacchetti. In pratica però queste operazioni sono sempre effettuate con il package manager Pip.

Pip: il package manager ufficiale di Python.

Pip è un package manager per pacchetti Python: è “ufficiale” perché è entrato a far parte della libreria standard, e quindi dovrebbe essere a disposizione con la vostra installazione di Python (dalla versione 3.4 in poi, e anche nella 2.7. Se avete una versione più vecchia, Pip va installato separatamente).
Pip è un modulo della libreria standard, ma esiste anche un eseguibile pip.exe nella directory Scripts della vostra installazione Python, che è leggermente più comodo da usare. Per esempio, potete usare direttamente pip install invocando l’eseguibile, invece di py -m pip install invocando l’interprete Python e chiedendogli di eseguire il modulo pip.
Abbiamo già usato Pip per installare il pacchetto virtualenv nel Python 2 di sistema. Come abbiamo detto, è bene tuttavia installare i pacchetti sempre in un venv.
Come primo esempio, proviamo a installare Arrow: si tratta di un pacchetto per la gestione delle date. Se non lo avete già fatto, create un venv di Python 3 (chiamiamolo test) e usiamo pip install:
> cd d:/envs
D:\envs > py -3 -m venv test
> d:/envs/test/scripts/activate
(test) > pip install arrow
[...]
Come vedete la sintassi è elementare: invochiamo pip.exe seguito dall’opzione install, seguita dal nome del pacchetto che vogliamo installare. Pip si occupa di tutto per noi: cerca il pacchetto su PyPI, sceglie la versione più aggiornata, la scarica e la installa. Se c’è bisogno (come in questo caso) di installare degli altri pacchetti mancanti per il funzionamento di Arrow, allora Pip cerca, scarica e installa anche questi.
Se adesso aprite una shell nel venv e provate a importare e usare Arrow, scoprirete che… funziona, naturalmente!
(test) > py
Python 3.7.0 ([etc]) on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import arrow
>>> print(arrow.utcnow()) 
Niente di difficile. Se adesso uscite dal venv (deactivate), e provate di nuovo ad aprire una shell Python (py -3) e importare Arrow, otterrete invece un ModuleNotFoundError. Come promesso, Arrow è stato installato solo nel venv, e non nel Python di sistema.
La stessa cosa, ovviamente, vale per i vostri script. Modificate ancora una volta il vostro test.py, come segue:
import sys
import arrow

print(sys.executable)
print(arrow.utcnow())
Adesso se lo eseguite da dentro il venv, funziona:
(test) > cd c:/test
(test) C:\test> py test.py
[...]
Ma fuori dal venv, no:
(test) C:\test> deactivate
C:\test> py test.py
[...]
ModuleNotFoundError: No module named 'arrow'
La documentazione completa di Pip spiega nel dettaglio tutte le opzioni possibili. È importante ricordare, oltre a pip install, anche pip uninstall <package> e pip upgrade <package> per aggiornare. È importante la possibilità di specificare la versione del pacchetto da installare, in modo più o meno preciso. Per esempio,
pip install arrow==0.4.4
pip install 'arrow>=0.5'
e così via.
Una nota ancora: nei rari casi in cui effettivamente serve installare un pacchetto “globalmente”, e non all’interno di un venv, state attenti a usare pip install quando avete molteplici versioni di Python installate. La semplice invocazione a pip utilizza il primo pip.exe che Windows trova nella path di sistema: è chiaro che quando siete all’interno di un venv, questo è sempre il pip.exe del venv, e quindi pip install “funziona e basta”. Ma fuori da un venv, pip potrebbe non riferirsi al pip.exe che volete voi. Per fortuna oltre a pip.exe esistono sempre anche le versioni “numerate” degli eseguibili di Pip: per esempio, in una installazione di Python 2.7 troverete anche pip2.exe e pip2.7.exe. Quindi, se siete fuori da un venv, dovete invocare le versioni numerate per essere sicuri di usare il Pip giusto: invece di pip install, usate pip2 install, pip3.7 install, etc., a seconda di dove volete installare il pacchetto. Tutto questo, ripetiamo, non è necessario all’interno di un venv (ovvero, nella quasi totalità dei casi): all’interno di un venv, pip install fa sempre la cosa giusta.
Una nota ancora per concludere: se avete installato Python 3 “for all users”, probabilmente lo avete installato in C:/Program Files. In questo caso, naturalmente, l’installazione di pacchetti “globali” con pip3 install fallirà con un PermissionError: [WinError 5]: dovete installare da una shell “privilegiata” (ovvero con privilegi da amministratore). Ancora una volta, precisiamo: è molto raro incontrare questo problema, perché in genere installerete pacchetti solo nei venv, e i venv naturalmente risiedono in una directory per la quale avete i necessari permessi di scrittura.

Liste di “requirements”.

Pip mantiene un registro dei pacchetti. Potete verificare quali pacchetti avete installato con pip freeze:
(test) > pip freeze

Di solito, dopo aver installato i pacchetti necessari in un venv, si “salva” la configurazione raggiunta in un file di testo:
(test) > pip freeze > requirements.txt
Qui naturalmente > è il comando standard per re-direzionare l’output della shell, e requirements.txt è il nome che per convenzione si dà a questo tipo di file. Un file requirements.txt non è solo un promemoria per noi: è l’impronta digitale del nostro venv, ed è anche la ricetta per ri-crearlo identico a partire da zero. Infatti pip install ha anche l’opzione di installare in un colpo solo tutti i pacchetti che trova elencati in un file di testo.
Potete fare la prova. Supponiamo che abbiate messo il vostro requirements.txt nella directory c:/test che abbiamo usato finora come base d’appoggio. Ora, uscite dal venv test, createne un altro (poniamo, in d:/envs/test2), ed entrateci. Adesso tutto ciò che dovete fare è:
(test2) > cd c:/test
(test2) C:\test > pip install -r requirements.txt
La chiave qui è l’opzione -r di pip install, che dice a Pip di installare tutto ciò che trova elencato nel file specificato (in questo caso, requirements.txt). Alla fine della procedura, vi ritroverete un venv completo di tutti i pacchetti dichiarati nel requirements.txt (potete verificare con pip freeze, naturalmente).
La lista di requirements che ottenete da pip freeze è molto specifica: per ciascun pacchetto è indicata la versione esatta che avete installato. Questo è utile se si desidera ri-creare ogni volta lo stesso preciso venv. Tuttavia, talvolta questo non è davvero ciò che volete: potreste voler installate ogni volta le versioni più recenti dei pacchetti. Per questo vi basterà correggere manualmente il vostro requirements.txt togliendo le versioni, e lasciando solo l’elenco dei pacchetti:
arrow
python-dateutil
six
In questo modo pip install -r requirements.txt cercherà automaticamente sempre le versioni più aggiornate. Il formato di un requirements.txt può essere molto semplice (un elenco di pacchetti), molto specifico (il risultato di un pip freeze) o… raffinato: rimandiamo alla documentazione per tutte le sottigliezze.
Una particolare sottigliezza che conviene tenere a mente è che i file dei requirement sono componibili tra loro, nel senso che un file può specificare di installare anche i pacchetti elencati in un altro file. Questo torna utile in molti scenari. Per esempio: un progetto richiede un elenco di pacchetti comuni, e inoltre alcuni pacchetti aggiuntivi che si usano solo in fase di sviluppo, o di test, ma che non sono necessari per la versione “pubblica” del progetto. In questo caso, potete indicare i pacchetti comuni in un requirements.txt come al solito. Poi potete creare un secondo file (per esempio requirements_devel.txt) in cui scrivete i pacchetti aggiuntivi:
-r requirements.txt
pacchetto_aggiuntivo1
pacchetto_aggiuntivo2
...
Notate l’opzione iniziale -r che rimanda a requirements.txt: potete specificare la path assoluta di requirements.txt, o preferibilmente la sua path relativa al file corrente, che in questo caso è requirements_devel.txt (ma mettete tutti i file di requirements nella stessa directory e siete a posto). Adesso potete creare il vostro venv di sviluppo con pip install -r requirements_devel.txt, che installerà anche i pacchetti elencati in requirements.txt.
Una strategia comune, al momento di iniziare un nuovo progetto, è di creare un venv apposito. Man mano che si installano dei nuovi pacchetti necessari al progetto, si rigenera il requirements.txt con pip freeze. Il file dei requirements è quindi tutto ciò che occorre per definire l’ambiente Python necessario a far funzionare il codice di quel progetto. Il venv si può distruggere e ricreare da zero, con un semplice pip install -r requirements.txt.
A questo punto, è importante sottolineare che non bisognerebbe mai manipolare direttamente la directory del venv (aggiungere, togliere, spostare dei file al suo interno). Il motivo è appunto questo: tutto il contenuto del venv deve dipendere esclusivamente da un requirements.txt. Il nostro progetto (i moduli Python che scriviamo, la documentazione, i file di configurazione e accessori…) deve sempre risiedere in una directory separata dal venv. Anche il file dei requirement risiede nella directory del progetto. Vedremo in seguito come strutturare un progetto Python in modo razionale.

Installare pacchetti locali.

Pip è in grado di installare pacchetti che avete scaricato in precedenza da PyPI, ma anche da altre repository purché i pacchetti siano conformi alle specifiche richieste da Pip/PyPI. Molto raramente potrebbe capitarvi di installare un pacchetto che non è stato caricato su PyPI, ma che trovate solo (per esempio) su sito dell’autore. Oppure potreste volervi fare una piccola collezione di pacchetti che installate frequentemente, in modo da non doverli scaricare tutte le volte da PyPI. Una terza possibilità è che dobbiate installare dei pacchetti pre-compilati non ufficiali, che non trovate quindi su PyPI: approfondiremo tra poco questo caso.
Vi conviene prima di tutto creare una directory d:/envs/packages in cui scaricare e conservare i vostri pacchetti preferiti. Potete scaricare direttamente dal sito di PyPI, ma potete anche lasciare che sia Pip a fare il lavoro per voi: Pip ha infatti un’opzione per scaricare senza installare. Facciamo una prova, questa volta con la nota libreria Requests (serve a manipolare richieste HTTP). Lavoriamo sempre all’interno del nostro venv di Python 3: anche se Pip non installerà subito il pacchetto, è importante che sappia per quale versione di Python deve scaricarlo.
(test) > pip download requests -d d:/envs/packages
L’opzione -d specifica una directory di destinazione; se non la indicate, Pip scaricherà il pacchetto nella directory corrente. Se adesso guardate dentro d:/envs/packages, troverete il pacchetto scaricato. Si tratta di una Wheel (riconoscibile dall’estensione .whl): è il formato oggi più comune per i pacchetti Python, e ne parleremo ancora in seguito. Per il momento, una raccomandazione: non dovete cambiare il nome del file scaricato. In una Wheel il nome del file è significativo, e Pip non potrà installarlo correttamente se lo modificate.
Una volta scaricato il pacchetto, potete installarlo con Pip senza più bisogno della connessione internet:
(test) > pip install d:/envs/packages/requests-2.13.0-py2.py3-none-any.whl
Come vedete, basta specificare la path del pacchetto (non è necessario che sia una path assoluta: basta la path relativa alla directory corrente). Potete anche scrivere le path dei pacchetti locali che desiderate in un requirements.txt, e installare in un colpo solo tutto quanto con pip install -r requirements.txt.

Wheel e pacchetti binari precompilati.

La procedura che abbiamo descritto fin qui funziona per installare pacchetti che sono scritti completamente in Python. Ma occasionalmente (per fortuna sempre più di rado) pip install fallisce con una serie di messaggi di errore piuttosto complicati, ma che in sostanza ci avvertono che non è possibile “compilare” i pacchetti necessari.
Che cosa è successo? Molti pacchetti, in realtà, non sono scritti solo in Python, ma ospitano anche qualche estensione scritta in C/C++. Fino a qualche anno fa, il formato di distribuzione dei pacchetti Python (le “egg”, che si trovano ancora occasionalmente) non prevedeva un modo per distribuire pacchetti con estensioni già compilate. Quindi, le estensioni in C dovevano essere compilate da voi al momento dell’installazione. Questo non è un problema sui sistemi Linux, che tradizionalmente hanno un compilatore di default pronto all’uso: in questo caso, Pip si rivolge al compilatore, compila le estensioni C, e installa il pacchetto senza problemi.
Windows invece non ha un compilatore di default, e quindi Pip scarica correttamente il pacchetto giusto, ma al momento di compilare le estensioni non sa più come procedere.
Da qualche anno Python ha introdotto un nuovo formato per distribuire i pacchetti, che ormai ha quasi del tutto sostituito il precedente: le Wheel, che consentono tra l’altro di pre-compilare le estensioni C. Tuttavia occasionalmente pip install potrebbe ancora trovare su PyPI solo vecchi pacchetti non pre-compilati. O magari Pip trova una wheel, ma non quella pre-compilata per la vostra particolare versione di Python. In casi del genere, possiamo rimediare seguendo una delle due strade:
  1. installiamo un compilatore C, e compiliamo a mano le estensioni che ci servono. In linea di principio, questa è una strada perfettamente percorribile. Tuttavia, compilare su Windows richiede un po’ di esperienza e capacità di affrontare corner case fastidiosi;
  2. nella maggior parte dei casi, qualcuno ha già fatto il lavoro al posto nostro. Si tratta quindi di trovare e scaricare un pacchetto con le estensioni già compilate, che Pip può quindi installare senza problemi.
Vedremo subito come percorrere la seconda strada - ma prima, una legittima domanda: perché gli sviluppatori non distribuiscono su PyPI direttamente i pacchetti con le estensioni già compilate? In primo luogo, questa possibilità è arrivata solo di recente, con le wheel. Ma soprattutto, è possibile e scusabile che gli sviluppatori non abbiano voglia di supportare diverse versioni dello stesso pacchetto, pre-compilate per ciascuna diversa piattaforma. Spesso è possibile trovare le wheel compilate solo per le piattaforme che gli sviluppatori ritengono più interessanti o accessibili per loro. Talvolta esistono wheel compilate solo per certe versioni di Python, e non per altre. In particolare, potrebbe essere leggermente più complicato trovare wheel compilate per Python 3.5/6/7. Infatti ricordate che queste versioni di Python sono compilate con Visual Studio 2015. Uno scenario frequente è che il pacchetto, per quanto riguarda il solo codice Python, sia perfettamente compatibile tra Python 3.4 e Python 3.5+. Tuttavia, se il pacchetto contiene delle estensioni C, lo sviluppatore deve ugualmente compilarlo due volte con un compilatore diverso (per supportare la 3.4 e la 3.5+). Molti sviluppatori, comprensibilmente, potrebbero non aver voglia di tenere entrambi i compilatori installati e fare due volte il lavoro. Va detto comunque che, man mano che Python 3.4 esce di scena, le wheel precompilate con Visual Studio 2015 stanno ormai diventando la norma.
In ogni caso, ci sono diverse opzioni da tentare.
  • La wheel precompilata per la vostra piattaforma è già su PyPI. Questa è in realtà una opzione-zero che dovreste aver già controllato, perché in questo caso pip install funziona senza problemi. Tuttavia raramente su PyPI si trovano altre distribuzioni precompilate, solo non come wheel (tipicamente degli .exe, cfr. sotto, punto 5): quindi, anche se pip install fallisce, una ricerca manuale su PyPI può essere utile.
  • Il pacchetto “problematico” è una dependency di un altro pacchetto che state cercando di installare da PyPI. In casi del genere, prendete nota dei pacchetti che Pip non riesce ad installare da PyPI, cercateli e installateli manualmente, e poi ripetete l’installazione del pacchetto di partenza.
  • La wheel precompilata si trova sulla raccolta non ufficiale di Christoph Gohlke. Questo sito è una vera miniera d’oro per chi sviluppa in Python su Windows. Il buon Chris mantiene da anni una raccolta di decine di pacchetti precompilati, per varie versioni di Python e diverse piattaforme (32 e 64 bit). Semplicemente, cercate il pacchetto che vi interessa con ctrl+F nel vostro browser, assicuratevi di scaricare la versione giusta per voi, e installate dal file locale come abbiamo visto sopra. Va detto che la raccolta di Chris diventa (per fortuna!) sempre meno utile, man mano che gli sviluppatori mettono a disposizione le wheel precompilate su PyPI: tuttavia spesso sul sito di Chris trovate le wheel compatibili con le versioni più vecchie di Python, che gli sviluppatori non coprono direttamente. Questo sito è assolutamente da tenere nei bookmark.
  • La wheel precompilata si trova sul sito dello sviluppatore. Questo è raro: se lo sviluppatore si è dato la pena di produrre una wheel precompilata, allora deve averla messa anche su PyPI, e voi dovreste già averla trovata con pip install. Può capitare però, per esempio, che la libreria che vi interessa sia ancora in fase di sviluppo, e non sia disponibile su PyPI. Talvolta inoltre lo sviluppatore non produce delle wheel precompilate, ma sul suo sito si trovano altre indicazioni utili, per esempio link a contributori del progetto che mantengono versioni “non ufficiali” compilate, etc.
  • Sul sito dello sviluppatore (o siti collegati di contributori etc.; talvolta, anche su PyPI) si trova non la wheel precompilata, ma comunque un installer .exe. Questa era la tecnica più diffusa fino a poco tempo fa, anche perché era in pratica l’unico modo di mettere a disposizione pacchetti precompilati per Windows. L’aspetto negativo è che se fate doppio clic su questi .exe, si installano solo “globalmente” ignorando i venv. L’aspetto positivo è che molto spesso si tratta solo di pacchetti .egg (il vecchio modo di creare pacchetti in Python) raccolti in un file .zip eseguibile: in questo caso è facile convertirli in moderne wheel. Tutto quello che occorre, una volta scaricato manualmente il pacchetto, è dare il comando wheel convert pacchetto.exe per ottenere un wheel installabile poi con Pip. La libreria necessaria per l’operazione, Wheel, dovrebbe essere installata di default con Pip. Altrimenti, basta installarla con pip install wheel. Se la trasformazione da .exe a .whl non riesce, vi resta ancora una possibilità: usate il pacchetto .exe, ma costringetelo a installarsi in un venv anziché globalmente. Il trucco è non fare doppio clic sull’eseguibile, ma lanciarlo dalla shell… dopo aver attivato un venv! Di solito l’installer si limita a cercare Python nella path di sistema, e siccome l’attivazione del venv inserisce il suo interprete Python in testa alla path, il gioco è fatto. In genere funziona: se però l’installer .exe fa delle cose insolite (guarda il registro di configurazione, copia dei file in directory predefinite, etc…), allora bisogna rassegnarsi.
Se cercate delle wheel in rete da scaricare manualmente, fate attenzione a scegliere il pacchetto giusto: i nomi dei file delle wheel sono significativi e riportano la versione del pacchetto e con quali versioni di Python è compatibile. pip install e pip download vi procurano automaticamente la versione giusta, ma voi dovete scegliere tra le opzioni possibili.
Una volta scaricato il pacchetto, potete installarlo con Pip dal file locale, come visto sopra. Se adesso chiedete un pip freeze, noterete che il pacchetto che avete installato a mano è regolarmente elencato insieme agli altri. Di conseguenza, potete anche generare un normale file di requirements (pip freeze > requirements.txt). Tuttavia, se in futuro volete usare il file di requirements per re-installare l’ambiente del venv (pip install -r requirements.txt), questo potrebbe non funzionare: Pip cercherebbe i pacchetti richiesti su PyPI, e non troverebbe quelli installati manualmente.
L’unica soluzione è correggere il file requirements.txt, e specificare il pacchetto locale dove necessario: il vostro file potrebbe quindi avere questo aspetto:
# pacchetti "pure-python" da cercare su PyPI:
pacchetto1
pacchetto2
# etc etc

# pacchetti binari da installare localmente:
d:/envs/packages/pacchetto3.whl
d:/envs/packages/pacchetto4.whl
# etc etc
Potete chiamare requirements_local.txt questo file di requirements specifico per il vostro sistema, e generare poi un requirements.txt “ufficiale” con pip freeze da includere nel vostro progetto se intendete pubblicarlo.

Pacchetti ausiliari di uso comune.

Quando installate dei pacchetti in un venv, questi sono ovviamente disponibili solo nel venv in cui li avete installati. È possibile tuttavia creare un venv con l’opzione --system-site-packages, che permette al venv di utilizzare anche i pacchetti installati nel Python di sistema da cui è stato creato.
Se per esempio create un venv così:
> cd d:/envs
D:\envs > py -3 -m venv my_venv --system-site-packages
allora il venv my_venv avrà accesso anche ai pacchetti installati nel Python 3 di sistema. Ma non abbiamo detto e ripetuto che non bisognerebbe mai installare pacchetti nel Python di sistema? Infatti: quindi, anche --system-site-packages non andrebbe usata affatto, o con estrema cautela.
C’è però uno scenario comune, in cui tutto questo è permesso e forse perfino consigliabile. Vi troverete spesso a dover installare una serie di pacchetti ausiliari che non sono necessari per eseguire il codice del vostro progetto, ma che comunque vorrete installare per aiutarvi nello sviluppo. Per esempio, un linter come Pep8 o PyLint; pacchetti che vi aiutano con i test, come Pytest, Nose o Fixtures; oppure ancora Sphinx per generare la documentazione; e altri ancora (parleremo di questi aspetti e di questi pacchetti nelle sezioni successive).
Ci sono due strategie per gestire questi pacchetti ausiliari. La prima, più semplice, è di installarli con Pip in ciascun venv che create. Tutt’al più, potete risparmiare il tempo di download scaricando una volta per tutte le wheel necessarie, e chiedendo a Pip di installare dal file locale. Dovete solo fare attenzione a un dettaglio: siccome non sono pacchetti necessari a eseguire il vostro codice, non dovreste includerli nel requirements.txt “pubblico” del vostro venv: potete creare un requirements_local.txt (o requirements_devel.txt) con tutti i pacchetti che servono solo a voi, e popolare il venv con quello.
La seconda strategia è quella di installare questi pacchetti ausiliari di uso comune nel Python di sistema, e poi creare i vostri venv con l’opzione --system-site-packages: in questo modo i venv possono attingere anche ai pacchetti ausiliari che vi servono per lo sviluppo. In questo modo vi risparmiate la piccola noia di installare gli stessi pacchetti in tutti i venv. Una cautela: come vedremo, molti di questi pacchetti potrebbero essere utilizzati e integrati nel vostro editor preferito: in genere gli editor evoluti possono essere istruiti sul venv di cui ha bisogno il codice su cui state lavorando. Se però il venv utilizza anche dei pacchetti globali, controllate se l’editor è in grado di capire dove trovarli: in genere non ci sono problemi, ma nei casi più complicati la documentazione dell’editor dovrebbe aiutarvi.
È importante in ogni caso ribadire un concetto: usate (eventualmente) questa strategia esclusivamente per i pacchetti ausiliari che non servono per far funzionare il codice dei vostri progetti, ma che servono solo a voi per lo sviluppo. Tutte le “dependencies” necessarie al vostro progetto devono essere installate nel venv ed elencate nel requirements.txt.

Riassunto: installare pacchetti.

Ripetiamo in breve i punti fondamentali:
  • Dedicate un venv a ciascun progetto. Potete creare anche uno o più venv generici per semplici script, per valutare nuovi pacchetti, etc. Ma non installate pacchetti nelle versioni “globali” di Python.
  • Installate con pip install: quasi sempre è la cosa giusta e la più facile.
  • Quando occasionalmente questo non funziona, cercate in rete le wheel precompilate del pacchetto che volete installare: scaricatele manualmente e installatele con Pip.
  • Generate un requirements.txt con pip freeze: questo file è la ricetta per creare il venv del vostro progetto. Il contenuto della directory del venv dovrebbe dipendere solo dal requirements.txt: non dovete mai modificare manualmente il venv.

Che cosa non sono i venv.

Prima di abbandonare l’argomento venv, è forse opportuno inserire alcune note di carattere più avanzato, che un principiante potrebbe tenere a mente per il futuro. Occorre tener presente che i venv Python non sono (e non vogliono essere) una soluzione completa di virtualizzazione: si tratta solo di un meccanismo, anche abbastanza poroso, per tenere separati i pacchetti Python occorrenti in un progetto. Il resto dell’ambiente di lavoro in cui il progetto è immerso non è controllabile con i venv. Un requirements.txt non può elencare le risorse esterne necessarie, come i file da aprire o le connessioni ai database; i venv non conservano le variabili d’ambiente, né qualsiasi altra informazione pertinente al vostro sistema operativo, al file system, all’hardware impiegato; un file di requirements, di per sé, è indifferente perfino alla versione di Python su cui state lavorando.
Se tutto questo per il vostro progetto è indifferente, allora nessun problema. Ma se avete bisogno di un controllo maggiore, dovreste rivolgervi a una soluzione di virtualizzazione più completa, come per esempio un container Docker. Una “virtual machine” di qualche tipo potrebbe aiutarvi sia in fase di sviluppo, per “congelare” lo status del sistema su cui lavorate; sia in fase di distribuzione della vostra applicazione, per garantire build deterministiche e/o per replicare fedelmente le caratteristiche della macchina che ospiterà il vostro software in produzione.
Un altro aspetto importante da ricordare è che Pip e i venv fanno molto poco per aiutarvi ad affrontare la variegata classe di problemi nota informalmente come “dependency hell”. In sostanza, con Pip avete solo due alternative. Potete specificare nel requirements.txt solo i pacchetti principali che vi occorrono, senza numeri di versione: in questo modo, lasciate a pip install il compito di scaricare anche le dependency secondarie (i pacchetti che servono ai pacchetti che servono a voi!) e installare le versioni più aggiornate ogni volta che ri-create il venv. In questo modo però non avete una build deterministica: se una nuova versione di un pacchetto introduce una incompatibilità con il vostro codice, il risultato potrebbe essere imprevisto. In linea di principio, far girare il vostro codice produce risultati diversi in tempi diversi.
Oppure potete elencare tutti i pacchetti con le loro versioni esatte, come risultano da un pip freeze. In questo caso vi garantite una build deterministica, ma lo svantaggio è che dovete gestire manualmente il requirements.txt tutte le volte che volete aggiornare un pacchetto, modificando il numero di versione di questo e di tutte le sue dependency.
I moduli pip e venv della libreria standard sono sicuramente sufficienti per i principianti e anche per i più esperti, almeno nella maggioranza dei casi. Esistono comunque soluzioni più avanzate che prima o poi potreste voler prendere in considerazione: in questo periodo lo strumento di packaging più valido e popolare nel mondo Python è diventato Pipenv di Kenneth Reitz, ormai adottato come raccomandazione ufficiale della Python Packaging Authority. Prima o poi lo incontrerete sicuramente sulla vostra strada, visto che è usato/supportato anche da importanti PaaS come Heroku, PythonAnywhere e progressivamente tutti gli altri.

Commenti

Post più popolari