Impacchettare progetti Python per Debian / Raspbian con dh-virtualenv

Questo articolo ha lo scopo di spiegare alcune cose agli sviluppatori che non usano molto Python, e potrebbero lottare con alcuni dei concetti altrimenti.

Consiglio vivamente la lettura del seguente articolo come introduzione ai concetti qui discussi:

pypi.org

pypi.org è un repository ufficiale di software per il linguaggio di programmazione Python. Include librerie che vogliamo usare nei nostri progetti, che non sono fornite di default con Python.

Il problema

Il nostro obiettivo è quello di impacchettare un'applicazione scritta in Python (mirando a Python3) come pacchetto .deb per Raspbian (che è, tutto sommato, davvero equivalente a Debian).

La nostra applicazione ha dipendenze da diverse librerie Python.

Per esempio, vorremmo usare l'elemento richieste libreria. La stessa libreria delle richieste dipende da altre librerie, per esempio chardet, urllib3, ecc.

Gestire manualmente queste dipendenze e spedire manualmente il codice di libreria richiesto è intrinsecamente poco pratico.

pip

Python ha un proprio sistema di gestione dei pacchetti e delle dipendenze, chiamato pip

pip vi permette di installare librerie e applicazioni python, in modo che il vostro codice possa usare queste librerie.

Qui ci sono due problemi:

  • vogliamo spedire il pacchetto come Pacchetto Debianper gli utenti finali da installare facilmente. Possiamo assumere con sicurezza che su un numero significativo di sistemi di destinazione le librerie python appropriate non saranno state installate dall'utente
  • se cerchiamo di installare queste librerie a livello di sistema per conto dell'utentepotremmo rompere la compatibilità con alcuni altri pacchetti (ad esempio a causa di cambiamenti API tra le versioni delle librerie)

Perciò abbiamo bisogno di una soluzione.

virtualenv

virtualenv ci permetterà di creare ambienti virtuali per Python.

In questi ambienti virtuali abbiamo:

  • il nostro interprete Python
  • copie delle librerie standard richieste
  • il nostro pip
  • la nostra biblioteca "di sistema

Parte pratica: Provare virtualenv

Installare virtualenv (come parte di dh-virtualenv, a cui arriveremo tra poco) facendo:

sudo apt install dh-virtualenv

Poiché le richieste sono già installate a livello di sistema sulla mia scatola di prova, useremo un pacchetto diverso come esempio. randstr vi permetterà di generare una stringa casuale.

Controllare se randstr è installato in tutto il sistema:

pip3 mostra randstr

Questo non dovrebbe restituire nulla. Indica che questo pacchetto non è installato in tutto il sistema:

immagine

Ora creeremo un ambiente virtuale. La cartella dell'ambiente virtuale NON deve vivere all'interno della tua cartella del codice, ma può farlo. Dovresti ignorarla in GIT.

diciamo che abbiamo una directory /home/pi/tutorial

Entrate in questa directory e create una nuova cartella di ambiente env:

cd /home/pi/tutorial

quale python3

virtualenv -p /usr/bin/python3 env

Nota: devi specificare l'interprete Python se vuoi Python 3, altrimenti verrà usato Python 2. Io sto usando Python 3. (Esecuzione di virtualenv con interprete /usr/bin/python2)

che python3 vi dirà dove risiede il binario python3, regolate il percorso per virtualenv di conseguenza.

immagine

attivare l'ambiente:

fonte env/bin/activate

(Questo percorso di comando presuppone che siate rimasti nella directory del tutorial). Notate anche che questo non è uno script eseguibile - dovete usare fonte per usarlo con il vostro terminale bash.

Notate come (env) viene anteposto al vostro prompt dei comandi:

immagine

Questo significa che l'ambiente è attivo. Ora è possibile eseguire nuovamente pip3, ma questa volta pip3 eseguirà il suo lavoro sull'ambiente virtuale:

pip3 mostra randstr

pip3 mostra le richieste

non dovrebbe ancora produrre nulla. Anche la seconda linea non mostrerà nulla - mostrando che (se le richieste sono installate a livello di sistema) siamo davvero in un ambiente virtuale.

Ora installate randstr:

pip3 installa randstr

e controllare di nuovo se è installato:

pip3 mostra randstr

immagine

Questa volta mostra che randstr è installato. È stato installato in env/lib/python3.5/site-packages:

immagine

Mentre l'ambiente è ancora attivato, scriviamo una piccola applicazione Python di esempio:

