requirements.txt contre setup.py

111

J'ai commencé à travailler avec Python. J'ai ajouté requirements.txtet setup.pyà mon projet. Mais je suis toujours confus quant au but des deux fichiers. J'ai lu qui setup.pyest conçu pour les choses redistribuables et qui requirements.txtest conçu pour les choses non redistribuables. Mais je ne suis pas certain que ce soit exact.

Comment ces deux fichiers sont-ils vraiment destinés à être utilisés?

Lucy
la source
1
Avez-vous effectué une recherche sur le Web en utilisant votre titre exact? Cet article (le premier hit quand j'ai cherché) est le meilleur que j'ai lu sur le sujet.
Chris
2
Cet article pourrait être utile: caremad.io/posts/2013/07/setup-vs-requirement (désolé, trop paresseux pour extraire l'essentiel dans une bonne réponse). Une autre chose est que certains outils (par exemple les tests) peuvent avoir leurs préjugés envers l'un ou l'autre - mais ne vous laissez pas déranger si vous venez de commencer à travailler sur Python.
drdaeman

Réponses:

83

requirements.txt

Cela vous aide à configurer votre environnement de développement. Des programmes tels que pippeuvent être utilisés pour installer tous les packages répertoriés dans le fichier d'un seul coup. Après cela, vous pouvez commencer à développer votre script python. Particulièrement utile si vous prévoyez que d'autres contribuent au développement ou utilisent des environnements virtuels. Voici comment vous l'utilisez:

pip install -r requirements.txt

setup.py

Cela vous permet de créer des packages que vous pouvez redistribuer. Ce script est destiné à installer votre package sur le système de l'utilisateur final et non à préparer l'environnement de développement comme le pip install -r requirements.txtfait. Consultez cette réponse pour plus de détails sur setup.py.

Les dépendances de votre projet sont répertoriées dans les deux fichiers.

AndreasT
la source
2
Dans quels cas n'en aurais-je qu'un seul? Dans lequel aurais-je les deux?
Martin Thoma
27
Euh ... vous venez de script pour vous amuser sur votre machine locale: Ni l'un ni l'autre. Le script est développé sur plusieurs machines / vitualenvs mais pas redistribué: requirements.txt. Le script est développé uniquement sur votre machine mais doit être redistribué: setup.py. Le script sera redistribué et développé dans plusieurs environnements: les deux.
AndreasT
Pourriez-vous ajouter ceci à la réponse?
Martin Thoma
58

La réponse courte est que requirements.txtc'est pour lister les exigences des packages uniquement. setup.pyd'autre part, c'est plus comme un script d'installation. Si vous ne prévoyez pas d'installer le code python, vous n'aurez généralement besoin que de requirements.txt.

Le fichier setup.pydécrit, en plus des dépendances du package, l'ensemble des fichiers et des modules qui doivent être packagés (ou compilés, dans le cas des modules natifs (c'est-à-dire écrits en C)), et les métadonnées à ajouter aux listes de packages python ( ex. nom du package, version du package, description du package, auteur, ...).

Étant donné que les deux fichiers répertorient les dépendances, cela peut entraîner un peu de duplication. Lisez ci-dessous pour plus de détails.

requirements.txt


Ce fichier répertorie les exigences du package Python. Il s'agit d'un fichier texte brut (éventuellement avec des commentaires) qui répertorie les dépendances de package de votre projet python (une par ligne). Il ne décrit pas la manière dont votre package python est installé. Vous consommez généralement le fichier des exigences avec pip install -r requirements.txt.

Le nom de fichier du fichier texte est arbitraire, mais est souvent requirements.txtpar convention. Lorsque vous explorez les référentiels de code source d'autres packages python, vous pouvez tomber sur d'autres noms, tels que dev-dependencies.txtou dependencies-dev.txt. Celles-ci ont le même objectif, dependencies.txtmais répertorient généralement des dépendances supplémentaires d'intérêt pour les développeurs du paquet particulier, à savoir pour tester le code source (par exemple pytest, pylint, etc.) avant la publication. Les utilisateurs du package n'ont généralement pas besoin de l'ensemble complet de dépendances de développeur pour exécuter le package.

Si plusieurs requirements-X.txtvariantes sont présentes, l'une répertoriera généralement les dépendances d'exécution et l'autre au moment de la construction ou testera les dépendances. Certains projets mettent également en cascade leur fichier d'exigences, c'est-à-dire lorsqu'un fichier d'exigences comprend un autre fichier ( exemple ). Cela peut réduire la répétition.

setup.py


Il s'agit d'un script python qui utilise le setuptoolsmodule pour définir un package python (nom, fichiers inclus, métadonnées du package et installation). Il listera requirements.txtégalement les dépendances d'exécution du package. Setuptools est le moyen de facto de construire et d'installer des paquets python, mais il a ses défauts, qui au fil du temps ont poussé au développement de nouveaux «gestionnaires de méta-paquets», comme pip. Des exemples de défauts de setuptools sont son incapacité à installer plusieurs versions du même package et l'absence de commande de désinstallation.

