requisitos.txt vs setup.py

111

Comecei a trabalhar com Python. Eu adicionei requirements.txte setup.pyao meu projeto. Mas, ainda estou confuso sobre o propósito de ambos os arquivos. Eu li que setup.pyfoi projetado para coisas redistribuíveis e que requirements.txté projetado para coisas não redistribuíveis. Mas não estou certo de que isso seja correto.

Como esses dois arquivos realmente devem ser usados?

lucy
fonte
1
Você pesquisou na web usando seu título exato? Este artigo (o primeiro acerto quando fiz uma pesquisa) é o melhor que li sobre o assunto.
Chris
2
Este artigo pode ser útil: caremad.io/posts/2013/07/setup-vs-requirement (desculpe, preguiça de extrair o essencial em uma resposta adequada). Outra coisa é que algumas ferramentas (por exemplo, testes) podem ter suas tendências em relação a uma ou outra - mas não deixe que isso o incomode se você acabou de começar a trabalhar no Python.
drdaeman

Respostas:

83

requisitos.txt

Isso ajuda você a configurar seu ambiente de desenvolvimento. Programas como pippodem ser usados ​​para instalar todos os pacotes listados no arquivo de uma só vez. Depois disso, você pode começar a desenvolver seu script Python. Especialmente útil se você planeja ter outras pessoas contribuindo para o desenvolvimento ou usando ambientes virtuais. É assim que você o usa:

pip install -r requirements.txt

setup.py

Isso permite que você crie pacotes que você pode redistribuir. Este script destina-se a instalar seu pacote no sistema do usuário final, não a preparar o ambiente de desenvolvimento como o pip install -r requirements.txtfaz. Veja esta resposta para mais detalhes em setup.py.

As dependências do seu projeto são listadas em ambos os arquivos.

AndreasT
fonte
2
Em quais casos eu teria apenas um deles? Em que eu teria os dois?
Martin Thoma
27
Erm ... você apenas script para se divertir em sua máquina local: Nenhum. O script é desenvolvido em várias máquinas / vitualenvs, mas não redistribuído: requirements.txt. O script é desenvolvido apenas em sua máquina, mas deve ser redistribuído: setup.py. O script será redistribuído e desenvolvido em vários ambientes: Ambos.
AndreasT
Você poderia adicionar isso à resposta?
Martin Thoma
58

A resposta curta requirements.txté para listar apenas os requisitos do pacote. setup.pypor outro lado, é mais como um script de instalação. Se você não planeja instalar o código python, normalmente você só precisa requirements.txt.

O arquivo setup.pydescreve, além das dependências do pacote, o conjunto de arquivos e módulos que devem ser empacotados (ou compilados, no caso de módulos nativos (ou seja, escritos em C)) e metadados para adicionar às listagens de pacotes python ( por exemplo, nome do pacote, versão do pacote, descrição do pacote, autor, ...).

Como os dois arquivos listam dependências, isso pode causar um pouco de duplicação. Leia abaixo para obter detalhes.

requisitos.txt


Este arquivo lista os requisitos do pacote Python. É um arquivo de texto simples (opcionalmente com comentários) que lista as dependências do pacote do seu projeto Python (uma por linha). Ele não descrever a maneira em que seu pacote python está instalado. Você geralmente consumiria o arquivo de requisitos com pip install -r requirements.txt.

O nome do arquivo de texto é arbitrário, mas geralmente é requirements.txtpor convenção. Ao explorar repositórios de código-fonte de outros pacotes Python, você pode topar com outros nomes, como dev-dependencies.txtou dependencies-dev.txt. Eles têm a mesma finalidade, dependencies.txtmas geralmente listam dependências adicionais de interesse para os desenvolvedores do pacote específico, a saber, para testar o código-fonte (por exemplo, pytest, pylint, etc.) antes do lançamento. Os usuários do pacote geralmente não precisam de todo o conjunto de dependências do desenvolvedor para executar o pacote.

Se várias requirements-X.txtvariantes estiverem presentes, geralmente uma listará as dependências de tempo de execução e a outra de tempo de construção ou dependências de teste. Alguns projetos também distribuem seus arquivos de requisitos em cascata, ou seja, quando um arquivo de requisitos inclui outro arquivo ( exemplo ). Isso pode reduzir a repetição.

setup.py


Este é um script Python que usa o setuptoolsmódulo para definir um pacote Python (nome, arquivos incluídos, metadados do pacote e instalação). Irá, como requirements.txt, também listar dependências de tempo de execução do pacote. Setuptools é a maneira de fato de construir e instalar pacotes Python, mas tem suas deficiências, que com o tempo geraram o desenvolvimento de novos "gerenciadores de meta-pacotes", como o pip. As deficiências de exemplo das ferramentas de instalação são a incapacidade de instalar várias versões do mesmo pacote e a falta de um comando de desinstalação.

Quando um usuário Python faz pip install ./pkgdir_my_module(ou pip install my-module), o pip será executado setup.pyno diretório (ou módulo) fornecido. Da mesma forma, qualquer módulo que tenha um setup.pypode ser pipinstalado, por exemplo, executando a pip install .partir da mesma pasta.