nano sample.py

incollare il seguente codice, generando una stringa casuale utilizzando la nuova libreria che abbiamo appena installato:

da randstr importare randstr
stampa("ciao mondo")
stampa(randstr())

e salvare.

Eseguite il codice usando python3:

python3 campione.py

mentre l'ambiente è ancora attivo, questo funzionerà:

immagine

Ora è il momento di uscire dall'ambiente, per vedere se il codice funzionerà ancora:

disattivare

Il tuo prompt tornerà alla normalità. E

python3 campione.py

lancerà un messaggio di errore sul fatto che non esiste alcun modulo 'randstr' sul vostro sistema. Questo è esattamente il comportamento che vogliamo!

immagine

Puoi dare un'occhiata alla cartella env, vedendo come spedisce tutte le cose necessarie per eseguire il tuo codice Python, comprese le librerie, l'eseguibile python, pip e altro.

Pacchetti Debian virtualenv con dh-virtualenv

OK, ora che le basi sono state eliminate, quello che vogliamo è impacchettare un virtualenv insieme al nostro codice, in modo che la nostra applicazione venga eseguita in modo affidabile e senza disturbare le altre applicazioni sui computer dei nostri utenti.

Qui è dove dh-virtualenv entra in scena. dh-virtualenv è un'aggiunta agli script di compilazione di Debian, che permette di pacchettizzare ambienti virtuali.

Dobbiamo creare una struttura di base per il nostro nuovo pacchetto. Qui è dove cookiecutter sarà d'aiuto.

cookiecutter

cookiecutter crea nuovi progetti per voi a partire da modelli, riducendo così l'overhead e il tempo che spendete per sviluppare e farvi capire i molti file che sono necessari.

https://cookiecutter.readthedocs.io/en/latest/

sudo apt install cookiecutter

Nota: il pacchetto python3-cookiecutter fornirà un modulo Python per Python 3. Quello che vogliamo è l'applicazione stessa, che è installata con il pacchetto cookiecutter come visto sopra. Questo potrebbe tirare in Python 2, ma - oh bene.

cookiecutter non è necessario per i vostri utenti finali, è usato solo per creare diversi file per il vostro pacchetto.

creare una directory di progetto di esempio, nella quale applicheremo il modello (sono ancora nella directory del tutorial):

mkdir sampleproject

cd sampleproject

Usando uno stampo speciale per dh-virtualenv possiamo impostare molti dei file necessari:

https://github.com/Springerle/dh-virtualenv-mold

cookiecutter https://github.com/Springerle/dh-virtualenv-mold.git

Questo vi porrà diverse domande.

immagine

Si noti che la cartella dovrebbe essere chiamata debian, anche quando si impacchetta per Raspbian - quindi mantenere il nome come debian.

Questo installerà i seguenti file:

immagine

Ora è necessario eseguire i seguenti comandi, come da istruzioni sotto il dh-virtualenv-mold:

sudo apt-get install build-essential debhelper devscripts equivs
sudo mk-build-deps --install debian/control
Per costruire il pacchetto in seguito, eseguirete il seguente comando dalla directory di primo livello del vostro progetto:
dpkg-buildpackage -uc -us -b
In questo momento, però, il comando fallirà perché manca setup.py. Arriveremo al setup.py e al requirements.txt tra poco:
/usr/bin/python3: impossibile aprire il file 'setup.py': [Errno 2] No such file or directory

Informazioni sui singoli file

changelog

Questo file conterrà le informazioni sul rilascio e sulla versione del pacchetto. Puoi aggiornarlo usando uno strumento speciale, dch.

Questo file non è un changelog per la tua applicazione, ma solo un changelog per il confezionamento della tua applicazione.

compat