Lorsqu'un utilisateur python le fait pip install ./pkgdir_my_module(ou pip install my-module), pip s'exécutera setup.pydans le répertoire (ou module) donné. De même, tout module qui a un setup.pypeut être pip-installé, par exemple en s'exécutant à pip install .partir du même dossier.

Ai-je vraiment besoin des deux?


La réponse courte est non, mais c'est bien d'avoir les deux. Ils atteignent des objectifs différents, mais ils peuvent tous deux être utilisés pour répertorier vos dépendances.

Vous pouvez envisager une astuce pour éviter de dupliquer votre liste de dépendances entre requirements.txtet setup.py. Si vous avez déjà écrit un setup.pypackage entièrement fonctionnel pour votre package et que vos dépendances sont principalement externes, vous pouvez envisager d'avoir une version simple requirements.txtavec uniquement les éléments suivants:

 # 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)
 # .

Il -es'agit d'une pip installoption spéciale qui installe le package donné en mode modifiable . Quand pip -r requirements.txtest exécuté sur ce fichier, pip installera vos dépendances via la liste dans ./setup.py. L'option modifiable placera un lien symbolique dans votre répertoire d'installation (au lieu d'un œuf ou d'une copie archivée). Il permet aux développeurs de modifier le code en place à partir du référentiel sans le réinstaller.

Vous pouvez également profiter de ce qu'on appelle les "extras setuptools" lorsque vous avez les deux fichiers dans votre référentiel de packages. Vous pouvez définir des packages facultatifs dans setup.py sous une catégorie personnalisée et installer ces packages à partir de cette catégorie uniquement avec pip:

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

puis, dans le fichier des exigences:

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

Cela conserverait toutes vos listes de dépendances dans setup.py.

Remarque : vous exécutez normalement pip et setup.py à partir d'un bac à sable, comme ceux créés avec le programme virtualenv. Cela évitera d'installer des packages python en dehors du contexte de l'environnement de développement de votre projet.

init_js
la source
7
et vous pouvez également avoir juste .w / o à l' -eintérieur requirements.txt. Cette méthode délègue simplement toutes les exigences à setup.pyet vous n'avez pas besoin de forcer qui que ce soit en mode modifiable. Les utilisateurs peuvent toujours le faire pip install -e .s'ils le souhaitent.
stason
1
Truc intéressant avec "-e". dans requirements.txt, mais cela ne va-t-il pas à l'encontre du but d'exigences.txt étant les spécifications exactes du système? Pourquoi même en avoir un dans ce cas?
Ben Ogorek le
Vous pouvez avoir la configuration système requise exacte dans setup.py. Ayant "." dans requirements.txt, il utilise le setup.py dans le dossier actuel. L'utilisation -e .utilise également setup.py pour trouver les dépendances, mais lie le dossier actuel (en place, avec un lien symbolique) dans le dossier d'installation de pip, plutôt que de prendre une copie - vous n'utiliseriez -egénéralement que si vous développez le package. Avec -e, les modifications apportées à vos fichiers de package python (* .py) prendraient effet immédiatement dans votre environnement pip, plutôt que d'avoir à forcer la réinstallation du package après chaque modification.
init_js le
@init_js est le "dossier courant" par rapport au fichier d'exigences ou CWD à partir duquel pip est appelé? Ie si vous le faites cd foo && pip install -r ./bar/requirements.txtva - t-il rechercher setup.py dans foo/barou foo? Si c'est le dernier, y a-t-il un moyen d'atteindre le premier?
Dan M. le
pip -r REQne se soucie pas du répertoire dans lequel se trouve REQ. Vous pouvez l' alimenter à partir d' un fifo même si vous voulez: pip install -r <(echo "mylib1"; echo "mylib2";). Où <(CMD)est la substitution de commande bash, pas la redirection stdin.
init_js
12

Par souci d'exhaustivité, voici comment je le vois sous 3 4 angles différents.

  1. Leurs objectifs de conception sont différents

