Empacotando projetos Python para Debian / Raspbian com dh-virtualenv

Este artigo visa explicar algumas coisas aos desenvolvedores que não usam muito Python, e podem ter dificuldades com alguns dos conceitos de outra forma.

Recomendo vivamente a palestra do artigo seguinte como uma introdução aos conceitos aqui discutidos:

pypi.org

pypi.org é um repositório oficial de software para a linguagem de programação Python. Ele inclui bibliotecas que queremos usar em nossos projetos, que não são distribuídas por padrão com o Python.

O problema

Nosso objetivo é empacotar uma aplicação escrita em Python (tendo como alvo o Python3) como um pacote .deb para o Raspbian (que é, tudo considerado, realmente equivalente ao Debian).

A nossa aplicação tem dependências em várias bibliotecas Python.

Por exemplo, gostaríamos de usar o solicitações biblioteca. A própria biblioteca de pedidos depende de outras bibliotecas, por exemplo, chardet, urllib3, etc.

A gestão manual destas dependências e o envio manual do código de biblioteca necessário é intrinsecamente impraticável.

pip

Python tem o seu próprio sistema de gestão de pacotes / dependências, chamado pip

pip permite-lhe instalar bibliotecas e aplicações python, para que o seu próprio código possa utilizar estas bibliotecas.

Aqui há dois problemas:

  • queremos enviar o pacote como um pacote Debianpara que os utilizadores finais possam instalar facilmente. Podemos assumir com segurança que num número significativo de sistemas alvo as bibliotecas python apropriadas não terão sido instaladas pelo utilizador.
  • se tentarmos instalar estas bibliotecas em todo o sistema em nome do utilizadorpodemos quebrar a compatibilidade com alguns outros pacotes (por exemplo, devido a alterações de API entre as versões das bibliotecas).

Portanto, precisamos de uma solução.

virtualenv

virtualenv vai permitir-nos criar ambientes virtuais para Python.

Nesses ambientes virtuais que temos:

  • o nosso próprio intérprete Python
  • cópias das bibliotecas padrão necessárias
  • nosso próprio canal
  • a nossa própria biblioteca "de todo o sistema

Parte prática: Experimentar o virtualenv

Instalar virtualenv (como parte do dh-virtualenv, o que vamos fazer dentro de pouco tempo) fazendo:

sudo apt install dh-virtualenv

Como os pedidos já estão instalados em todo o sistema na minha caixa de teste, vamos usar um pacote diferente como exemplo. randstr permitirá que você gere uma cadeia aleatória.

Verifique se randstr está instalado em todo o sistema:

pip3 mostrar randstr

Isto não deve devolver nada. Indicando que este pacote não está instalado em todo o sistema:

imagem

Agora vamos criar um ambiente virtual. A pasta de ambiente virtual NÃO tem que viver dentro da sua pasta de código, mas pode. Você deve ignorá-la no GIT.

digamos que temos um diretório /home/pi/tutorial

Mude para este diretório, e crie uma nova pasta ambiente env:

cd /home/pi/tutorial

que python3

virtualenv -p /usr/bin/python3 env

Nota: você deve especificar o intérprete Python se você quiser Python 3, caso contrário Python 2 será usado. Eu estou usando Python 3. (Executando virtualenv com intérprete /usr/bin/python2)

que python3 lhe dirá onde reside o binário python3, ajuste o caminho para o virtualenv em conformidade.

imagem

activar o ambiente:

fonte env/bin/activar

(Este caminho de comando assume que você permaneceu no diretório do tutorial). Note também, que este não é um script executável - você tem que usar o fonte para o usar com o seu terminal de bash.

Repare, como (env) é preparado antes do seu prompt de comando:

imagem

Isto significa que o ambiente está ativo. Agora você pode executar pip3 novamente, mas desta vez pip3 vai executar o seu trabalho no ambiente virtual:

pip3 mostrar randstr

pip3 mostrar pedidos

ainda não deve render nada. A segunda linha também não mostrará nada - mostrando que (se os pedidos forem instalados em todo o sistema) estamos de fato em um ambiente virtual.

Agora instale o randstr:

pip3 instalar randstr

e verificar novamente, se está instalado:

pip3 mostrar randstr

imagem