Eu realmente preciso de ambos?


A resposta curta é não, mas é bom ter os dois. Eles alcançam finalidades diferentes, mas podem ser usados ​​para listar suas dependências.

Há um truque que você pode considerar para evitar a duplicação de sua lista de dependências entre requirements.txte setup.py. Se você já escreveu um totalmente funcional setup.pypara o seu pacote e suas dependências são principalmente externas, você pode considerar ter um simples requirements.txtcom apenas o seguinte:

 # requirements.txt
 #
 # installs dependencies from ./setup.py, and the package itself,
 # in editable mode
 -e .

 # (the -e above is optional). you could also just install the package
 # normally with just the line below (after uncommenting)
 # .

O -eé uma pip installopção especial que instala o pacote fornecido em modo editável . Quando pip -r requirements.txtfor executado neste arquivo, o pip instalará suas dependências por meio da lista em ./setup.py. A opção editável colocará um link simbólico em seu diretório de instalação (em vez de um ovo ou cópia arquivada). Ele permite que os desenvolvedores editem o código no local do repositório sem reinstalar.

Você também pode tirar proveito do que é chamado de "extras de ferramentas de instalação" quando você tem ambos os arquivos em seu repositório de pacotes. Você pode definir pacotes opcionais em setup.py em uma categoria personalizada e instalar esses pacotes apenas dessa categoria com pip:

# setup.py
from setuptools import setup
setup(
   name="FOO"
   ...
   extras_require = {
       'dev': ['pylint'],
       'build': ['requests']
   }
   ...
)

e então, no arquivo de requisitos:

# install packages in the [build] category, from setup.py
# (path/to/mypkg is the directory where setup.py is)
-e path/to/mypkg[build]

Isso manteria todas as suas listas de dependências dentro de setup.py.

Observação : você normalmente executaria pip e setup.py a partir de uma sandbox, como as criadas com o programa virtualenv. Isso evitará a instalação de pacotes Python fora do contexto do ambiente de desenvolvimento do seu projeto.

init_js
fonte
7
e você também pode ter apenas .w / o -edentro requirements.txt. Este método apenas delega todos os requisitos setup.pye você não precisa forçar ninguém a entrar no modo editável. Os usuários ainda podem fazer, pip install -e .se quiserem.
stason de
1
Truque interessante com "-e". em requirements.txt, mas isso não invalida o propósito de requirements.txt ser as especificações exatas do sistema? Por que mesmo ter um nesse caso?
Ben Ogorek
Você pode ter os requisitos de sistema exatos em setup.py. Tendo "." em requirements.txt faz com que ele use o setup.py na pasta atual. Usar -e .também usa setup.py para encontrar dependências, mas vincula a pasta atual (no local, com um link simbólico) na pasta de instalação do pip, em vez de fazer uma cópia - -egeralmente você usaria apenas se estiver desenvolvendo o pacote. Com -e, as alterações nos arquivos do pacote Python (* .py) entrariam em vigor imediatamente no ambiente pip, em vez de forçar a reinstalação do pacote após cada alteração.
init_js
@init_js é a "pasta atual" relativa ao arquivo de requisitos ou CWD a partir do qual o pip é chamado? Ou seja, se você fizer cd foo && pip install -r ./bar/requirements.txtisso, ele pesquisará setup.py em foo/barou foo? Nesse último caso, há uma maneira de alcançar o primeiro?
Dan M.
pip -r REQnão se preocupa com o diretório em que REQ está. Você pode alimentá-lo a partir de um fifo mesmo se você quiser: pip install -r <(echo "mylib1"; echo "mylib2";). Onde <(CMD)está a substituição do comando bash, não o redirecionamento stdin.
init_js
12

Para fins de integridade, aqui está como eu vejo isso em 3 4 ângulos diferentes.

  1. Seus propósitos de design são diferentes

Esta é a descrição precisa citada da documentação oficial (grifo meu):

Enquanto install_requires (em setup.py) define as dependências para um único projeto , Arquivos de Requisitos são freqüentemente usados ​​para definir os requisitos para um ambiente Python completo .

Enquanto os requisitos de install_requires são mínimos, os arquivos de requisitos geralmente contêm uma lista exaustiva de versões fixadas com o objetivo de obter instalações repetíveis de um ambiente completo.