Voici la description précise citée de la documentation officielle (c'est moi qui souligne):

Alors que install_requires (dans setup.py) définit les dépendances pour un seul projet , les fichiers d'exigences sont souvent utilisés pour définir les exigences d'un environnement Python complet .

Alors que les exigences install_requires sont minimales, les fichiers d'exigences contiennent souvent une liste exhaustive des versions épinglées dans le but de réaliser des installations répétables d'un environnement complet.

Mais cela peut encore ne pas être facile à comprendre, donc dans la section suivante, il y a 2 exemples factuels pour démontrer comment les 2 approches sont censées être utilisées, différemment.

  1. Leurs usages réels sont donc (supposés être) différents

    • Si votre projet foodoit être publié en tant que bibliothèque autonome (ce qui signifie que d'autres le feraient probablement import foo), alors vous (et vos utilisateurs en aval) voudriez avoir une déclaration de dépendance flexible, de sorte que votre bibliothèque ne le fasse pas (et elle ne doit pas ) soyez "pointilleux" sur la version exacte de VOS dépendances. Donc, généralement, votre setup.py contient des lignes comme celle-ci:

      install_requires=[
          'A>=1,<2',
          'B>=2'
      ]
    • Si vous souhaitez simplement «documenter» ou «épingler» votre environnement actuel EXACT pour votre application bar, c'est-à-dire que vous ou vos utilisateurs souhaitez utiliser votre application telle quelle bar, c'est-à-dire en cours d'exécution python bar.py, vous pouvez geler votre environnement afin qu'il se comporterait toujours de la même manière. Dans ce cas, votre fichier d'exigences ressemblerait à ceci:

      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. En réalité, lequel dois-je utiliser?

    • Si vous développez une application barqui sera utilisée par python bar.py, même s'il ne s'agit que d'un "script pour le plaisir", il est toujours recommandé d'utiliser requirements.txt car, qui sait, la semaine prochaine (qui se trouve être Noël) vous recevrez un nouvel ordinateur en cadeau, vous devrez donc configurer à nouveau votre environnement exact.

    • Si vous développez une bibliothèque fooqui sera utilisée par import foo, vous devez préparer un setup.py. Période. Mais vous pouvez toujours choisir de fournir également un requirements.txt en même temps, ce qui peut:

      (a) soit être dans le A==1.2.3style (comme expliqué au point 2 ci-dessus);

      (b) ou contiennent simplement un single magique .

      .

      ce qui équivaudrait à peu près à "installer les exigences basées sur setup.py" sans duplication. Personnellement, je considère que cette dernière approche brouille la ligne, ajoute à la confusion et n'ajoute PAS vraiment de valeur, mais c'est néanmoins une astuce dérivée d'une approche mentionnée par le responsable de l'emballage Python, Donald dans son article de blog .

  3. Différentes limites inférieures.

    Même après avoir suivi les 3 critères ci-dessus et correctement décidé que votre bibliothèque hybrid-engineutiliserait a setup.pypour déclarer sa dépendance engine>=1.2.0, et que votre exemple d'application reliable-carutiliserait requirements.txtpour déclarer sa dépendance engine>=1.2.3, même si la dernière version de engineest déjà à 1.4.0. Comme vous le voyez, votre choix pour leur nombre limite inférieure est encore subtilement différent. Et voici pourquoi.

    • hybrid-enginedépend du engine>=1.2.0fait que, hypothétiquement parlant, la capacité de "combustion interne" nécessaire a été introduite pour la première fois dans engine 1.2.0, et cette capacité est la nécessité de hybrid-engine, indépendamment du fait qu'il puisse y avoir des bogues (mineurs) dans cette version et a été corrigée dans les versions ultérieures 1.2.1 , 1.2.2 et 1.2.3.

    • reliable-cardépend engine>=1.2.3parce que c'est la version la plus ancienne SANS problèmes connus, jusqu'à présent. Bien sûr, il y a de nouvelles capacités dans les versions ultérieures, par exemple, "moteur électrique" introduit engine 1.3.0et "réacteur nucléaire" introduit engine 1.4.0, mais elles ne sont pas nécessaires pour le projet reliable-car.

RayLuo
la source
"Votre bibliothèque ne serait pas (et ne doit pas) être" pointilleuse "sur la version exacte de VOS dépendances." Pourriez-vous nous en dire un peu plus sur ce point? Je suppose que votre code est généralement testé avec uniquement des versions spécifiques de dépendances, et cette approche peut être un peu dangereuse. Je suppose qu'une bibliothèque devrait fonctionner avec une gamme de versions car vous ne voulez pas installer trop de versions de dépendances? Pour économiser de l'espace disque?
Taro Kiritani
@TaroKiritani En fait, j'ai répertorié deux scénarios différents côte à côte, le cas de la bibliothèque et le cas de l'application. Peut-être n'avez-vous pas travaillé dans une bibliothèque avant? En tant que bibliothèque, elle devrait être consommée par les packages en aval. Donc, si vous êtes pointilleux pour épingler VOTRE dépendance A==1.2.3, puis si le package en aval de votre bibliothèque dépend A==1.2.4, il n'y aura plus moyen de satisfaire les deux. La solution pour minimiser ce conflit est que votre bibliothèque définit une plage dont vous savez qu'elle fonctionnera. En supposant que de nombreuses bibliothèques en amont suivent déjà semver.org , A>=1,<2cela fonctionnerait.
RayLuo
Je ne savais pas qu'une seule version d'un package pouvait être installée dans un seul environnement. stackoverflow.com/a/6572017/5686692 Merci pour la clarification.
Taro Kiritani
1
@TaroKiritani, oui, sinon comment votre savoir app quelle version de foone import foovous donner? La réponse acceptée par le piratage dans ce lien que vous avez fourni est un exemple parfait de la raison pour laquelle le responsable du paquet "ne devrait pas et ne doit pas être pointilleux". :-) Maintenant, puis-je avoir votre vote pour?
RayLuo
1
Je pourrais également commenter cette nouvelle pensée, mais cette section de commentaires est déjà hors sujet et difficile à suivre pour les nouveaux arrivants. Je vous suggère de poser une nouvelle question "Devons-nous utiliser tox ou quelque chose pour garantir que ma bibliothèque fonctionne sur diverses combinaisons de dépendances", et ensuite les gens peuvent intervenir.
RayLuo