Desta vez mostra que o randstr está instalado. Ele foi instalado em env/lib/python3.5/site-packages:

imagem

Enquanto o ambiente ainda está ativado, vamos escrever uma pequena aplicação de amostra Python:

nano amostra.py

cole o seguinte código, gerando uma string aleatória usando a nova biblioteca que acabamos de instalar:

de randstr importação randstr
print("olá mundo")
print(randstr())

e salvar.

Execute o código usando o python3:

python3 amostra.py

enquanto o ambiente ainda estiver activo, isto vai funcionar:

imagem

Agora é hora de sair do ambiente, para ver se o código ainda vai funcionar:

desativar

O seu pedido voltará ao normal. E

python3 amostra.py

irá lançar uma mensagem de erro, sobre nenhum módulo 'randstr' existente no seu sistema. Este é exactamente o comportamento que nós queremos!

imagem

Você pode dar uma olhada na pasta env, vendo como ela envia todas as coisas necessárias para executar seu código Python, incluindo bibliotecas, o executável python, pip, e muito mais.

Empacotamento Debian virtualenv com dh-virtualenv

OK, agora com o básico fora do caminho, o que queremos é empacotar um virtualenv junto com o nosso código, para que a nossa aplicação seja executada de forma confiável e sem perturbar outras aplicações nos computadores dos nossos usuários.

Aqui é onde dh-virtualenv entra na imagem. dh-virtualenv é uma adição aos scripts de construção Debian, que lhe permite empacotar ambientes virtuais.

Precisamos de criar uma estrutura básica para o nosso novo pacote. Aqui é onde cookiecutter vai ajudar.

cookiecutter

O cookiecutter cria novos projetos para você a partir de modelos, reduzindo assim as despesas gerais e o tempo que você gasta com o desenvolvimento e a organização dos muitos arquivos que são necessários.

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

sudo apt instalar o cookiecutter

Nota: o pacote python3-cookiecutter irá fornecer um módulo Python para o Python 3. O que queremos é a aplicação em si, que é instalada com o pacote cookiecutter, como visto acima. Isto pode puxar no Python 2, mas - oh bem.

O cookiecutter não é necessário para seus usuários finais, ele é usado apenas para criar vários arquivos para seu pacote.

criar um exemplo de diretório de projetos, no qual aplicaremos o modelo (eu ainda estou no diretório tutorial):

projeto de amostra mkdir

cd sampleproject

Usando um molde especial para dh-virtualenv podemos montar muitos dos arquivos necessários:

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

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

Isto vai fazer-lhe várias perguntas.

imagem

Note que a pasta deve ser nomeada debian, mesmo quando embalada para Raspbian - portanto mantenha o nome como debian.

Isto irá instalar os seguintes ficheiros:

imagem

Agora você precisa executar os seguintes comandos, de acordo com as instruções sob o molde dh-virtualenv:

sudo apt-get install build-essential debhelper devscripts equivs
sudo mk-build-deps --install debian/control
Para construir o pacote mais tarde, você executará o comando follwoing a partir do diretório de nível superior do seu projeto:
dpkg-buildpackage -uc -us -b
Neste momento, porém, o comando falhará porque o setup.py está faltando. Vamos chegar a setup.py e requirements.txt dentro de pouco tempo:
/usr/bin/python3: não consegue abrir o ficheiro 'setup.py': [Errno 2] Não há tal arquivo ou diretório

Informações sobre os arquivos individuais

changelog

Este arquivo conterá o seu lançamento e informações da versão para o pacote. Você pode atualizá-lo usando uma ferramenta especial, dch.

Este arquivo não é um changelog para a sua aplicação, mas apenas um changelog para a embalagem da sua aplicação.

compatriota

Este arquivo inclui um número mágico especial "9". (para questões de compatibilidade, não há problema em deixá-lo como está)

controle

Este é o arquivo principal para definir as configurações dos pacotes, e as dependências em um nível Debian. Se a sua aplicação depende, por exemplo, do omxplayer a ser instalado, ela precisará entrar aqui como uma dependência:

Fonte: projecto de amostra
Secção: contrib/python
Prioridade: extra
Mantenedor: Maximilian Batz
Build-Depends: debhelper (>= 9), python, python-dev, dh-virtualenv (>= 0.10), tar
Padrão-Versão: 3.9.5
Página inicial:
https://picockpit.com