Mas ainda pode não ser fácil de ser entendido, então, na próxima seção, virão 2 exemplos factuais para demonstrar como as 2 abordagens devem ser usadas de forma diferente.

  1. Seus usos reais são, portanto (supostamente) diferentes

    • Se o seu projeto foovai ser lançado como uma biblioteca autônoma (ou seja, outras provavelmente o fariam import foo), então você (e seus usuários posteriores) gostariam de ter uma declaração flexível de dependência, para que sua biblioteca não fosse (e não deve ) ser "exigente" sobre qual versão exata de SUAS dependências deve ser. Portanto, normalmente, seu setup.py contém linhas como esta:

      install_requires=[
          'A>=1,<2',
          'B>=2'
      ]
    • Se você deseja apenas "documentar" ou "fixar" seu ambiente atual EXATO para seu aplicativo bar, ou seja, você ou seus usuários gostariam de usar seu aplicativo barcomo está, ou seja python bar.py, em execução , você pode querer congelar seu ambiente para que ele sempre se comportaria da mesma forma. Nesse caso, seu arquivo de requisitos ficaria assim:

      A==1.2.3
      B==2.3.4
      # It could even contain some dependencies NOT strickly required by your library
      pylint==3.4.5
  2. Na verdade, qual devo usar?

    • Se você estiver desenvolvendo um aplicativo barque será usado por python bar.py, mesmo que seja "apenas um script por diversão", ainda é recomendado usar o arquivo requirements.txt porque, quem sabe, na próxima semana (que por acaso é o Natal) você receberá um novo computador como um presente, então você precisaria configurar seu ambiente exato lá novamente.

    • Se você estiver desenvolvendo uma biblioteca fooque será usada por import foo, deverá preparar um setup.py. Período. Mas você ainda pode optar por fornecer também um requirements.txt ao mesmo tempo, que pode:

      (a) seja no A==1.2.3estilo (conforme explicado no item 2 acima);

      (b) ou apenas conter um único mágico .

      .

      que seria aproximadamente igual a "instalar os requisitos com base em setup.py", mas sem duplicação. Pessoalmente, considero que essa última abordagem confunde os limites, aumenta a confusão e NÃO realmente agrega valor, mas, mesmo assim, é um truque derivado de uma abordagem mencionada pelo mantenedor do pacote Python, Donald, em seu blog .

  3. Limites inferiores diferentes.

    Mesmo depois de ter seguido os 3 critérios acima e decidido corretamente que sua biblioteca hybrid-engineusaria um setup.pypara declarar sua dependência engine>=1.2.0, e seu aplicativo de amostra reliable-carusaria requirements.txtpara declarar sua dependência engine>=1.2.3, embora a versão mais recente do enginejá esteja em 1.4.0. Como você pode ver, sua escolha para o número do limite inferior ainda é sutilmente diferente. E aqui está o porquê.

    • hybrid-enginedepende de engine>=1.2.0porque, hipoteticamente falando, a capacidade de "combustão interna" necessária foi introduzida pela primeira vez em engine 1.2.0, e essa capacidade é a necessidade de hybrid-engine, independentemente de haver alguns (menores) bugs dentro de tal versão e foram corrigidos nas versões subsequentes 1.2.1 , 1.2.2 e 1.2.3.

    • reliable-cardepende de engine>=1.2.3porque essa é a versão mais antiga SEM problemas conhecidos, até agora. Claro, há novos recursos em versões posteriores, digamos, "motor elétrico" introduzido engine 1.3.0e "reator nuclear" introduzido engine 1.4.0, mas eles não são necessários para o projeto reliable-car.

RayLuo
fonte
"sua biblioteca não seria (e não deve) ser 'exigente' sobre qual versão exata de SUAS dependências deveria ser." Você poderia elaborar um pouco sobre este ponto? Eu acho que seu código é normalmente testado apenas com versões específicas de dependências, e essa abordagem pode ser um pouco perigosa. Presumo que uma biblioteca deva funcionar com uma variedade de versões porque você não deseja instalar muitas versões de dependências. Para economizar espaço em disco?
Taro Kiritani
@TaroKiritani Na verdade, listei 2 cenários diferentes lado a lado, o caso da biblioteca e o caso da aplicação. Talvez você não tenha trabalhado em uma biblioteca antes? Como uma biblioteca, espera-se que seja consumida por pacotes downstream. Portanto, se você for exigente em fixar SUA dependência A==1.2.3e, em seguida, se o pacote downstream de sua biblioteca depender A==1.2.4, agora não haverá uma maneira de satisfazer ambos. A solução para minimizar esse conflito é sua biblioteca definir um intervalo que você sabe que funcionaria. Supondo que muitas bibliotecas upstream já sigam semver.org , A>=1,<2funcionaria.
RayLuo
Não sabia que apenas uma versão de um pacote pode ser instalada em um único ambiente. stackoverflow.com/a/6572017/5686692 Obrigado pelo esclarecimento.
Taro Kiritani
1
@TaroKiritani, sim, caso contrário, como é que a sua aplicação saber qual a versão do fooque import foodar-lhe? Aquela resposta hacky aceita naquele link que você forneceu serve como um exemplo perfeito de porque o mantenedor do pacote "não deve e não deve ser exigente". :-) Agora posso ter seu voto positivo?
RayLuo
1
Eu também poderia comentar sobre esse novo pensamento, mas essa seção de comentários já está saindo do assunto e é difícil para os novatos seguirem. Eu sugiro que você faça uma nova pergunta "Devemos usar tox ou algo assim para garantir que minha biblioteca funcione em várias combinações de dependências", e então as pessoas podem
intervir