requirements.txt vs setup.py

111

Empecé a trabajar con Python. He añadido requirements.txty setup.pya mi proyecto. Pero, todavía estoy confundido sobre el propósito de ambos archivos. He leído que setup.pyestá diseñado para cosas redistribuibles y que requirements.txtestá diseñado para cosas no redistribuibles. Pero no estoy seguro de que esto sea exacto.

¿Cómo se pretende que se utilicen realmente esos dos archivos?

lucy
fuente
1
¿Ha buscado en la web con su título exacto? Este artículo (el primer resultado cuando busqué) es el mejor que he leído sobre el tema.
Chris
2
Este artículo podría ser útil: caremad.io/posts/2013/07/setup-vs-requirement (lo siento, demasiado perezoso para extraer lo esencial en una respuesta adecuada). Otra cosa es que algunas herramientas (por ejemplo, las pruebas) pueden tener sus sesgos hacia una u otra, pero no dejes que te moleste si acabas de empezar a trabajar en Python.
drdaeman

Respuestas:

83

requirements.txt

Esto le ayuda a configurar su entorno de desarrollo. Programas como pipse pueden utilizar para instalar todos los paquetes enumerados en el archivo de una sola vez. Después de eso, puede comenzar a desarrollar su secuencia de comandos de Python. Especialmente útil si planea que otros contribuyan al desarrollo o utilicen entornos virtuales. Así es como se usa:

pip install -r requirements.txt

setup.py

Esto le permite crear paquetes que puede redistribuir. Este script está destinado a instalar su paquete en el sistema del usuario final, no a preparar el entorno de desarrollo comopip install -r requirements.txt hace. Consulte esta respuesta para obtener más detalles sobre setup.py.

Las dependencias de su proyecto se enumeran en ambos archivos.

AndreasT
fuente
2
¿En qué casos tendría solo uno de ellos? ¿En cuál tendría ambos?
Martin Thoma
27
Erm ... simplemente escribe por diversión en tu máquina local: Ninguno. El script se desarrolla en varias máquinas / vitualenvs pero no se redistribuye: requirements.txt. La secuencia de comandos se desarrolla solo en su máquina, pero debe redistribuirse: setup.py. El script se redistribuirá y desarrollará en múltiples entornos: ambos.
AndreasT
¿Podría agregar esto a la respuesta?
Martin Thoma
58

La respuesta corta es que solo requirements.txtsirve para enumerar los requisitos del paquete. setup.pypor otro lado, es más como un script de instalación. Si no planea instalar el código de Python, normalmente solo necesitarárequirements.txt .

El archivo setup.pydescribe, además de las dependencias del paquete, el conjunto de archivos y módulos que deben empaquetarse (o compilarse, en el caso de los módulos nativos (es decir, escritos en C)) y los metadatos para agregar a las listas de paquetes de Python ( por ejemplo, nombre del paquete, versión del paquete, descripción del paquete, autor, ...).

Debido a que ambos archivos enumeran dependencias, esto puede conducir a un poco de duplicación. Lea a continuación para obtener más detalles.

requirements.txt


Este archivo enumera los requisitos del paquete de Python. Es un archivo de texto sin formato (opcionalmente con comentarios) que enumera las dependencias del paquete de su proyecto de Python (una por línea). Que no describen la manera en la que está instalado el paquete python. Por lo general, consumiría el archivo de requisitos con pip install -r requirements.txt.

El nombre del archivo de texto es arbitrario, pero a menudo es requirements.txtpor convención. Al explorar repositorios de código fuente de otros paquetes de Python, es posible que se encuentre con otros nombres, como dev-dependencies.txto dependencies-dev.txt. Aquellos tienen el mismo propósito que, dependencies.txtpero generalmente enumeran dependencias adicionales de interés para los desarrolladores del paquete en particular, es decir, para probar el código fuente (por ejemplo, pytest, pylint, etc.) antes del lanzamiento. Los usuarios del paquete generalmente no necesitarían todo el conjunto de dependencias del desarrollador para ejecutar el paquete.

Si hay varias requirements-X.txtvariantes, normalmente una enumerará las dependencias de tiempo de ejecución y la otra el tiempo de compilación o las dependencias de prueba. Algunos proyectos también conectan en cascada su archivo de requisitos, es decir, cuando un archivo de requisitos incluye otro archivo ( ejemplo ). Hacerlo puede reducir la repetición.

setup.py


Este es un script de Python que usa el setuptoolsmódulo para definir un paquete de Python (nombre, archivos incluidos, metadatos del paquete e instalación). Será, como requirements.txt, también la lista de ejecución dependencias del paquete. Setuptools es la forma de facto de construir e instalar paquetes de Python, pero tiene sus deficiencias, que con el tiempo han hecho surgir el desarrollo de nuevos "administradores de metapaquetes", como pip. Ejemplos de deficiencias de setuptools son su incapacidad para instalar múltiples versiones del mismo paquete y la falta de un comando de desinstalación.