Pacote: projecto de amostra
Arquitetura: qualquer
Pré-Depende: dpkg (>= 1.16.1), python2.7 | python3, ${misc:Pré-Depende}
Depende: ${python:Depends}, ${misc:Depends}
Descrição: Um pacote de exemplo para demonstrar o empacotamento Debian virtualenv com o dh-virtualenv
     .
     Esta é uma distribuição de "projecto de amostra" como um projecto auto-contido
     Python virtualenv embrulhado em um pacote Debian (pacote "omnibus"),
     todos os passageiros a bordo). O virtualenv empacotado é mantido em sincronia com
     o intérprete do anfitrião automaticamente.
     .
     Ver
https://github.com/spotify/dh-virtualenv para mais detalhes.

Você também vai querer editar a longa Descrição.

cookiecutter.json

Inclui as suas configurações iniciais. Não é necessário para embalagem.

direitos autorais

Seu arquivo de direitos autorais, com sua licença (por exemplo, MIT). É pré-preenchido com o seu nome, ano, e-Mail e "alguns direitos reservados".

regras

Isto é um Makefile, para realmente construir o seu pacote. Ele é pré-preenchido pelo modelo do coolkiecutter.

exemploprojeto.links

Este arquivo permite que você crie links durante a instalação . O nome do arquivo incluirá o nome do seu projeto ao invés de projecto de amostra.

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

exemplo de projeto.postinst

Este arquivo permite que você execute passos adicionais de configuração após a instalação (por exemplo, ativando seu script python como um serviço). O nome do arquivo incluirá o nome do seu projeto ao invés de projecto de amostra.

gatilhos.de.amostra

Tanto quanto sei, isto é para a dh-virtualenv instalar um novo intérprete python no ambiente virtual para enviar, se o sistema (sua caixa de desenvolvimento) for atualizado. O nome do arquivo incluirá o nome do seu projeto ao invés de projecto de amostra.

Este pode ou não ser o caso, eu não o testei.

setup.py

Como mencionado acima, nós precisamos de um setup.py arquivo. Esta é a forma padrão de empacotamento das aplicações em Python (elas são enviadas com um arquivo setup.py, que é executado para várias tarefas relacionadas ao empacotamento).

Um simples setup.py pode ser visto nesta página:

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

Nem todas as entradas nestes arquivos são necessárias. Por exemplo, você pode deixar de fora os classificadores. Eu tenho o seguinte setup.py:

imagem

Nota: o número da versão e outras informações aqui dentro são para o seu pacote Python (que será criado durante a construção de um pacote Debian com o seu virtualenv).

Estrutura para o seu código

O seu código irá agora viver num subdirectório do directório principal do projecto de amostra:

imagem

Neste caso, o subdiretório tem o mesmo nome do diretório principal.

Note o __init__.py

A sample.py é a que usamos acima, mas um pouco retrabalhada para ficar assim:

imagem

Além disso, configurei um ambiente virtual para testes enquanto desenvolvia dentro do diretório principal (nível superior) do projeto de amostra:

virtualenv -p /usr/bin/python3 sp_env

chamado sp_env para ambiente de projeto modelo

A entrar no ambiente:

fonte sp_env/bin/activate

Instale a biblioteca randstr neste ambiente:

pip3 instalar randstr

e agora podemos executar o código:

python3 amostraprojeto/amostra.py

imagem

ESTÁ BEM. Sair do ambiente (! importante!)e tentar construir o pacote novamente:

desativar

dpkg-buildpackage -us -uc

Por padrão (por causa do cortador de cookies), seu pacote será criado para instalação em /opt/venvs/:

Novo python executável em /home/pi/tutorial/sampleproject/debian/sampleproject/opt/venvs/sampleproject/bin/python3

Isto pode ser alterado no debian/regras arquivo.

Agora o pacote foi construído, e depositado fora do diretório de topo do projeto de exemplo (dentro do diretório tutorial):

imagem

Você pode instalar o arquivo .deb usando o dpkg:

sudo dpkg -i sampleproject_0.2.1+nmu1_armhf.deb

Veja os resultados da instalação com

árvore /opt/venvs