Questo file include un numero magico speciale "9". (per questioni di compatibilità, va bene lasciarlo così com'è)

controllare

Questo è il file principale per impostare le impostazioni dei pacchetti e le dipendenze a livello Debian. Se la tua applicazione dipende, per esempio, dall'installazione di omxplayer, questo dovrà essere inserito qui come dipendenza:

Fonte: sampleproject
Sezione: contrib/python
Priorità: extra
Manutentore: Maximilian Batz
Build-Depends: debhelper (>= 9), python, python-dev, dh-virtualenv (>= 0.10), tar
Versione standard: 3.9.5
Homepage:
https://picockpit.com


Pacchetto: sampleproject
Architettura: qualsiasi
Pre-Depends: dpkg (>= 1.16.1), python2.7 | python3, ${misc:Pre-Depends}
Dipende: ${python:Depends}, ${misc:Depends}
Descrizione: Un pacchetto di esempio per dimostrare il packaging di virtualenv Debian con dh-virtualenv
     .
     Questa è una distribuzione di "sampleproject" come un progetto autonomo
     Python virtualenv avvolto in un pacchetto Debian (pacchetto "omnibus",
     tutti i passeggeri a bordo). Il virtualenv confezionato è tenuto in sincronia con
     l'interprete dell'host automaticamente.
     .
     Vedere
https://github.com/spotify/dh-virtualenv per maggiori dettagli.

Vorrai anche modificare la Descrizione lunga.

cookiecutter.json

Include le impostazioni iniziali. Non necessario per l'imballaggio.

copyright

Il tuo file di copyright, con la tua licenza (per esempio MIT). È precompilato con il tuo nome, anno, e-Mail e "alcuni diritti riservati".

regole

Questo è un Makefile, per costruire effettivamente il tuo pacchetto. È precompilato dal modello cookiecutter.

progetto-campione.link

Questo file permette di creare collegamenti durante l'installazione. Il nome del file includerà il nome del vostro progetto invece di progetto campione.

Rif: https://stackoverflow.com/questions/9965717/debian-rules-file-make-a-symlink

progettocampione.postinst

Questo file ti permette di eseguire ulteriori passi di configurazione dopo l'installazione (ad esempio attivare il tuo script python come servizio). Il nome del file includerà il nome del tuo progetto invece di progetto campione.

progettocampione.trigger

Per quanto ho capito questo serve a dh-virtualenv per installare un nuovo interprete python nell'ambiente virtuale da spedire, se quello del sistema (la tua scatola di sviluppo) è aggiornato. Il nome del file includerà il tuo nome di progetto invece di progetto campione.

Questo potrebbe essere o non essere il caso, non l'ho testato.

setup.py

Come detto sopra, abbiamo bisogno di un setup.py file. Questo è il modo standard in cui le applicazioni sono impacchettate sotto Python (vengono spedite con un setup.py, che viene eseguito per vari compiti relativi al packaging).

Un semplice setup.py può essere visto in questa pagina:

https://github.com/benjaminirving/python-debian-packaging-example/blob/master/setup.py

Non tutte le voci di questo file sono necessarie. Per esempio, si possono tralasciare i classificatori. Ho il seguente setup.py:

immagine

Nota: il numero di versione e altre informazioni qui dentro sono per il tuo pacchetto Python (che sarà creato nel corso della costruzione di un pacchetto Debian con il tuo virtualenv).

Struttura per il tuo codice

Il vostro codice vivrà ora in una sottodirectory della directory principale di sampleproject:

immagine

La sottodirectory ha lo stesso nome della directory principale in questo caso.

Nota il file __init__.py

Il sample.py è quello che abbiamo usato sopra, ma un po' rielaborato per assomigliare a questo:

immagine

Inoltre, ho impostato un ambiente virtuale per i test durante lo sviluppo all'interno della directory principale (livello superiore) sampleproject:

virtualenv -p /usr/bin/python3 sp_env

chiamato sp_env per l'ambiente del progetto campione

Entrare nell'ambiente:

fonte sp_env/bin/activate

Installare la libreria randstr in questo ambiente:

pip3 installa randstr

e ora possiamo eseguire il codice:

python3 sampleproject/sample.py

immagine

OK. Uscire dall'ambiente (! importante!)e provare a costruire di nuovo il pacchetto:

disattivare

dpkg-buildpackage -us -uc

Per impostazione predefinita (a causa del cookie cutter), il vostro pacchetto sarà creato per l'installazione in /opt/venvs/:

Nuovo eseguibile python in /home/pi/tutorial/sampleproject/debian/sampleproject/opt/venvs/sampleproject/bin/python3

Questo può essere cambiato nel file debian/regole file.

Ora il pacchetto è stato costruito e depositato fuori dalla directory principale di sampleproject (dentro la directory tutorial):

immagine

Puoi installare il file .deb usando dpkg:

sudo dpkg -i sampleproject_0.2.1+nmu1_armhf.deb

Vedere i risultati dell'installazione con

albero /opt/venvs

Sono state installate 127 directory e 945 file.

Ora potete provare a eseguire il progetto campione usando

/opt/venvs/sampleproject/bin/sampleproject

requisiti / dipendenze

L'applicazione non viene eseguita come (forse) ci si aspetta, ma lancia un errore su randstr che non è presente. Cosa succede?

immagine

La ragione è che dh-virtualenv non conosce il requisito che abbiamo, di mettere in bundle il pacchetto randstr durante il processo di costruzione. Questo è l'ultimo pezzo del puzzle.

I requisiti devono essere aggiunti al setup.pycome un modo per far sapere a pip quali pacchetti aggiuntivi devono essere installati nell'ambiente virtuale che si sta creando.

aggiungere il seguente a setup.py:

install_requires=[

        'randstr'

]

L'intero file avrà quindi questo aspetto:

immagine

Nei miei test il requirements.txt posto al livello superiore ha fatto non influenzare il comportamento di dh-virtualenv e dei pacchetti Python effettivamente inclusi nel pacchetto Debian. Se lo volete ancora, ecco come fare:

pip vi offre un modo per mettere fuori le esatte dipendenze che avete usato per sviluppare nell'ambiente virtuale. Entrate nell'ambiente virtuale:

fonte sp_env/bin/activate

E creare il file requirements.txt:

pip3 freeze > requirements.txt

Ora potete dare un'occhiata a quel file:

cat requirements.txt

immagine

Anche se questo file sembra non essere usato da dh-virtualenv (forse ha bisogno di andare in una sottodirectory diversa), è un buon riferimento per te, per vedere quali pacchetti dovresti mettere come dipendenze nella parte install_requires di setup.py.

Notate che il file sampleproject.links ci ha offerto un modo conveniente per collegare questo nella cartella /usr/bin, quindi rimuoverò il segno di commento per la prossima build:

immagine

Con questo fuori dai piedi, è il momento di eliminare il progetto campione, e costruirlo di nuovo, poi installarlo. Non dimenticare di disattivare prima!

disattivare

sudo apt purge sampleproject

dpkg-buildpackage -us -uc

cd ...

sudo dpkg -i sampleproject_0.2.1+nmu1_armhf.deb

Questa volta, il pacchetto dovrebbe essere accessibile usando sampleproject:

quale progetto campione

progetto campione

immagine

Rif: https://packaging.python.org/discussions/install-requires-vs-requirements/

Suggerimento bonus: cambiare il luogo di installazione

/opt/venvs non è un "posto" molto Debian per mettere un pacchetto.

Le specifiche sono qui:

http://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html

https://unix.stackexchange.com/questions/10127/why-did-my-package-get-installed-to-opt

  • /usr/local è non per i pacchetti
  • /opt non è per i pacchetti Debian propriamente detti (è per il software di terze parti installato dall'utente)

http://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s06.html

Le applicazioni possono usare una singola sottodirectory sotto /usr/lib. Se un'applicazione usa una sottodirectory, tutti i dati dipendenti dall'architettura usati esclusivamente dall'applicazione devono essere collocati in quella sottodirectory.

https://wiki.debian.org/HowToPackageForDebian

https://www.debian.org/doc/debian-policy/ch-opersys.html#file-system-hierarchy

"Il requisito FHS che i file statici specifici dell'applicazione indipendenti dall'architettura siano situati in /usr/share è rilassato a un suggerimento. In particolare, una sottodirectory di /usr/lib può essere usato da un pacchetto (o un insieme di pacchetti) per contenere un misto di file indipendenti dall'architettura e dipendenti dall'architettura. Tuttavia, quando una directory è interamente composta da file indipendenti dall'architettura, dovrebbe essere situata in /usr/share."

La posizione migliore mi sembra essere /usr/share

Dobbiamo modificare due file:

in debian/rules DH_VIRTUALENV_INSTALL_ROOT deve essere cambiato in /usr/share, come questo:

immagine

e debian/sampleproject.links devono essere cambiati:

immagine

Inoltre, faremo una nuova voce nel changelog:

dch -i

immagine

semplicemente modificare il testo, per riflettere un nuovo numero di versione, e una voce per il changelog.

Poi il progetto può essere costruito di nuovo:

dpkg-buildpackage -us -uc

Dopo l'installazione, potete verificare che effettivamente la nuova posizione sia utilizzata:

immagine

Il pacchetto occuperà circa 16 MB sul vostro sistema di destinazione, grazie all'ambiente virtuale e a tutto ciò che viene fornito con esso. Questo probabilmente potrebbe essere ridotto da alcuni parametri, ma questo esercizio è lasciato al lettore.

Divertitevi a impacchettare!