Cuando un usuario de Python lo hace pip install ./pkgdir_my_module(o pip install my-module), pip se ejecutará setup.pyen el directorio (o módulo) dado. De manera similar, cualquier módulo que tenga un setup.pyse puede pipinstalar, por ejemplo, ejecutándolo pip install .desde la misma carpeta.

¿Realmente necesito ambos?


La respuesta corta es no, pero es bueno tener ambos. Logran diferentes propósitos, pero ambos pueden usarse para enumerar sus dependencias.

Hay un truco que puede considerar para evitar duplicar su lista de dependencias entre requirements.txty setup.py. Si ya ha escrito un setup.pypaquete que funcione completamente y sus dependencias son en su mayoría externas, podría considerar tener un simple requirements.txtcon solo lo siguiente:

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

El -ees una especial pip installopción que instala el paquete dado en editable modo. Cuando pip -r requirements.txtse ejecuta en este archivo, pip instalará sus dependencias a través de la lista en ./setup.py. La opción editable colocará un enlace simbólico en su directorio de instalación (en lugar de un huevo o una copia archivada). Permite a los desarrolladores editar código en su lugar desde el repositorio sin reinstalarlo.

También puede aprovechar lo que se llama "extras de setuptools" cuando tiene ambos archivos en su repositorio de paquetes. Puede definir paquetes opcionales en setup.py en una categoría personalizada e instalar esos paquetes solo desde esa categoría con pip:

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

y luego, en el archivo 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]

Esto mantendría todas sus listas de dependencias dentro de setup.py.

Nota : normalmente, ejecutaría pip y setup.py desde una caja de arena, como las creadas con el programa virtualenv. Esto evitará instalar paquetes de Python fuera del contexto del entorno de desarrollo de su proyecto.

init_js
fuente
7
y también puedes tener solo .sin -eadentro requirements.txt. Este método simplemente delega todos los requisitos setup.pyy no necesita forzar a nadie al modo editable. Los usuarios todavía pueden hacerlo pip install -e .si quieren.
stason
1
Truco interesante con "-e". en requirements.txt, pero ¿no anula eso el propósito de requirements.txt siendo las especificaciones exactas del sistema? ¿Por qué incluso tener uno en ese caso?
Ben Ogorek
Puede tener los requisitos exactos del sistema dentro de setup.py. Teniendo "." en requirements.txt hace que utilice setup.py en la carpeta actual. El uso -e .también usa setup.py para encontrar dependencias, pero vincula la carpeta actual (en su lugar, con un enlace simbólico) en la carpeta de instalación de pip, en lugar de tomar una copia; -egeneralmente lo usaría solo si está desarrollando el paquete. Con -e, los cambios en los archivos de su paquete de Python (* .py) entrarían en vigencia inmediatamente en su entorno pip, en lugar de tener que forzar la reinstalación del paquete después de cada cambio.
init_js
@init_js es la "carpeta actual" relativa al archivo de requisitos o CWD desde el que se llama a pip? Es decir, si lo hace cd foo && pip install -r ./bar/requirements.txt, ¿ buscará setup.py en foo/baro foo? Si es lo último, ¿hay alguna manera de lograr lo primero?
Dan M.
pip -r REQno le importa el directorio en el que está REQ. Se puede alimentar desde un FIFO incluso si lo desea: pip install -r <(echo "mylib1"; echo "mylib2";). ¿Dónde <(CMD)está la sustitución del comando bash, no la redirección stdin?
init_js
12

En aras de la integridad, así es como lo veo en 3 4 ángulos diferentes.

  1. Sus propósitos de diseño son diferentes

Esta es la descripción precisa citada de la documentación oficial (el énfasis es mío):

Mientras que install_requires (en setup.py) define las dependencias para un solo proyecto , los archivos de requisitos se utilizan a menudo para definir los requisitos para un entorno Python completo .

Mientras que los requisitos de install_requires son mínimos, los archivos de requisitos a menudo contienen una lista exhaustiva de versiones ancladas con el fin de lograr instalaciones repetibles de un entorno completo.