127 diretórios e 945 arquivos foram instalados.

Agora você pode tentar executar o projeto de amostra usando

/opt/venvs/sampleproject/bin/sampleproject

requisitos / dependências

A aplicação não será executada como (talvez) esperado, mas atira um erro sobre o randstr não estar presente. O que dá?

imagem

A razão é que a dh-virtualenv não sabe sobre os requisitos que temos, para empacotar o pacote randstr durante o processo de construção. Esta é a peça final do puzzle.

Os requisitos precisam de ser acrescentados ao setup.pycomo uma forma de informar ao pip quais pacotes adicionais precisam ser instalados no ambiente virtual que está sendo criado.

adicione o seguinte ao setup.py:

install_requires=[

        'randstr'

]

O arquivo inteiro então vai ficar assim:

imagem

Nos meus testes os requisitos.txt colocados ao nível superior fizeram não influenciam o comportamento do dh-virtualenv e dos pacotes Python realmente incluídos no pacote Debian. Se você ainda o quer, veja como:

pip oferece-lhe uma forma de colocar para fora as dependências exatas que você usou para se desenvolver no ambiente virtual. Entre no ambiente virtual:

fonte sp_env/bin/activate

E crie o arquivo requirements.txt:

pip3 freeze > requirements.txt

Agora você pode dar uma olhada nesse arquivo:

cat requirements.txt

imagem

Mesmo que este arquivo pareça não ser utilizado pelo dh-virtualenv (talvez ele precise ir em um subdiretório diferente), ele é uma boa referência para você mesmo, para ver quais pacotes você deve colocar como dependências na parte install_requires do setup.py.

Note que o arquivo sampleproject.links nos ofereceu uma maneira conveniente de vincular isso na pasta /usr/bin, portanto removerei o sinal de comentário para a próxima compilação:

imagem

Com isso fora do caminho, é hora de purgar o projeto de amostra, e construí-lo novamente, e depois instalá-lo. Não se esqueça de desativar primeiro!

desativar

sudo apt purge sampleproject

dpkg-buildpackage -us -uc

cd ...

sudo dpkg -i sampleproject_0.2.1+nmu1_armhf.deb

Desta vez, o pacote deve estar acessível utilizando o projecto de amostra:

que projecto de amostra

projecto de amostra

imagem

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

Dica de bónus: mudar o local de instalação

O /opt/venvs não é um "lugar" muito Debian para se colocar um pacote.

As especificações estão aqui:

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 é não para embalagens
  • /opt não é para pacotes Debian propriamente ditos (é para software de terceiros instalado pelo usuário)

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

Os aplicativos podem usar um único subdiretório em /usr/lib. Se uma aplicação utiliza um subdiretório, todos os dados dependentes de arquitetura utilizados exclusivamente pela aplicação devem ser colocados dentro desse subdiretório.

https://wiki.debian.org/HowToPackageForDebian

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

"O requisito FHS de que os arquivos estáticos independentes da arquitetura de aplicação específica estejam localizados em /usr/acção é relaxado a uma sugestão. Em particular, um subdiretório de /usr/lib pode ser usado por um pacote (ou uma coleção de pacotes) para manter uma mistura de arquivos independentes e dependentes da arquitetura. Entretanto, quando um diretório é inteiramente composto de arquivos independentes de arquitetura, ele deve estar localizado em /usr/acção.”

A melhor localização para mim parece ser /usr/share

Precisamos de editar dois ficheiros:

em debian/rules DH_VIRTUALENV_INSTALL_ROOT deve ser alterado para /usr/share, desta forma:

imagem

e debian/sampleproject.links devem ser alterados:

imagem

Além disso, vamos fazer uma nova entrada no changelog:

dch -i

imagem

basta editar o texto, para refletir um novo número de versão, e uma entrada para o changelog.

Então o projeto pode ser construído novamente:

dpkg-buildpackage -us -uc

Após a instalação, você pode verificar se de fato o novo local é utilizado:

imagem

O pacote irá ocupar cerca de 16 MB no seu sistema alvo, graças ao ambiente virtual e a tudo o que é enviado com ele. Isso provavelmente poderia ser reduzido por alguns parâmetros, mas esse exercício é deixado para o leitor.

Divirta-se com a embalagem!