Pero aún puede que no sea fácil de entender, por lo que en la siguiente sección, se incluyen 2 ejemplos fácticos para demostrar cómo se supone que los 2 enfoques deben usarse de manera diferente.

  1. Por lo tanto, sus usos reales son (se supone que son) diferentes

    • Si su proyecto foova a ser lanzado como una biblioteca independiente (es decir, otros probablemente lo harían import foo), entonces usted (y sus usuarios posteriores) querrían tener una declaración flexible de dependencia, de modo que su biblioteca no lo haría (y no debe ) Sea "quisquilloso" acerca de la versión exacta de SUS dependencias. Entonces, típicamente, su setup.py contendría líneas como esta:

      install_requires=[
          'A>=1,<2',
          'B>=2'
      ]
    • Si solo desea "documentar" o "fijar" de alguna manera su entorno EXACTO actual para su aplicación bar, es decir, usted o sus usuarios desearían usar su aplicación bartal como está, es decir python bar.py, ejecutándose , es posible que desee congelar su entorno para que siempre se comportaría igual. En tal caso, su archivo de requisitos se vería así:

      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 realidad, ¿cuál uso?

    • Si está desarrollando una aplicación barque será utilizada por python bar.py, incluso si es "solo un script por diversión", se le recomienda que use requirements.txt porque, quién sabe, la próxima semana (que es Navidad) recibirá un una computadora nueva como regalo, por lo que necesitaría configurar su entorno exacto allí nuevamente.

    • Si está desarrollando una biblioteca fooque será utilizada por import foo, debe preparar un setup.py. Período. Pero aún puede optar por proporcionar un requirements.txt al mismo tiempo, que puede:

      (a) estar en el A==1.2.3estilo (como se explica en el # 2 arriba);

      (b) o simplemente contener un sencillo mágico .

      .

      que equivaldría aproximadamente a "instalar los requisitos basados ​​en setup.py" sin duplicación. Personalmente, considero que este último enfoque desdibuja la línea, aumenta la confusión y NO agrega realmente valor, pero no obstante, es un truco derivado de un enfoque mencionado por el mantenedor de paquetes de Python, Donald, en su publicación de blog .

  3. Diferentes límites inferiores.

    Incluso después de haber seguido los 3 criterios anteriores y haber decidido correctamente que su biblioteca hybrid-engineusaría a setup.pypara declarar su dependencia engine>=1.2.0, y su aplicación de muestra reliable-carusaría requirements.txtpara declarar su dependencia engine>=1.2.3, aunque la última versión de engineya está en 1.4.0. Como puede ver, su elección para su número límite inferior sigue siendo sutilmente diferente. Y he aquí por qué.

    • hybrid-enginedepende de engine>=1.2.0porque, hipotéticamente hablando, la capacidad necesaria de "combustión interna" se introdujo por primera vez en engine 1.2.0, y esa capacidad es la necesidad dehybrid-engine , independientemente de si podría haber algunos errores (menores) dentro de dicha versión y se corrigió en versiones posteriores 1.2.1 , 1.2.2 y 1.2.3.

    • reliable-cardepende de engine>=1.2.3porque esa es la versión más antigua SIN problemas conocidos, hasta ahora. Seguro que hay nuevas capacidades en versiones posteriores, digamos, "motor eléctrico" introducido engine 1.3.0y "reactor nuclear" introducido engine 1.4.0, pero no son necesarios para el proyecto reliable-car.

RayLuo
fuente
"su biblioteca no sería (y no debe) ser 'exigente' acerca de la versión exacta de SUS dependencias". ¿Podrías desarrollar un poco este punto? Supongo que su código generalmente se prueba con solo versiones específicas de dependencias, y este enfoque puede ser un poco peligroso. Supongo que una biblioteca debería funcionar con una variedad de versiones porque no desea instalar demasiadas versiones de dependencias. ¿Para ahorrar espacio en disco?
Taro Kiritani
@TaroKiritani En realidad, enumeré 2 escenarios diferentes uno al lado del otro, el caso de la biblioteca y el caso de la aplicación. ¿Quizás no trabajaste en una biblioteca antes? Como biblioteca, se espera que sea consumida por paquetes posteriores. Entonces, si es quisquilloso para fijar SU dependencia A==1.2.3, y luego si el paquete posterior de su biblioteca depende de A==1.2.4, ahora no habrá una manera de satisfacer ambos. La solución para minimizar este conflicto es que su biblioteca defina un rango que sabe que funcionaría. Suponiendo que muchas bibliotecas ascendentes ya siguen a semver.org , A>=1,<2funcionaría.
RayLuo
No me di cuenta de que solo se puede instalar una versión de un paquete en un solo entorno. stackoverflow.com/a/6572017/5686692 Gracias por la aclaración.
Taro Kiritani
1
@TaroKiritani, sí, si no ¿cómo sería su saber qué versión de la aplicación foono import foole dará? Esa respuesta hacky aceptada en ese enlace que proporcionó sirve como un ejemplo perfecto de por qué el encargado del paquete "no debe ni debe ser exigente". :-) ¿Ahora puedo darme tu voto a favor?
RayLuo
1
También podría comentar sobre ese nuevo pensamiento, pero esta sección de comentarios ya se está saliendo del tema y es difícil de seguir para los recién llegados. Le sugiero que haga una nueva pregunta "¿Debemos usar toxinas o algo así para garantizar que mi biblioteca funcione en varias combinaciones de dependencias?", Y luego la gente puede
intervenir