Pipenv: Propuesta: patrones y antipatrones `pipenv` para el proyecto de biblioteca de Python

Creado en 5 abr. 2018  ·  74Comentarios  ·  Fuente: pypa/pipenv

Hackear maya Aprendí algunas lecciones que resultaron en mi siguiente propuesta de uso recomendado de pipenv en bibliotecas de Python. Espero que otros revisen la propuesta y, si llegamos a un acuerdo, el texto (actualizado) podría terminar en pipenv docs.

pipenv patrones y antipatrones para el proyecto de biblioteca de Python

EDITAR
Lo siguiente es más aplicable para bibliotecas de Python generales (principalmente de código abierto), que se supone que se ejecutan en diferentes versiones y sistemas operativos de Python. Las bibliotecas desarrolladas en un entorno empresarial estricto pueden ser un caso diferente (asegúrese de revisar todas las secciones de Problemas de todos modos).

FIN DE EDICIÓN

TL; DR : Agregar archivos pipenv al proyecto de la biblioteca de Python es probable que introduzca una complejidad adicional y puede ocultar algunos errores sin agregar nada a la seguridad de la biblioteca. Por esta razón, mantenga Pipfile , Pipfile.lock y .env fuera del control de código fuente de la biblioteca.

Podrá utilizar toda la potencia de pipenv independientemente de los archivos que se encuentren en .gitignore .

Biblioteca de Python versus aplicación de Python

Por biblioteca de Python me refiero a un proyecto, que generalmente tiene setup.py , que está destinado a la distribución y el uso en varias plataformas que difieren en la versión de Python y / o el sistema operativo.

Los ejemplos son maya , requests , flask etc.

Por otro lado (no la biblioteca de Python), hay aplicaciones destinadas a un intérprete de Python específico, un sistema operativo y, a menudo, se implementan en un entorno estrictamente consistente.

pipfile describe estas diferencias muy bien en su Pipfile vs setup.py .

¿Qué es pipenv (herramienta de implementación)?

Estoy completamente de acuerdo con la afirmación de que pipenv es una herramienta de implementación ya que permite:

  • definir requisitos estrictos ( Pipfile.lock ) para la implementación del entorno virtual
  • Aplicar esos requisitos estrictos de manera reproducible en diferentes máquinas.

Ayuda cuando uno tiene que implementar una aplicación o desarrollar en un entorno Python muy consistente entre múltiples desarrolladores.

Llamar a la herramienta de empaquetado pipenv es engañoso si uno espera que cree bibliotecas de Python o que esté profundamente involucrado en su creación. Sí, pipenv puede ayudar mucho (en el desarrollo local de bibliotecas) pero posiblemente perjudique (a menudo en las pruebas de CI cuando se usa sin pensarlo en profundidad).

Aplicar "razones de seguridad" en un contexto incorrecto

TL; DR : pipenv proporciona un entorno seguro mediante la aplicación de dependencias concretas aprobadas descritas en el archivo Pipfile.lock y la biblioteca de Python solo puede definir dependencias abstractas (por lo tanto, no puede proporcionar Pipfile.lock ).

pipenv brilla en escenarios de implementación siguiendo estos pasos:

  • definir dependencias abstractas (a través de Pipfile )
  • generar a partir de él dependencias concretas que resulten en Pipfile.lock
  • crear un entorno de Python (virtual) que refleje esas dependencias concretas
  • ejecutar pruebas para asegurarse de que el entorno dado funciona como se espera y es seguro
  • publica el "dorado" probado Pipfile.lock como definición de entorno de Python aprobado
  • otros pueden usar pipenv sync para aplicar "el dorado" Pipfile.lock cualquier otro lugar obteniendo un entorno python idéntico.

Con el desarrollo de la biblioteca de Python, no se puede lograr tal seguridad, porque las bibliotecas no deben definir dependencias concretas . Romper esta regla (tratando de declarar dependencias concretas por la biblioteca de Python) da como resultado problemas como:

  • problemas para encontrar una versión satisfactoria de las bibliotecas compartidas (cada paquete estricto define la versión exacta de la biblioteca compartida y es muy probable que las versiones difieran e impidan encontrar una versión comúnmente aceptable)
  • las dependencias concretas pueden depender de la versión de Python, el sistema operativo u otros marcadores del entorno y tratar de instalar el paquete en un contexto diferente puede fallar fácilmente en satisfacer algunas de las reglas definidas en las dependencias abstractas originales.

Problema: Ocultar dependencias definidas setup.py rotas

setup.py definirá todas las dependencias abstractas a través de install_requires .

Si Pipfile define esas dependencias, puede ocultar fácilmente problemas como:

  • dependencia faltante en install_requires
  • Pipfile define reglas específicas (rangos de versiones, etc.) para una dependencia y install_requires no.

Para prevenirlo, siga estas reglas:

  • las dependencias definidas por la biblioteca no deben aparecer en Pipfile
  • la sección [packages] en Pipfile estará vacía o definirá solo una dependencia en la propia biblioteca.

Problema: Pipfile.lock en el repositorio

Mantener Pipfile.lock (generalmente por "razones de seguridad") en el repositorio de la biblioteca es incorrecto, porque:

  • Es probable que las dependencias descritas no sean válidas para diferentes versiones de Python o en otro sistema operativo
  • los desarrolladores se ven obligados a actualizar el archivo no solo cuando agregan / eliminan alguna dependencia, sino también cuando se actualizan otras bibliotecas y pueden utilizarse dentro de la biblioteca.

Para prevenirlo, uno debe:

  • eliminar Pipfile.lock del repositorio y agregarlo a .gitignore

Problema: Competir con tox (ocultar usedevelop )

Si tox.ini contiene en su sección commands entradas como:

  • pipenv install
  • pipenv install --dev
  • pipenv lock

a menudo es un problema, porque:

  • pipenv install instalará solo la biblioteca en sí, y tox está haciendo (por defecto). Además de la duplicidad, también evita usedevelop=True y usedevelop=False en tox.ini porque Pipenv puede expresarlo solo en una variante (y tox.ini permite diferencias en diferentes entornos).

Para prevenirlo, uno debe:

Problema: Rompiendo compilaciones, si pipenv falla

pipenv está en un gran desarrollo y las cosas se rompen en algún momento. Si tal problema rompe su compilación de CI, hay una falla que podría prevenirse al no usar pipenv y usar herramientas tradicionales (que a menudo son un poco más maduras).

Para prevenirlo, uno debe:

  • piénselo dos veces antes de agregar pipenv en un script de compilación de CI, tox.ini o un lugar similar. ¿Sabes qué valor obtienes al agregarlo? ¿Podría hacerse el trabajo con las herramientas existentes?
  • no lo agregue "solo por razones de seguridad" o porque "todos lo hacen".

Resumen

Las preguntas clave sobre el rol de pipenv en el desarrollo de la biblioteca de Python son:

  • ¿Qué valor aporta realmente pipenv ? A: herramienta de gestión Virtualenv.
  • ¿Cuál es el caso de uso relevante para pipenv ? R: Gestionar virtualenv.
  • ¿Aparecerá en el repositorio de la biblioteca? R: NO.

Siguen pocos detalles y trucos más.

pipenv no agregará ninguna seguridad a su paquete

No lo inserte en el proyecto solo porque todos lo hacen o porque espera seguridad adicional. Te decepcionará.

La protección mediante el uso de dependencias concretas (y aprobadas) se llevará a cabo en una fase posterior en la aplicación que utilizará su biblioteca.

Mantenga los archivos Pipfile , Pipfile.lock y .env fuera del repositorio

Coloque los archivos en .gitignore .

Pipfile es fácil de recrear como se muestra a continuación, ya que la mayoría o todos los requisitos ya están definidos en su setup.py . Y el archivo .env probablemente contenga información privada, que no se compartirá.

Mantener estos archivos fuera del repositorio evitará todos los problemas que pueden ocurrir con las compilaciones de CI al usar pipenv en situaciones que no son apropiadas.

pipenv como caja de herramientas privada del desarrollador

pipenv puede simplificar el trabajo del desarrollador como herramienta de administración virtualenv.

El truco es aprender cómo recrear rápidamente sus archivos relacionados (privados) pipenv , por ejemplo:

$ cd <project_repository>
$ # your library will bring the dependencies (via install_requires in setup.py)
$ pipenv install -e .   
$ # add more dev tools you preffer 
$ pipenv install --dev ipython pdbpp
$ # start hacking
$ pipenv shell
...

Utilice el archivo .env si necesita un método conveniente para configurar las variables de entorno.

Recuerde: Mantenga el uso de pipenv fuera de sus compilaciones de CI y su vida será más sencilla.

Truco: use la capacidad setup.py para declarar dependencias adicionales

En su setup.py use la sección extras_requires :

from setuptools import setup

setup(
    name='mypackage',
    ....,
    install_requires=["jinja2", "simplejson"],
    extras_require={
        'tests': ['pytest', 'pyyaml'],
        'pg': ['psycopg2'],
    },
    ....
)

Para instalar todas las dependencias declaradas por tests extra:

$ pipenv install -e .[tests]

Tenga en cuenta que siempre incluirá las dependencias install_requires .

Este método no permite dividir las dependencias en secciones predeterminadas y de desarrollo, pero esto no será un problema real en los escenarios esperados.

Discussion Type

Comentario más útil

@ Moritz90 Varias de las listas de correo de Python serían buenos lugares para llevar a cabo esta discusión.

pypa-dev es el más definido para las discusiones que se centran en el empaquetado de Python y el ecosistema que lo rodea. Probablemente comenzaría aquí si tuviera que publicar una discusión similar.

python-ideas es un lugar para discutir ideas y tiene una visibilidad bastante alta para toda la comunidad de Python. También sería un buen punto de partida si desea llevar esto al nivel de PEP (creo que eventualmente lo haría).

Todos 74 comentarios

Esto es muy impresionante, muchas gracias por compilar. Definitivamente lo revisaré con más detalle en un momento.

/ cc @uranusjr @jtratner @ncoghlan

Algunas referencias a problemas de maya :

  • kennethreitz / maya # 138 (EliminarPipfile.lock del repositorio)
  • kennethreitz / maya # 139 (Omitir pipenv ejecutar en tox.ini ...)
  • kennethreitz / maya # 145 (corregir pendulum>=1.0 en setup.py: la versión estaba en Pipfile pero faltaba en setup.py)
  • kennethreitz / maya # 143 (PR que muestra cómo el problema pipenv rompió toda la carrera de Travis)
  • kennethreitz / maya # 144 (uso de pipenv de PR Refactor de acuerdo con las mejores prácticas semioficiales)

Yo también amo esto. Tal vez deberíamos agregar esto a la documentación de Pipenv, o incluso a la Guía del usuario de empaquetado de Python .

El corolario del consejo anterior parece ser "renunciar a las construcciones de CI deterministas / reproducibles", lo que me parece un gran antipatrón.

¿Qué propones como alternativa que aún permitiría el determinismo?

@ tsiq-oliverc Las construcciones deterministas tienen su lugar en este momento, se debe construir una aplicación.

Imagine el siguiente intento de realizar compilaciones realmente deterministas de la biblioteca de Python:

  • las compilaciones deben basarse en Pipfile.lock
  • cada contexto de ejecución (combinación de cada Python y variante de SO) puede tener diferentes Pipfile.lock resultantes de las dependencias abstractas de la biblioteca definidas en Pipfile
  • El repositorio debería proporcionar instancias Pipfile.lock separadas definidas en el repositorio. Tenga en cuenta que construir Pipfile.lock automáticamente durante la construcción de CI no agrega ningún determinismo

Esto supone un gran esfuerzo extra. Y lo que obtienes es una biblioteca, que se instalará en un contexto diferente (por ejemplo, una semana después, la instalación estándar recogerá una dependencia actualizada o dos) y que no obtendrá nada del hecho de que usaste Pipfile.lock , que está obsoleto en este momento.

El conflicto está en el hecho de que la biblioteca nunca debe definir dependencias estrictas en su interior.

Si lo piensa, hay otra alternativa para obtener compilaciones deterministas para la biblioteca de Python: descríbala.

@vlcinsky : si un consumidor de su biblioteca usa diferentes versiones de dependencias, etc., eso está fuera de su control. Así que estoy de acuerdo en que no existe una forma viable de que un responsable de la biblioteca lo gestione.

Pero el objetivo aquí es presumiblemente un alcance mucho menor. En particular, vería los objetivos para un mantenedor de biblioteca como los siguientes (que son aproximadamente equivalencias):

  1. Si ejecuta su CI dos veces, tiene la garantía de obtener el mismo resultado (¡a pesar de los problemas de red!).
  2. Puede recrear localmente (y, por lo tanto, depurar) el comportamiento que observa en CI, incluso si eso significa ejecutar Docker / etc. en la zona.
  3. Puede decir con confianza "Mi biblioteca se comporta como se esperaba con las versiones de dependencia X, Y, Z" a sus consumidores.

Si alguna de esas tres cosas no se cumple, me parece que es la antítesis del control de calidad.

Entonces, sí, diría que si garantiza admitir las variantes A, B y C de Python a su consumidor, y se comportan de manera lo suficientemente diferente como para que un archivo de bloqueo (etc.) no lo corte, entonces debería tener tres archivos de bloqueo (o lo que).

Sin embargo, no he usado Pipenv lo suficiente como para saber lo fácil que sería en la práctica.

Actualmente también estoy considerando agregar Pipfile s a algunos proyectos de biblioteca para el sistema CI.

Necesito absolutamente el bloqueo de dependencia (+ hash) para cumplir con las pautas de seguridad de toda la empresa y actualmente no necesito probar con diferentes versiones de Python, ya que solo hay una que es oficialmente compatible. Y el hecho de que pipenv simplifique la creación de un entorno de desarrollo local, incluido virtualenv, es un efecto secundario agradable.

Y lo que obtienes es una biblioteca, que se instalará en un contexto diferente (por ejemplo, una semana después, la instalación estándar recogerá una dependencia actualizada o dos) y que no obtendrá nada del hecho de que usaste Pipfile.lock , que está actualmente obsoleto.

Esto no es universalmente cierto. En el mundo del software empresarial, todavía tiene entornos muy específicos que cuentan con soporte oficial y un problema de seguridad en una dependencia hace que su producto se actualice en lugar de que el cliente actualice la dependencia por sí mismo.

(Sí, estoy hablando de una biblioteca, no de una aplicación aquí ...)

@ Moritz90 su escenario es para la biblioteca de Python en un entorno empresarial y pipenv puede ayudar porque es un entorno mucho más determinista.

Mi descripción apunta a bibliotecas de Python generales como flask , request , maya etc., donde el contexto es mucho más variable. Tratando de arreglar un par de cosas en maya me sentí frustrado al saber que en muchos casos el uso de pipenv introdujo problemas reales (por lo general, ocultan problemas que normalmente se detectarían) sin proporcionar mucho o ningún agregado. valor.

Obtener compilaciones deterministas es algo bueno, pero conlleva costos. Y si se hace mal, es posible que pague más por un resultado de menor calidad, y esto es lo que quería evitar.

Yo diría que esta es una de las instancias en las que no queremos que las compilaciones sean absolutamente deterministas. Si no fija sus dependencias con == , se compromete a mantener la compatibilidad con varias versiones de forma predeterminada y debe diseñar la biblioteca de esa manera. Una actualización de dependencia que rompa la compilación en CI es en realidad algo bueno porque expone un error en la biblioteca. Las dependencias completamente deterministas (administradas por Pipenv) enmascararían eso. Aún así, sería beneficioso poder ser determinista cuando lo desee, que generalmente no es lo mejor.

@uranusjr - Claro. Estoy de acuerdo en que si el deseo es "construcciones no deterministas", entonces el consejo de arriba puede tener sentido. De hecho, es casi una equivalencia lógica y podría expresarse de manera mucho más sucinta: "Si no desea compilaciones deterministas, no utilice una herramienta ( pipenv ) cuyo propósito sea garantizar compilaciones deterministas". 😄.

Pero ciertamente ese no es un objetivo deseable en general.

@ tsiq-oliverc buena definición de alcance: admite discusiones enfocadas. Agregaría un requisito más: el determinismo de CI no ocultará posibles problemas dentro de la biblioteca probada.

Si usamos Pipenv.lock , creamos virtualenv en base a eso y ejecutamos la prueba de CI de la biblioteca, hicimos parte de la funcionalidad que se supone que debe hacer la biblioteca: instalar las dependencias adecuadas. Si la biblioteca está rota de alguna manera en este sentido, el entorno preinstalado ocultaría este problema.

Para mí, parece más importante detectar problemas dentro de una biblioteca que ejecutar CI de manera determinista. Si hay una manera de hacer ambas cosas (por ejemplo, ejecutar la prueba detrás del índice pypi privado, que también podría admitir el determinismo), no tengo ningún problema, pero si hay un conflicto, tengo mis prioridades.

No me malinterpreten: no hay ningún deseo de ejecutar compilaciones no deterministas, mi deseo es ejecutar compilaciones de CI, que detectarán tantos problemas como sea posible.

@vlcinsky Seguro, solo quería compartir mi experiencia para asegurarme de que la documentación actualizada también la refleje. La documentación actual hace un gran trabajo al explicar las compensaciones:

Para las bibliotecas, defina dependencias abstractas a través de install_requires en setup.py. [...]
Para las aplicaciones, defina las dependencias y dónde obtenerlas en el Pipfile y use este archivo para actualizar el conjunto de dependencias concretas en Pipfile.lock. [...]
Por supuesto, Pipfile y pipenv siguen siendo útiles para los desarrolladores de bibliotecas, ya que pueden usarse para definir un entorno de desarrollo o prueba.
Y, por supuesto, hay proyectos para los que la distinción entre biblioteca y aplicación no es tan clara.

(Destacó la parte que se aplica en mi caso).

Solo quiero asegurarme de que siga siendo así. Creo que su publicación original contiene demasiadas declaraciones generales sin una exención de responsabilidad de que está hablando de un proyecto de código abierto que se publicará en PyPI.

@ Moritz90 Estoy completamente de acuerdo. Estaba tratando de resaltar ese enfoque, pero puedo hacerlo aún más visible.

@ Moritz90 Agregué una nota introductoria que refleja su comentario.

@vlcinsky - Eso tiene sentido. Yo entiendo que no lo hace de forma explícita desea no determinista construye, pero creo que es inevitable equivalente a lo que quieren (es decir, a cuestiones de captura cuando sus dependencias aguas arriba de actualización).

Pensando en voz alta, ¿cuál es la mejor manera de resolver estos dos objetivos en conflicto? Una posibilidad es tener un proceso de CI de dos fases:

  1. La fase determinista. Aprovecha un Pipfile.lock en su repositorio, por lo que es completamente reproducible.
  2. La fase no determinista. Ejecuta pipenv update y luego ejecuta las pruebas, de modo que extrae la última de todas sus dependencias (que es básicamente el mismo que el comportamiento sin archivo de bloqueo, ¿creo?).

@ tsiq-oliverc Para obtener compilaciones deterministas, pensaría en la siguiente configuración:

  • construir trabajo de caché de pypi: ejecutar una vez, produciendo alguna forma de caché de índice de pypi (como directorio de archivos o algo similar)
  • trabajo de prueba de la biblioteca: usando el caché de pypi, pero evitando pipenv

Usar pipenv para realizar la instalación es similar a lo que debe hacer la instalación de la biblioteca, pero definitivamente es diferente porque es un código diferente el que hace el trabajo.

construir trabajo de caché de pypi

$ git clone <repo_url> <project_dir>
$ cd <project_dir>
$ pip install pipenv
$ $ # clean pypi cache and make it ready to cache somehow - not described here
$ pipenv install -e .[test]
$ # if we need extra testing packages in pipenv
$ pipenv install <extra_test_packages>
$ # record current requirements expressed in `Pipfile.lock`
$ pipenv lock
$ # if needed, record the `Pipfile.lock` somewhere

Los resultados de dicho trabajo son:

  • Pipfile.lock como dependencias registradas (puede ayudar a los desarrolladores a reproducir el entorno fácilmente)
  • caché pypi local precargado

trabajo de prueba de biblioteca

hay fases:

  • configurar el entorno para forzar tox , pip etc.utilizando solo nuestro caché pypi local
  • ejecute las pruebas de CI (evite usar pipenv )

lo que obtenemos

  • la biblioteca se prueba en un entorno determinista
  • se prueba la biblioteca incl. es la capacidad de instalarse por sí mismo
  • Pipfile.lock registra los paquetes pypi que se usaron para instalar la biblioteca. Se puede utilizar para reproducir el entorno en el sitio del desarrollador.
  • la adaptación a paquetes actualizados en pypi (posiblemente externo) es simple (vuelva a ejecutar el trabajo de "compilar la caché de pypi") y se realiza de manera controlada (el contenido de pypi se registra incluyendo hashes)

Otra ventaja es que esta configuración no requiere que los desarrolladores mantengan Pipfile o Pipfile.lock . Además, ejecutar las pruebas en diferentes contextos es siempre lo mismo ( Pipfile.lock siempre se reconstruye en un contexto dado).

Lo que aún falta (y se puede hacer)

La caché de pypi es la parte que necesita algo de investigación. Supongo que un directorio simple sería suficiente y tal vez pipenv ya esté listo para ayudar con eso. Quizás el número 1731 sea la parte que falta.

Como paquete que realiza la resolución de dependencias, muchas de nuestras propias pruebas se basan en compilaciones deterministas, es decir, tomando cosas conocidas y esperando un gráfico resuelto. Usamos pytest-pypi para esto.

Me encanta la animada discusión sobre este tema. Creo que el matiz es importante y siempre debes probar contra dependencias conocidas y no fijadas

siempre debe probar con dependencias conocidas y no ancladas

Apoyo esta sugerencia. Es una buena idea tener siempre un "buen estado conocido" explícito para las compilaciones reproducibles y simplificar la depuración en caso de que una actualización rompa algo, además de asegurarse de que las versiones menores / corregidas de errores también funcionen.

(En mi opinión muy personal, la situación ideal sería que el administrador de paquetes instale las últimas versiones menores de forma predeterminada para que las bibliotecas siempre puedan especificar las versiones de dependencia concretas con las que se probaron, pero me doy cuenta de que esa es una opinión muy controvertida y requiere que todos para seguir a semver.)

@ Moritz90 @techalchemy @uranusjr @ tsiq-oliverc

Aquí está mi resumen de la discusión anterior.

Problemas particulares y soluciones propuestas

Muchos contextos de ejecución: ¿quién mantendrá Pipfile.lock archivo (s)?

Cada intérprete de Python y SO compatible contribuye a la matriz de posibles contextos de ejecución.

Por ejemplo, Flask admite (al menos elementos de CI visibles en el repositorio):

  • Sistema operativo Windows (python 2.7 y Python 3.6)
  • Linux (python 2.7, 3.4, 3.5, 3.6, nocturno, pypi)
  • OSX (py - no estoy seguro si hay más versiones)

Crea 9 contextos de ejecución diferentes que pueden diferir.

Cada contexto de ejecución puede tener Pipfile.lock .

¿Quién los mantendrá?

Las opciones son:

  • Deje que los desarrolladores lo hagan manualmente (DE NINGÚN MODO)
  • Mantenga solo un Pipfile.lock para la plataforma de desarrollo principal (¿qué plataforma disfruta ser ignorada?)
  • Automatizar la creación a través de CI (SÍ)

Propuesta: Deje que CI genere el archivo por pipenv install -e . . No lo incluya en el repositorio, ayude a los desarrolladores a elegir el Pipfile.lock adecuado como resultado de las compilaciones automatizadas.

Los desarrolladores necesitan un entorno predecible

Al solucionar un problema, que puede ser causado por cambios de dependencias en pypi, el desarrollador puede necesitar un medio simple para reproducir el entorno de la prueba fallida.

Propuesta:

  • para muchos paquetes, los cambios en las dependencias de pypi son tan raros que no son un problema real
  • para arreglar el entorno por sí mismo, el desarrollador puede generar Pipfile.lock por pipenv install -e . seguido de pipenv lock .
  • Para replicar el entorno de la prueba fallida, el desarrollador elige Pipfile.lock de la prueba fallida.
  • todo: muestra ejemplos, cómo aplicar Pipfile.lock en tox.ini .

CI debe revelar roto setup.py

La biblioteca setup.py puede estar rota (falta la dependencia en install_requires , falta el especificador de versión, etc.) y la prueba de CI no debe ocultar dicho problema (preinstalando las dependencias omitidas por sí misma).

Propuesta:

  • confíe en pipenv install -e . para proporcionar el mismo resultado que la instalación simple (actualmente hay algunos problemas con eso).
  • ejecute una prueba de instalación simple (sin pipenv ) y posiblemente compare que la salida de pip freeze resultante es un subconjunto de lo que está instalado por pipenv .

Las dependencias de pypi actualizadas pueden romper cosas, CI detectará tales persecuciones

Alguna actualización de dependencia puede romper la biblioteca al usarla. CI detectará fallas en tal problema.

Propuesta:

  • al menos una prueba debe ejecutarse en una versión no fijada
  • si CI siempre genera nuevos Pipfile.lock , esto no es un problema (ya que de todos modos corremos en modo no fijado)

Modos CI para diferentes tipos de bibliotecas

En todos los modos propuestos, traté de evitar mantener los archivos pipenv en el repositorio, lo que evitó que los desarrolladores mantuvieran estas cosas realmente complejas (¡¡¡automatización !!!).

En contraste con mi texto original, el segundo y tercer modo usan pipenv en scripts de CI.

Modo: ¡Corre, Forrest, corre!

Paquete simple con menor número de dependencias que no cambian con frecuencia.

Simplemente ejecute como antes de la era pipenv y manténgalos simples.

Los casos raros en los que las dependencias generarán problemas son fáciles de solucionar y no justifican que la CI sea más compleja.

Modo: generar y sellar

Cada vez que se ejecute la prueba de CI, genere un nuevo Pipfile.lock que describa completamente el entorno utilizado en ese momento.

El Pipfile.lock se convertirá en un artefacto de CI.

Si las cosas salen mal, el desarrollador puede elegir Pipfile.lock de la compilación defectuosa, aplicarlo localmente y hacer las pruebas y la reparación.

Si alguien quiere implementar, se pueden usar Pipfile.lock de la última compilación exitosa.

Modo: Edad de Hielo

Cuando el cambio de dependencias es un problema real, CI creará Pipfile.lock una vez y lo mantendrá usando durante cierto período (¿un mes?).

Esto dificulta la configuración de CI, ya que debe haber al menos dos trabajos diferentes (uno generando Pipfile.lock , el otro aplicándolo y usándolo en pruebas).

Advertencia: Pipfile.lock debe actualizarse en este momento, setup.py cambia las dependencias.

Tenga en cuenta que Ice Age requiere Scrat, el tipo de prueba de ardilla que ignora el estado congelado y verifica las versiones no fijadas.

Palabras de clausura

Como se ve, el determinismo y la complejidad crecen de un modo a otro.

Mi propuesta seria:

  • comience simple ("Corre, Forrest, Corre"). Ganas eficiencia y velocidad.
  • si las cosas se vuelven demasiado complejas debido a las dependencias cambiantes, continúe con "Generar y sellar". Obtienes repetibilidad en el entorno local.
  • si las cosas están realmente mal, ve al modo "Ice Age". Obtienes determinismo (temporal).

Todas las ganancias cuestan algo.

Si el objetivo aquí es actualizar los consejos en los documentos, entonces, honestamente, se siente irresponsable decir algo dramáticamente diferente a "Siga las mejores prácticas (compilaciones reproducibles) de forma predeterminada, hasta que no tenga otra opción".

@vlcinsky Bajo el título "Modo: Generar y sellar", podría tener sentido mencionar que el último Pipfile.lock exitoso siempre debe mantenerse, por ejemplo, declarándolo como un artefacto de Jenkins. Con ese cambio, estaría bien recomendar esa configuración para la mayoría de los proyectos. Como @ tsiq-oliverc, nunca recomendaría el primer modo.

Cuanto más lo pienso, más siento que esta documentación se convertirá en una sección sobre por qué usar pipenv para compilaciones de CI es una gran idea, incluso si está desarrollando una biblioteca.

@ tsiq-oliverc La gran mayoría de los paquetes de Python generales están en modo "Ejecutar, Forrest, Ejecutar". He ayudado a algunos de estos paquetes a introducir tox y pytest , porque sentí que contribuiría a la calidad del paquete dado y porque tenía una idea bastante clara de cómo se podría hacer bien.

Ahora hay otra gran herramienta y me pregunto cómo usar pipenv correctamente en proyectos generales de Python para contribuir a su calidad. Quiero encontrar una o dos recetas que funcionen bien, que estén justificadas y sean fáciles de seguir.

¿Qué le diría al proyecto Flask?

  1. ¿Sigue las mejores prácticas (compilaciones reproducibles) de forma predeterminada, hasta que no tenga otra opción?
  2. ¿Agregar 9 Pipfile.lock archivos y configurar una política para actualizarlos?
  3. ¿Refactorizar los scripts de CI para que Travis y Appveyor funcionen en dos fases siguiendo el modo Ice Age?
  4. ¿Modificar los scripts de CI para Travis y Appveyor para generar Pipfile.lock artefacto para casos, cuando alguien necesita reproducir una prueba fallida en su propia computadora?
  5. no hay comentarios, aparte de "Muchas gracias por Flask".

El objetivo es encontrar un estilo de trabajo funcional. Si termina en doc, bueno, si no, no hay problema.

@vlcinsky Yo diría que (1) y (4) deberían ser la recomendación para tales proyectos. Si bien sin un Pipfile.lock preexistente, no sabrá las versiones utilizadas en la compilación de antemano (lo cual está bien fuera de los entornos corporativos), aún obtendrá un resultado reproducible si genera y archiva el archivo de bloqueo durante la construcción.

Editar : La versión tl; dr de mi recomendación sería:

  • Siempre asegúrese de que sus compilaciones sean reproducibles, independientemente de si está desarrollando una biblioteca o una aplicación. pipenv puede ayudarlo a lograr este objetivo.
  • Si está desarrollando una aplicación, asigne Pipfile.lock a su repositorio y utilícela para la implementación. (Esto ya está cubierto por la documentación existente).
  • Si está desarrollando una biblioteca de código abierto, genere Pipfile.lock sobre la marcha en su compilación de CI y archívela para más adelante.
  • Si está desarrollando una biblioteca y trabaja en un entorno corporativo restrictivo, mantenga la cantidad adecuada de archivos de bloqueo y utilícelos en sus compilaciones de CI.

(Por supuesto, la documentación real debería tener un poco más de detalles y ejemplos).

@ Moritz90 Modifiqué el "Generar y propusiste .

Re (1): fácil de decir, imposible de ejecutar sin ser más específico.

Re (4): sí, también creo que "Generar y sellar" es el modo más factible. Pero en el caso de Flask no me atreveré (al menos no por el momento).

Volver Pipfile.lock preexistentes en el entorno empresarial: debe crearse de alguna manera, ya sea (semi) manual o automáticamente. Supongo que en un entorno corporativo no se instala directamente desde una pypi pública, sino que se usa una privada y que solo proporciona paquetes aprobados ( devpi-server brinda un gran servicio en eso: índices múltiples, volatilidad controlada de los paquetes publicados, aprobaciones para paquetes externos). paquetes, etc.) Si el proceso de construcción de Pipfile.lock ejecuta en dicho entorno, solo puede usar lo que está aprobado, por lo que si una nueva versión va a aparecer allí, alguien tiene que ponerse de pie y aprobarla. Después de la compilación de CI, se probará que no rompe cosas. Y con pipenv check prueba de problemas de seguridad también se puede automatizar.

Supongo que tal flujo de trabajo sería más seguro en comparación con alguien que lo crea (semi) manualmente. Pero mi conocimiento del entorno empresarial es muy limitado.

Hola equipo de pipenv. Comparto mucho de lo que se dice en este texto, ayuda mucho a cualquier desarrollador a comprender mejor las limitaciones de Pipfile / pipenv al desarrollar una biblioteca. Quiero ver este texto o parte de este texto integrado dentro de la documentación oficial de pipenv.

Tengo la siguiente enmienda que me gustaría discutir:

Para nuestro paquete python interno, completamente reutilizable, publicado en nuestro pypi interno, etc., e incluso para mis propios paquetes python (por ejemplo: cfgtree , txrwlock , pipenv-to-requirements ), utilizo un paquete que algunos ya conocen o incluso usan , que abstrae estos detalles y facilita la vida del desarrollador de Python: PBR.
PBR básicamente lee requirements.txt encuentra en la carpeta raíz de un paquete de distribución y lo inyecta en el install_requires del setup.py . El desarrollador simplemente necesita mantener un requirements.txt con declaraciones de dependencia sueltas. Hasta que el soporte de Pipfile se integre oficialmente dentro de PBR, tengo que usar pipenv-to-requirements que genera automáticamente requirements.txt partir de Pipfile para que ambos estén sincronizados y comprometidos en fuente, y PBR realiza la inyección correctamente después de que se ha creado el paquete de distribución. Creo que se podría usar pipenv para generar este requirements.txt

Trabajo en un soporte de Pipfile para PBR, para que pueda leer el Pipfile (y no el archivo de bloqueo) e inyectarlo en install_requires como lo hace con requirements.txt .

No sé si existen otros paquetes similares, porque también hace otras cosas que la gente podría no querer (versión del historial de git, generación automática de AUTHORS y ChangLog).

Pero al final, realmente siento que es tan fácil escribir, mantener y manejar el control de versiones de una biblioteca de Python, me entristecería no compartir esta experiencia. Lo estoy promocionando como la forma "recomendada" de escribir bibliotecas de Python modernas en mi empresa.

Reconozco que es como "hacer trampa" en todas las dificultades sobre la biblioteca y pipenv, pero al final el trabajo está hecho y los desarrolladores están felices de usarlo hasta ahora. Parte del entrenamiento de Python, que le estoy dando a un nuevo desarrollador de Python en mi empresa, implica, primero escribir una biblioteca de Python manteniendo install_requires manualmente, y luego cambiar a PBR para ver cómo se vuelve más fácil ( y, francamente, soy un fanático de la función de

Parte de la razón para declarar las dependencias de las bibliotecas usando un archivo dedicado también para las bibliotecas es poder usar herramientas como readthedocs o pyup (incluso si pyup tiene más sentido cuando está vinculado a una aplicación).

No necesariamente quiero promover este método como la forma "estándar" de hacer un paquete de Python, en realidad es la forma "OpenStack", pero quisiera compartir mi experiencia, y si otros tienen una experiencia similar o contradictoria, estaré feliz de escucharlos y actualizar mi punto de vista.

Equipo, ¿qué opinas de una especie de sección de "comunidad" en la documentación? ¿Para que los usuarios como yo puedan compartir su experiencia sobre cómo usa pipenv, sin necesariamente el respaldo total del equipo de pipenv?

PD: puedo mover esto a un problema dedicado si no desea contaminar este hilo

@vlcinsky (1) es muy fácil de ejecutar: coloque su archivo de bloqueo en su repositorio.

Creo que lo que quieres decir en cambio es: es imposible dar un consejo específico una vez que esta estrategia básica ya no es suficiente. Eso es ciertamente cierto, pero eso se debe a que el problema específico probablemente difiera según el caso.

O, para decirlo de otra manera, la solución depende de las garantías adicionales que desee que brinde su flujo de trabajo de CI.

@gsemet, ¿sabes qué? Todos mis paquetes de Python creados en los últimos dos años se basan en pbr , es realmente genial. Y sigo sus intentos de apoyar Pipfile en pbr siempre que puedo (algunos pulgares arriba, votos, etc.).

En caso de este problema (buscando pipenv patrones y antipatrones para bibliotecas de Python generales) omití intencionalmente pbr por dos razones:

  • Haría más compleja la discusión conceptual
  • a algunas personas no les gusta pbr por otras razones (las mencionaste) y probablemente desviaría la discusión

Por otro lado, tengo muchas ganas de tener una receta tuya para los amantes de la pbr. Lo leeré.

@ tsiq-oliverc has dado en el clavo: pon tu archivo de bloqueo en tu repositorio

Este es exactamente el problema que me motivó a comenzar este número. Si vuelve a leer el comienzo de este problema, encontrará una descripción de algunos casos, en los que agregar Pipfile.lock puede romper sus pruebas de CI (ya sea interrumpiendo la ejecución de la compilación u ocultando problemas, que de otro modo se detectarían, o instalando dependencias incorrectas para un contexto dado ...).

Si me muestra un repositorio, donde esto se hace correctamente (biblioteca general de Python), estaría feliz. O demostraría, qué riesgos hay o qué cosas están sin terminar.

Frio ! También mantengo este cortador de galletas :)

@vlcinsky Correcto,

Lo mejor que puedo decir, estos son los síntomas específicos en su publicación original:

  • Ocultar dependencias de setup.py rotas. Esto parece que no es un problema: pipenv install -e . , ¿verdad?
  • Es probable que las dependencias no sean válidas para diferentes versiones de Python o en otro sistema operativo. Puedo ver que esto podría ser un problema, pero ¿podría proporcionar un ejemplo concreto de dónde ha sido importante en la práctica? (en el contexto de un archivo de bloqueo)
  • Los desarrolladores se ven obligados a actualizar ... cuando se actualizan otras bibliotecas y pueden utilizarse dentro de la biblioteca. No están obligados a hacer eso. Lo hacen si quieren proporcionar una garantía de que su biblioteca funciona con la versión n + 1 en lugar de la versión n de su dependencia. Pero tenga en cuenta que ya propuse una alternativa que ofrece lo mejor de ambos mundos.
  • Competir con Tox. Realmente no sé nada sobre Tox. Pero sí, usar dos herramientas simultáneamente para administrar sus dependencias parece una receta para el desastre. Yo diría que use el que sea superior para esa tarea en particular.
  • Pipenv falla. Esto suena como otro problema que no es un problema: puede anclar la versión de Pipenv (esa es mi solución actual, al igual que anclo mi imagen de Docker, mi versión de Pip, etc.)

@ tsiq-oliverc Tengo que decir que sus comentarios me inspiraron y sé que contribuyeron a un mayor nivel de reproducibilidad de la solución propuesta.

Lo siguiente está relacionado con su propuesta de poner el archivo de bloqueo ( Pipfile.lock ) en el repositorio para garantizar la repetibilidad:

re Ocultar dependencias de setup.py rotas. . El pipenv install -e . sigue lo que propongo, pero tenga en cuenta que esto no es un uso de Pipfile.lock , es un método para (re) crearlo. Si alguien conserva Pipenv.lock y lo usa para crear virtualenv antes de instalar el paquete, el problema está presente.

Es probable que las dependencias no sean válidas para diferentes versiones de Python o en otro sistema operativo . Los ejemplos son muchos: doit instalado para Python 2.7 debe ser una versión anterior, ya que una versión más reciente dejó de ser compatible con Python 2.x. watchdog dependencia de

re Los desarrolladores se ven obligados a actualizar ... Imagine una biblioteca de código abierto con 15 colaboradores. Es muy fácil olvidar la regeneración de Pipfile.lock por parte de un desarrollador central recién llegado o cansado. Por ejemplo, en el paquete maya me pidió que regenerara el Pipfile.lock ya que se agregó una nueva dependencia a setup.py . ¿Era eso necesario? ¿Lo actualicé correctamente? ¿Lo actualicé para todos los contextos de ejecución admitidos? Las respuestas son no, no estoy seguro, no. De todos modos, gracias por su propuesta (me inspiró para la solución descrita al lado de su comentario).

re Competir con tox : Tox permite la creación de múltiples virtualenvs y la automatización de la ejecución de pruebas dentro de ellos. El típico tox.ini define diferentes virtualenvs para python 2.7, 3.4, 3.5, 3.6 y cualquier otro que necesite, y permite instalar el paquete allí y ejecutar el conjunto de pruebas. Es una herramienta de poder establecida de probadores serios. pipenv no es la herramienta para este propósito, pero puede interferir en la instalación de las cosas necesarias. En cierto modo, seguí su consejo y le propuse utilizar una herramienta superior (tox) de más de pipenv siempre que fuera posible.

re Pipenv falla. Esto es realmente lamentable. Tuve una prueba de CI (basada en toxinas) que se ejecuta bien en localhost, pero cuando se ejecuta a través de Travis, falló debido a un problema de pipenv . Si quiero usarlo ahora, anclar no ayuda hasta que se libere la corrección. Pero así es como va: esperaré.

Tenga en cuenta que algunas partes de mi publicación original tendrán que actualizarse como parece, el uso de pipenv en los scripts de CI tiene su lugar justificado ("sellar" la configuración de virtualenv para un posible uso posterior).

@ tsiq-oliverc Aunque inicialmente me gustó tu sugerencia de probar tanto el "bien conocido" como las últimas versiones, me resulta cada vez más difícil justificar el esfuerzo cuanto más lo pienso. Creo que deberías decidir hacer uno u otro, no ambos.

Lo único que gana es que sabrá inmediatamente si una falla fue causada por una actualización de dependencia o un cambio de código. Pero puede lograr lo mismo simplemente haciendo confirmaciones por separado (al actualizar manualmente las dependencias bloqueadas) o intentando reproducir el error con el último archivo de bloqueo producido por una compilación exitosa (cuando siempre se usan las últimas versiones). Y en entornos restringidos, no puede "simplemente actualizar" de todos modos ...

@vlcinsky Si bien estoy de acuerdo con su punto general sobre las diferencias entre entornos, el argumento de "un archivo de bloqueo por configuración" me suena como un hombre de paja. En la práctica, podrá compartir los archivos de bloqueo entre al menos algunos de los entornos.

Una pregunta pendiente que aún no ha sido respondida es cómo lidiar con el caso en el que ambos necesitan probar en un entorno diferente y bloquear sus dependencias. Tengo que admitir que no sé nada sobre tox más que eso existe, pero parece que se necesita algún tipo de pegamento entre tox y pipenv que resuelve este problema de alguna manera.

@ Moritz90

Conociendo al hombre de paja

Con respecto a demasiadas variantes de Pipfile.lock sirviendo como hombre de paja (para mantener a otros fuera de mi campo):

Matraz

Tomé el proyecto flask (considerándolo muy maduro) y realicé pruebas de toxinas:

Aquí puede ver una lista de variantes probadas (solo localmente en Linux, multiplíquelo por 3 ya que Windows y OSX harán el mismo conjunto de pruebas pero pueden resultar en diferentes entornos).

Hay 16 ejecuciones de prueba diferentes en un sistema operativo, 5 de ellas fallaron porque no las tengo instaladas (eso está bien), una está lidiando con la construcción de documentos (requiere una biblioteca imporable) y otra cobertura (que también requiere una biblioteca importable) ):

  coverage-report: commands succeeded
  docs-html: commands succeeded
  py27-devel: commands succeeded
  py27-lowest: commands succeeded
  py27-simplejson: commands succeeded
  py27: commands succeeded
  py35: commands succeeded
  py36-devel: commands succeeded
  py36-lowest: commands succeeded
  py36-simplejson: commands succeeded
  py36: commands succeeded
ERROR:   py34: InterpreterNotFound: python3.4
ERROR:   pypy-devel: InterpreterNotFound: pypy
ERROR:   pypy-lowest: InterpreterNotFound: pypy
ERROR:   pypy-simplejson: InterpreterNotFound: pypy
ERROR:   pypy: InterpreterNotFound: pypy

Para cada uno de los virtualenvs creados, he creado requirements.txt archivo por pip freeze > {venv_name}.txt

Luego calcula los hashes para los archivos, ordenados de acuerdo con los valores hash, por lo que se agruparán todos los mismos. Aquí viene el hombre de paja:

b231a4cc8f30e3fd1ca0bfb0397c4918f5ab5ec3e56575c15920809705eb815e  py35.txt
b231a4cc8f30e3fd1ca0bfb0397c4918f5ab5ec3e56575c15920809705eb815e  py36.txt
cdf69aa2a87ffd0291ea65265a7714cc8c417805d613701af7b22c8ff2b5c0e4  py27-devel.txt
dfe27df6451f10a825f4a82dfe5bd58bd91c7e515240e1b102ffe46b4c358cdf  py36-simplejson.txt
e48cd24ea944fc9d8472d989ef0094bf42eb55cc28d7b59ee00ddcbee66ea69f  py36-lowest.txt
f8c745d16a20390873d146ccb50cf5689deb01aad6d157b77be203b407e6195d  py36-devel.txt
053e107ac856bc8845a1c8095aff6737dfb5d7718b081432f7a67f2125dc87ef  docs-html.txt
45b90aa0885182b883b16cb61091f754b2d889036c94eae0f49953aa6435ece5  py27-simplejson.txt
48bd0f6e66a6374a56b9c306e1c14217d224f9d42490328076993ebf490d61b5  coverage-report.txt
564580dad87c793c207a7cc6692554133e21a65fd4dd6fc964e5f819f9ab249c  py27.txt
8b8ff4633af0897652630903ba7155feee543a823e09ced63a14959b653a7340  py27-lowest.txt

Aterrador, ¿no es así? De todas las pruebas, solo dos comparten las mismas dependencias congeladas.

Esta es la realidad de la biblioteca Python general con un buen conjunto de pruebas. Probablemente ahora admitirá que esto es algo bastante diferente de la biblioteca de Python probada en un entorno empresarial.

Jinja2

Comprobando jinja2 , que parece ser una bestia mucho más simple:

  coverage-report: commands succeeded
  py26: commands succeeded
  py27: commands succeeded
  py33: commands succeeded
  py35: commands succeeded
  py36: commands succeeded
ERROR:   docs-html: commands failed
ERROR:   py34: InterpreterNotFound: python3.4
ERROR:   pypy: InterpreterNotFound: pypy

Al ver las sumas de comprobación, me sorprende que py27.txt y py26.txt difieran:

047a880804009107999888a3198f319e5bbba2fa461b74cfdfdc81384499864e  py26.txt
047a880804009107999888a3198f319e5bbba2fa461b74cfdfdc81384499864e  py33.txt
047a880804009107999888a3198f319e5bbba2fa461b74cfdfdc81384499864e  py35.txt
047a880804009107999888a3198f319e5bbba2fa461b74cfdfdc81384499864e  py36.txt
48bd0f6e66a6374a56b9c306e1c14217d224f9d42490328076993ebf490d61b5  coverage-report.txt
743ad9e4b59d19e97284e9a5be7839e39e5c46f0b9653c39ef8ca89c7b0bc417  py27.txt

@vlcinsky Eso es realmente aterrador. Me pregunto si Flask es un caso especial o si en realidad es la norma, pero definitivamente me ha demostrado que estoy equivocado.

Ahora espero que nuestra biblioteca de Python no sufra el mismo problema algún día y que las diferencias sean más manejables allí.

@ Moritz90 Su biblioteca interna atiende a una audiencia completamente diferente, por lo que puede permitirse mantener el contexto de ejecución mucho más estrecho.

Las bibliotecas de Python generales a menudo son flexibles y configurables, por ejemplo, Flask permite que se instalen y utilicen analizadores json alternativos, lo que está cubierto por una ejecución de prueba separada.

Se puede aprender mucho sobre pruebas y toxinas de Flask's tox.ini

las variantes de prueba más bajas se encargan de probar con la versión de dependencia más antigua.

devel está probando contra la versión de desarrollo de las dependencias centrales.

Yo diría que Flask está en el nivel más alto de complejidad y exhibe un conjunto de pruebas cuidadoso.

tox.ini de pyramid muestra un número similar de entornos (también apuntan a una cobertura de código del 100%).

maya's tox.ini es muy fresco (2 días) y simple, incluso aquí hay 4 entornos diferentes y py27 difiere en los requisitos de congelación de py35 y py36.

@ Moritz90
Con respecto al pegamento entre pipenv y tox.

  • pipenv --man muestra algunas instrucciones, cómo usar pipenv dentro de tox.ini comandos
  • tox-pipenv está intentando proporcionar una integración adicional, pero me confunde en este momento.

tox.ini file permite ejecutar comandos arbitrarios, por lo que esto incluye pipenv .

pipenv tiene una gran característica, que cuando se ejecuta en virtualenv ya activado (lo que es el caso dentro de la prueba basada en toxinas), se instala en un entorno virtual dado. Esto es realmente bueno.

Como probablemente necesitemos generar Pipfile.lock , se debe hacer un esfuerzo adicional para obtenerlo y moverlo al lugar adecuado (ag en .tox/py36/Pipfile.lock para evitar que se sobrescriba mediante la siguiente prueba. Esto será posible, pero con cierta simplificación Quizás algún truco con la variable ambiental para la ubicación de Pipfile lo haría aún más simple.

@vlcinsky

  • setup.py : todavía no estoy seguro de entender el problema. Ejecuta pipenv install -e . una vez para que setup.py ahora se rastree a través de su archivo de bloqueo. Y luego ejecute pipenv install cada vez que agregue nuevos paquetes a setup.py.
  • Los desarrolladores se olvidan de actualizar el archivo de bloqueo : pipenv --deploy está diseñado para detectar esto. ¡Ejecútelo en su CI!
  • Pipenv falla , de acuerdo, si hay errores en la herramienta, eso apesta. Pero los errores generalmente se solucionan. Esa no es una razón para desechar toda una filosofía 😞
  • Tox

    • Si Tox es bueno para administrar pruebas, eso es genial. Si también es excelente para administrar paquetes y compilaciones deterministas , eso es aún mejor.

    • Pero si eso fuera cierto, no habría una razón para que Pipenv existiera. Así que solo puedo asumir que hay algún tipo de limitación con Tox.

    • De manera similar a lo anterior, el estado del mundo actual (la falta de una buena interoperabilidad) no suena como una razón para rechazar la filosofía.

  • Múltiples envs

    • Está claro que aquí hay al menos algunos casos especiales, como Flask.

    • No tengo una mejor sugerencia que múltiples archivos de bloqueo (¿aunque tal vez haya una función futura para Pipenv en este sentido?)

    • Pero incluso en este caso, todavía no estoy convencido de que la gestión de varios archivos de bloqueo sea un problema en la práctica. En el peor de los casos, parece que podría crear un script simple update-all-lockfiles.sh localmente y ejecutar pipenv --deploy en su CI para detectar errores.


@ Moritz90 - De acuerdo, el enfoque de "dos fases" puede ser excesivo en la mayoría de los casos. En particular, si está haciendo actualizaciones "manuales" deliberadas / intencionales a su archivo de bloqueo, es completamente innecesario.


De manera más general, sería bueno asegurarse de que esta "propuesta" se centre en las cosas que en realidad son problemas difíciles (en mi opinión, eso es (A) sirviendo múltiples envs, (B) queriendo captar cambios en las dependencias ascendentes). No debe basarse en cosas transitorias (errores en Pipenv) o posibles malentendidos sobre cómo se pretende utilizar la herramienta.

Pero incluso para esos problemas "difíciles", el encuadre debería ser como "en algunos casos extremos complejos, es posible que un flujo de trabajo básico de Pipenv sea insuficiente, así que aquí hay algunas cosas en las que pensar". En mi opinión, no debe enmarcarse como el enfoque predeterminado (porque la mayoría de las personas no tendrán esas preocupaciones).

El ejemplo de documentación proporcionado por @vlcinsky se volvería más simple y menos confuso si Pipenv / Pipfile permitiera el manejo de lib-dependencies , app-dependencies y dev-dependencies . Los documentos podrían verse así:

Utilice la opción lib-dependencies si su paquete es una biblioteca compartida. Ejemplo Pipfile :

[lib-dependencies]
some-lib=="*"
another-lib=="*"
yet-another-one==">=1.0"

[dev-dependencies]
some-dev-tool=="1.1"

Para las bibliotecas compartidas, es importante mantener los rangos de versiones por debajo de [lib-dependencies] tan amplios como sea posible, para evitar conflictos de versiones en el sistema del consumidor.

Si su paquete es una aplicación (destinada a ser instalada por pipenv en el sistema de destino) que requiere versiones de dependencia exactas, debe usar la opción [app-dependencies] . Ejemplo Pipfile :

[app-dependencies]
some-lib=="1.0.12"
another-lib=="1.*"
yet-another-one=="2.0"

[dev-dependencies]
some-dev-tool=="1.1"

/ End doc ejemplo

Otro enfoque podría ser Pipfile.lib y Pipfile.app .

Creo que algo como esto omitiría la necesidad de una gran cantidad de secciones anti-patrón y herramientas de terceros para llenar el vacío.

Llamar a la herramienta de empaquetado pipenv es engañoso si se espera que cree bibliotecas de Python o que esté profundamente involucrado en su creación.

Creo que este es un problema real, que genera mucha confusión. Especialmente entre las personas que están acostumbradas a los administradores de paquetes en otros lenguajes de programación (por ejemplo, JS, Rust, Elm). Me tomó varios meses y leer ocasionalmente los problemas de GIthub, hasta que me di cuenta de que estaba usando Pipenv y setup.py de manera incorrecta.

@feluxe

Su [lib-dependencies] o Pipfile.lib es lo que tenemos hoy en Pipfile (como dependencias abstractas, siendo lo más amplio posible).

Su [app-dependencies] o Pipfile.app es lo que tenemos en Pipfile.lock (como dependencias específicas).

pipenv y sus archivos se pueden usar en dos situaciones diferentes: desarrollar una biblioteca o preparar la implementación de una aplicación, pero probablemente no para ambos a la vez. Por esta razón, no veo razones sólidas para agregar secciones adicionales en Pipenv . Es responsabilidad de los desarrolladores saber qué tipo de propósito servirá el Pipfile .

Creo que este es un problema real, que genera mucha confusión. Especialmente entre las personas que están acostumbradas a los administradores de paquetes en otros lenguajes de programación (por ejemplo, JS, Rust, Elm). Me tomó varios meses y leer ocasionalmente los problemas de GIthub, hasta que me di cuenta de que estaba usando Pipenv y setup.py de manera incorrecta.

Acordado. La solución de tres secciones también es una solución muy interesante que nunca había considerado, y parece ser correcta y (¡sorprendentemente!) Simple.

Viniendo de un entorno de Python, siempre he sentido que el package.json de Node lo está haciendo mal (Rust es mejor porque tiene un compilador y enlazador, y puede resolver esto en una etapa posterior). Tratar las dependencias de aplicaciones y bibliotecas de la misma manera simplemente no funcionará para un lenguaje de scripting como Python, al menos en un sentido abstracto, es decir, podría funcionar para usted, pero una herramienta genérica como Pipenv no puede hacerlo porque necesita ser genérico.

Si bien me gusta la solución de tres secciones en concepto, sigue siendo un cambio bastante incompatible con el ecosistema existente. Ya hay setup.py, setup.cfg y (potencialmente) pyproject.toml llenando este espacio. Si Pipenv (Pipfile, para ser exactos) quiere moverse al espacio, necesita consolidarse con proyectos relacionados, como pip (idealmente, el soporte de la biblioteca debería ser compatible directamente con él) y flit .

Como mencioné en otros problemas relacionados con el manejo de la dependencia de lib / app, esta discusión debe escalarse a pypa-dev (la lista de correo) y / o al proceso PEP, para que pueda ser mejor escuchada por otras partes y personas relevantes, antes de Pipenv. (Pipfile) puede moverse en cualquier dirección.

@vlcinsky

Su [lib-dependencies] o Pipfile.lib es lo que tenemos hoy en Pipfile (como dependencias abstractas, siendo lo más amplio posible).

Perdón si esto no estaba claro. Mis lib-dependencies están destinados a ser lo que la gente actualmente pone en setup.py / install_requires . Quizás pypi-dependencies sería un mejor nombre para lo que quiero decir.

@uranusjr

Ya hay setup.py, setup.cfg y (potencialmente) pyproject.toml llenando este espacio.

Pipenv (la herramienta de línea de comandos) podría interactuar con setup.py . Solo la sección de dependencia de setup.py tendría que moverse a Pipfile. Al menos en mi imaginación :)

Como mencioné en otros problemas relacionados con el manejo de la dependencia de lib / app, esta discusión debe escalarse a pypa-dev (la lista de correo) y / o al proceso PEP, para que pueda ser mejor escuchada por otras partes y personas relevantes, antes de Pipenv. (Pipfile) puede moverse en cualquier dirección.

Ok, perdón por molestarme;) Si encuentro algo de tiempo, escribiré algo para la lista de correo.

Sin embargo, dentro del alcance de esta propuesta, sugeriría que se centrara en las mejores prácticas posibles actualmente, en lugar de entrar en la madriguera de elaborar un nuevo flujo de trabajo para toda la comunidad de empaquetado de Python. Sería más productivo proponer una mejor práctica dentro de las limitaciones actuales y luego comenzar la discusión para las mejoras.

@uranusjr - Vengo de un entorno "compilado", así que tengo curiosidad por saber por qué es así.

Tratar las dependencias de aplicaciones y bibliotecas de la misma manera simplemente no funcionará para un lenguaje de scripting como Python

@ tsiq-oliverc Dado que la mejor práctica de la aplicación requiere que fije sus dependencias, las bibliotecas también comenzarán a fijar las suyas, si utilizan la misma fuente de archivos de requisitos. Esto daría lugar a problemas en la resolución de dependencias.

Digamos que mi aplicación tiene dos dependencias A y B, ambas dependen de C, pero A pines v1, mientras que B pines v2. Los lenguajes compilados permiten que la cadena de herramientas detecte esto en tiempo de compilación y lo resuelva de muchas formas. Rust, por ejemplo, hace esto durante el tiempo de vinculación: el ejecutable final contendría dos copias de C (v1 y v2), con A y B vinculadas a cada una de ellas. En la tierra de C ++ esto se resolvería con bibliotecas dinámicas; la búsqueda de símbolos se realiza incluso más tarde (en tiempo de ejecución), pero la idea es la misma: el compilador sabe lo que necesita (de la interfaz que usa) y puede actuar en consecuencia.

Los lenguajes de secuencias de comandos no pueden hacer esto porque no saben lo que realmente quiere hacer hasta que llega a la llamada. Node soluciona esto asumiendo siempre que las dependencias son incompatibles (A y B siempre obtienen su propia C, incluso si las dos copias son idénticas), pero eso conduce a una nueva clase de problemas y da como resultado hacks incómodos como dependencias entre pares que todos (¿Espero?) Concuerda que son terribles. Python probablemente no quiera ir allí (de todos modos no puede, ya que eso probablemente rompería todas las instalaciones de Python existentes).

Otra forma de evitar esto es hacer algo inteligente en las herramientas de empaquetado que "desconecte" la versión de dependencia. Bundler (de Ruby) hace esto, recomendando a las personas que no incluyan el archivo de bloqueo en la gema, para que Bundler pueda usar las versiones no ancladas en Gemfile, en lugar de las versiones ancladas en Gemfile.lock. Pero las personas tienden a ignorar los consejos y hacer lo que quieran, por lo que aún obtienes versiones ancladas en todas partes.

Probablemente fui demasiado fuerte para decir que simplemente no funcionará . Pero al menos todos los intentos anteriores han fallado, y muchos de los que lo intentaron son personas muy inteligentes, mucho más inteligentes que yo. No creo que esto se pueda hacer, personalmente, y seguiría pensando de esta manera hasta que vea la brillante propuesta que realmente lo hace.

@ tsiq-oliverc Pieter Hintjens escribió en algún lugar un concepto de "Los comentarios son bienvenidos en forma de solicitud de extracción"

Me gusta eso porque cambia el enfoque de los consejos filosóficos a cosas realmente tangibles y prácticas. Y también limita la cantidad de comentarios porque un comentarista a menudo aprende en el camino que la idea está incompleta o de alguna manera rota en el uso real.

Le pedí un ejemplo de biblioteca de Python, donde pipenv se usa correctamente (o al menos se usa) y no proporcionó ninguna.

Comenta las cualidades de tox pero admite que no está familiarizado con ellas, y sigue repitiendo algo sobre las mejores prácticas en el mundo del desarrollo de paquetes de Python.

Dices que Flask posiblemente sea un caso especial. Así que busqué en Github proyectos de Python usando la palabra "biblioteca", ordenados según el número de bifurcaciones (ya que probablemente refleja cuántas personas están haciendo algún desarrollo con él), ignoré todas las "listas curradas de algo" y conté el número de entornos para uno. SO (normalmente Linux):

La cantidad real de entornos en los que ejecutar pruebas será en su mayoría 2 (+ Windows) o 3 (+ OSX) veces mayor.

tox se usa en 2 proyectos de 3 (no lo comparo con Travis o Appveyor, ya que hacen otro nivel de prueba al lado).

El número de entornos para probar es bastante alto, Flask definitivamente no es el más salvaje.

El número de entornos para los que se deben definir dependencias fijas realmente no se puede gestionar manualmente.

Simplemente soltar Pipfile.lock en un repositorio es bastante fácil, pero no hace una mejora mágica (si es así, muéstrame el escenario real, cuándo mejorará la situación).

Tal vez conozca la regla de oro del mundo "compilado" y sienta que el determinismo (o la repetibilidad) también es una necesidad para Python. Como puede ver, muchos proyectos de Python viven bastante bien sin él, por lo que puede ser que la regla de oro no se aplique tan estrictamente aquí.

Estaré feliz si encontramos el uso de pipenv para bibliotecas de Python, lo que mejorará la situación. Y quiero evitar el uso, lo que dañaría la calidad general.

Para alcanzar ese objetivo, mi enfoque es iterar sobre las preguntas:

  • ¿Debo usar la herramienta?
  • ¿Cómo?
  • ¿Por qué, qué valor obtuve?
  • ¿Introdujo algunos problemas? (trabajo extra, errores ocultos ...)

@feluxe

Perdón si esto no estaba claro. Mis dependencias de lib están destinadas a ser lo que la gente pone actualmente en setup.py / install_requires. Quizás pypi-dependencies sería un mejor nombre para lo que quise decir.

Vea la discusión de pbr en este número. Es el esfuerzo de apoyar las dependencias de la biblioteca por Pipfile.

Creo que un Pipfile no se usará para dos propósitos (lib y aplicación), estas cosas se harán por separado. Si cree que es realmente necesario, ¿podría describir el propósito de un proyecto que lo use? Por lo general, trato de mantener separados los proyectos de desarrollo e implementación de bibliotecas, ya que tienen un uso bastante diferente en el tiempo.

@vlcinsky No estoy muy seguro de a dónde quieres llevar esto (¡no estoy seguro de qué tipo de relaciones públicas estás pidiendo!), así que me retiraré de esta conversación por ahora.

Para reafirmar el TL; DR de mi posición:

  1. Las compilaciones deterministas son muy deseables, comunes en otras partes de la industria del software y fáciles de lograr con Pipenv.
  2. Definitivamente hay algunos casos extremos, y alguien debería impulsar el estado de la técnica hacia adelante (a través de soluciones alternativas o mediante mejores herramientas).
  3. Sería irresponsable que los documentos de Pipenv rechacen la recomendación general en contra de (1) simplemente porque (2) afecta a un pequeño subconjunto de casos.

@uranusjr Lo tengo. Aunque no creo que haya nada específico del idioma aquí, es simplemente que diferentes comunidades se han decidido por diferentes heurísticas para tratar un problema sin una solución genérica; si tiene conflictos de versiones, tiene un problema.

Maven / Java (por ejemplo) te obliga a pensar en ello en el momento de la compilación. La forma NPM significa que tiene problemas de tiempo de ejecución si las versiones no coincidentes cruzan una interfaz. La resolución en tiempo de ejecución (por ejemplo, Python, bibliotecas dinámicas) significa que un dependiente puede fallar / etc. si la versión de dependencia no es la que esperaba.

@vlcinsky

Consulte la discusión de pbr en este número. Es el esfuerzo de apoyar las dependencias de la biblioteca por Pipfile.

pbr parece agradable y todo, pero entra en la categoría que estaba tratando de abordar con esto:

Creo que algo como esto omitiría la necesidad de una gran cantidad de secciones anti-patrón y herramientas de terceros para llenar el vacío.

Creo que estas herramientas no deberían ser necesarias en primer lugar.

Si cree que es realmente necesario, ¿podría describir el propósito de un proyecto que lo use? Por lo general, trato de mantener separados los proyectos de desarrollo e implementación de bibliotecas, ya que tienen un uso bastante diferente en el tiempo.

Cuando se trata de paquetes pypi, terminé usando Pipenv para manejar las dependencias de desarrollo, Pipfile para describir las dependencias de desarrollo, setup.py para describir las dependencias de lib con install_requires y setuptools en setup.py para publicar mi paquete con pipenv run python setup.py bdist_wheel upload . Esto es lo que considero complicado.

En otros lenguajes modernos, tengo que aprender una herramienta de línea de comandos (administrador de paquetes) más un formato de archivo de dependencia. La documentación está en un solo lugar y es más fácil de seguir, y un recién llegado resolverá todo esto en un par de horas. Es una cuestión de npm init , npm install foo --dev , npm publish . Pipenv / Pipfile ya puede hacer la mayor parte, si pudiera hacerlo todo, problemas como este no existirían.

Vuelvo a repetir mi llamado a una especie de sección / wiki de "comunidad" para esta divulgación. Hay varios "patrones" que pueden ser legítimos y algunos de nosotros podríamos querer compartirlos como "forma de hacer bibliotecas de Python", algunos como yo con pbr, y otros pueden tener un patrón muy bueno. Pero una página dentro del documento pipenv, no estoy seguro de si es una buena idea.

PD: para preparar la migración a la nueva pypi, debe usar twine y no la carga de python setup.py. El uso de "cargar" debe considerarse como un antipatrón.

¿Quizás pipenv pueda hacer crecer comandos de "publicación"?

@feluxe Es posible que desee echar un vistazo a la poesía . Simplemente me tropiezo con él y parece que es lo que estás buscando.

Hace lo que hace pipenv y más y parece que lo hacen mejor, especialmente en lo que respecta a la gestión de dependencias (al menos eso es lo que pretenden). Se encarga de la gestión de dependencias, empaquetado y publicación de todo en una sola herramienta poetry .

Me pregunto si pipenv y poetry podrían reunir esfuerzos para finalmente darle a Python un verdadero administrador de paquetes.

Quiero reiterarme nuevamente antes de que esta discusión vaya demasiado lejos. Pipenv no puede simplemente hacer crecer un comando publish , o hacer cualquier cosa que intente hacerse cargo de la tarea de empaquetar. Esto solo fragmentaría más el ecosistema porque no todos lo hacen de esta manera, y dado que las dependencias de aplicaciones y bibliotecas son teóricamente diferentes, no puede decirle a alguien que las vuelva a fusionar una vez que se haga la distinción en su flujo de trabajo.

Puede parecer que casi todo el mundo está de acuerdo con esta fusión, pero la verdad es que hay muchas más personas que no se unen a esta discusión porque las cosas les funcionan y están haciendo otra cosa. Lo he dicho repetidamente: la discusión sobre la mejora del diseño de cadenas de herramientas y formatos de archivo debería ocurrir en algún lugar más alto en la jerarquía de paquetes de Python, por lo que recibe más exposición a las personas que diseñan cosas más fundamentales en las que se basa Pipenv. Por favor, lleve la discusión allí. No sirve de nada sugerirlo aquí, porque Pipenv no está en condiciones de cambiarlo.

Lo he dicho repetidamente: Ddiscusión sobre la mejora del diseño de cadenas de herramientas y formatos de archivo debería ocurrir en algún lugar más alto en la jerarquía de paquetes de Python, por lo que recibe más exposición a las personas que diseñan cosas más fundamentales en las que se basa Pipenv.

Estoy de acuerdo en que la discusión sobre este error se sale de control ahora que surgieron el empaquetado y la publicación (¡este error solo se trata de la administración de dependencias!), Pero ¿podría indicarnos el lugar correcto para tener esta discusión? La gente lo está teniendo aquí porque pipenv se considera un paso muy necesario en la dirección correcta, no porque quieran imponer responsabilidades adicionales a los mantenedores de pipenv.

Editar : Lo siento, debo haber perdido la publicación en la que hiciste exactamente eso cuando leíste los nuevos comentarios la primera vez.

Sin embargo, dentro del alcance de esta propuesta, sugeriría que se centrara en las mejores prácticas posibles actualmente, en lugar de entrar en la madriguera de elaborar un nuevo flujo de trabajo para toda la comunidad de empaquetado de Python. Sería más productivo proponer una mejor práctica dentro de las limitaciones actuales y luego comenzar la discusión para las mejoras.

Yo estoy muy de acuerdo con esto. Primero deberíamos averiguar cuál es el mejor flujo de trabajo posible para los mantenedores de bibliotecas en este momento antes de elaborar grandes planes. Así que centrémonos en eso de nuevo, como hicimos al principio de este hilo. No creo que hayamos llegado a una conclusión todavía.

Volver al tema: Citando la publicación de

Otra forma de evitar esto es hacer algo inteligente en las herramientas de empaquetado que "desconecte" la versión de dependencia. Bundler (de Ruby) hace esto, recomendando a las personas que no incluyan el archivo de bloqueo en la gema, para que Bundler pueda usar las versiones no ancladas en Gemfile, en lugar de las versiones ancladas en Gemfile.lock. Pero las personas tienden a ignorar los consejos y hacer lo que quieran, por lo que aún obtienes versiones ancladas en todas partes.

Probablemente fui demasiado fuerte para decir que simplemente no funcionará. Pero al menos todos los intentos anteriores han fallado.

Todavía no veo por qué la recomendación oficial para las bibliotecas por ahora no puede ser usar pipenv para sus compilaciones de CI, pero mantenga Pipfile.lock fuera del control de la fuente. Dado que, como señalaron algunas personas, pipenv actualmente no tiene nada que ver con el proceso de empaquetado, no deberíamos encontrarnos con el problema que describiste anteriormente.

Y tampoco veo por qué este es un argumento en contra de definir sus dependencias abstractas en el mismo archivo que usan las aplicaciones para definir sus dependencias abstractas. Está bien si pipenv no quiere implementar una solución elaborada para integrar Pipfile con setup.py , pero no veo por qué es una mala idea en general.

@vlcinsky

Creo que un Pipfile no se utilizará para dos propósitos (lib y aplicación), estas cosas se harán por separado.

Vea mi publicación anterior. ¿Podrías explicar por qué piensas eso? Simplemente, no veo ningún inconveniente en principio. En este momento, podría ser una mala idea incluir un Pipfile , ya que luego tendrá que definir las dependencias de la misma manera en dos archivos diferentes, pero aún no he visto ningún argumento que explique por qué sería una mala idea usar Pipfile para declaraciones de dependencia en general.

Tenga en cuenta que ya he acordado que Pipfile.lock no debería estar en el control de código fuente de las bibliotecas a menos que se encuentre en la misma situación en la que yo estoy.

Editar : Además, si resulta que pipenv sí mismo realmente necesita conocer la diferencia, puede introducir algo como el campo crate-type carga antes de comenzar a introducir app-dependencies y lib-dependencies - eso suena demasiado complicado.

@ Moritz90 Varias de las listas de correo de Python serían buenos lugares para llevar a cabo esta discusión.

pypa-dev es el más definido para las discusiones que se centran en el empaquetado de Python y el ecosistema que lo rodea. Probablemente comenzaría aquí si tuviera que publicar una discusión similar.

python-ideas es un lugar para discutir ideas y tiene una visibilidad bastante alta para toda la comunidad de Python. También sería un buen punto de partida si desea llevar esto al nivel de PEP (creo que eventualmente lo haría).

@ tsiq-oliverc

Por relaciones públicas quiero decir: mostrar un ejemplo que demuestre que su concepto es viable.

Así que elija alguna biblioteca existente, bifurque, aplique su (1) - usted dice que será fácil con pipenv y muéstremelo. Me esforcé bastante y tengo dificultades.

Si su (2) significa "alguien más tiene que hacer el trabajo", su RP no existirá.

En (3) se habla de "pequeños subconjuntos de casos" sin dar ningún número real. ¿Todas las bibliotecas principales que describí con respecto al número de virtualenvs se consideran "subconjuntos pequeños"?

Para concluir esta discusión, creé un breve resumen de lo que se encontró durante la discusión.

Enfoque: pipenv (anti) patrones para bibliotecas y aplicaciones de Python

Cambié un poco el enfoque: habla no solo de bibliotecas de Python (generales), sino también de aplicaciones, ya que era bastante barato incluirlo y demuestra bien las diferencias.

Excluí intencionalmente cualquier cosa que proponga cambios en las herramientas existentes, como pipenv , tox etc.

¿Qué es pipenv y qué no?

  • es una herramienta de despliegue, que permite definir y aplicar dependencias concretas mediante Pipfile.lock .
  • es una herramienta de gestión virtualenv.
  • NO es una herramienta de empaquetado en el sentido de generar un paquete de Python.

Bibliotecas y aplicaciones

El producto (software de Python) está listo para usarse en otro producto (por lo tanto, biblioteca) o es la aplicación final lista para ejecutarse.

Personalmente, creo que incluso las "bibliotecas empresariales" entran en la categoría de biblioteca (se aplican las mismas reglas, solo el número de contextos de ejecución es menor).

Tipos de productos de software

  • biblioteca: para usar en otro producto (biblioteca o aplicación)
  • aplicación: para implementar y ejecutar

Métodos de instalación:

  • biblioteca: pipenv install <package> así "poner el paquete en juego (resolviendo versiones para otras bibliotecas)"
  • aplicación: pipenv sync así "aplicar dependencias concretas"

Dependencias abstractas y concretas

dependencias de productos de software

  • dependencias abstractas: debe nombrar las bibliotecas utilizadas, puede restringir las versiones o el uso, pero debe permanecer lo suficientemente flexible (no debe fijar versiones)
  • dependencias concretas: deben anclar versiones, idealmente con hashes de bibliotecas usadas

    pipenv artefactos:

  • Pipfile : dependencias abstractas

  • Pipfile.lock : dependencias concretas (bloqueadas) "

Contextos de ejecución

Número típico de contextos de ejecución diferentes

  • Biblioteca:

    • python virtualenvs en un sistema operativo: 3 a 9 (tornado usando 30)

    • número de SO: 1 a 3 (Linux, OSX, Windows)

    • número total: 3 a 18

  • solicitud:

    • python virtualenvs en un sistema operativo: 1

    • número de SO: 1

    • número total: 1 (o muy pocos)

Objetivos, prioridades y determinismo de CI

Objetivo de CI

  • Biblioteca:

    • el código incl. Sus dependencias abstractas permiten la instalación y la función esperada dentro de todas las variantes esperadas de contextos de ejecución.

    • cuando pypi (privado / público) obtiene la actualización de la biblioteca de dependencias, falla, si afecta la instalación de la función de la biblioteca probada.

  • solicitud:

    • cuando se instala (usando dependencias concretas / ancladas), toda la funcionalidad esperada se proporciona dentro de un contexto de ejecución predefinido

Objetivos particulares de CI con respecto a la funcionalidad:

  • Biblioteca:

    • las dependencias abstractas declaradas por la biblioteca están completas e incluyen todas las restricciones necesarias (solo donde sea necesario): la biblioteca se instala correctamente

    • todos los casos de uso esperados funcionan correctamente

  • solicitud:

    • Las dependencias de hormigón están completas y todas están ancladas, mejor incl. hashes: la aplicación se instala correctamente

    • todos los casos de uso esperados funcionan correctamente

Diferentes modos de prueba de CI

  • Modo: "Correr, Forrest, Correr"

    • La gran mayoría de las bibliotecas de Python en la actualidad se prueban de esta manera.

    • use tox o un software de prueba similar

    • Sin uso de pipenv y dependencias concretas (puede estar bien para bibliotecas)

  • Modo: "Generar y sellar"

    • No Pipfile en el repositorio

    • Pipfile.lock creado por pipenv install -e .

    • Pipfile.lock documenta (sella) el entorno y permite la reproducción posterior de virtualenv para analizar problemas.

  • Modo: "Edad de Hielo"

    • prueba de dos fases

      • cuando las dependencias abstractas (definidas dentro de setup.py install_requires ) cambian o se actualiza el paquete dependiente en pypi, regenere Pipfile.lock por pipenv install -e .

    • función de prueba de ejecución: se ejecuta cuando cambia el código de la biblioteca. Se ejecuta dentro de virtualenv creado por pipenv sync Pipfile.lock

      Cómo y quién puede Pipfile.lock crear

  • manualmente por el desarrollador (puede funcionar para aplicaciones)

  • automáticamente por compilación de CI (y pasando todas las pruebas, declare que es un artefacto verificado)

Prioridad del determinismo frente a la flexibilidad

  • biblioteca: flexibilidad (ejecutar siempre que sea posible)
  • aplicación: determinismo (se ejecuta exactamente de la misma manera dentro del contexto de ejecución seleccionado)

    Qué puede afectar el determinismo del producto instalado:

  • pypi público (determinismo bajo, los paquetes se actualizan en cualquier momento)

  • pypi privado (mayor determinismo, las actualizaciones de paquetes pueden ser controladas)
  • requisitos abstractos dentro de las bibliotecas (no se utilizará para determinar el determinismo)
  • requisitos concretos ( Pipfile.lock ): determinismo total

Diverso

Algunos casos de uso de Pipfile.lock :

  • (antipatrón) define las dependencias abstractas de la biblioteca (porque debe ser abstracto)
  • (antipattern) configura virtualenv para la biblioteca probada (puede ocultar dependencias abstractas de bibliotecas rotas)
  • Documentar virtualenv exacto ("sello"), donde se ejecutó una prueba (por lo tanto, permita que el desarrollador la vuelva a crear más tarde para las pruebas rotas y experimente en ella)
  • configurar virtualenv para la aplicación probada
  • implementar la aplicación en producción

    Otras sugerencias

  • pbr library permite la definición de dependencias de bibliotecas abstractas a través de requirements.txt . La lectura de actualización Pipfile está en camino.

  • poetry paquete intenta algo similar a pyenv

    Problemas comunes

  • "suelte el archivo de bloqueo en el repositorio" y obtendrá compilaciones deterministas:

    • Funcionará para aplicaciones.
    • No funcionará para bibliotecas, ya que existen numerosos contextos de ejecución y cada uno tiene un buen potencial para dar como resultado diferentes Pipfile.lock . En serio: flask muestra en sus 11 entornos virtuales diferentes (en un sistema operativo) 10 dependencias bloqueadas diferentes. ¿Quién los va a crear y comprometer?
    • Tenga en cuenta que con el modo CI "Generar y sellar", aún puede obtener Pipfile.lock (pero generado por el script CI) que permite regenerar el virtualenv en otro lugar.
  • Pipfile.lock en el repositorio de la biblioteca

    • si se usa para crear virtualenv, puede ocultar la definición rota de las dependencias de la biblioteca dentro de setup.py .

  • Pipfile en el repositorio de la biblioteca

    • si repite dependencias abstractas (que se definirán dentro de setup.py ), puede ocultar la declaración de dependencia setup.py rota.

    • recomendado: Genere Pipfile por pipenv install -e . o pipenv install -e .[tests] si también necesita probar dependencias y se declaran como "pruebas" extras en el setup.py

  • agregar pipenv install <something> en los scripts de CI

    • no mejora mucho el determinismo por sí solo

    • consulte el modo CI "Generar y sellar" (entonces toda la instalación en virtualenv debe ir a través de pipenv ).

Conclusiones

Las bibliotecas de Python (especialmente las generales) exhiben un número inesperadamente alto de contextos de ejecución. La razón es que con las bibliotecas el objetivo es la flexibilidad demostrada en diferentes condiciones. La flexibilidad parece más importante que las construcciones deterministas. Para las personas que vienen del mundo de la "compilación", esto puede parecer un antipatrón muy malo. El hecho es que la mayoría (posiblemente todas) las bibliotecas de Python no proporcionan compilaciones deterministas (si conoce algunas, hágamelo saber) y Python todavía lo está haciendo muy bien. Las razones por las que las aplicaciones de Python siguen vivas pueden ser: Python, como lenguaje de programación, difiere del mundo compilado. La otra razón podría ser que el determinismo puede (deberá) resolverse un paso después tan pronto como y la aplicación (compilada a partir de un conjunto de bibliotecas) resuelva el requisito (natural y justificado) del determinismo.

Para las aplicaciones, la situación es justamente opuesta y aquí el determinismo es realmente fácil de alcanzar con herramientas como pipenv .

¿Qué hacer a continuación?

  • No tendré tiempo de ocuparme de esto durante la próxima semana o dos.
  • Me imagino creando una serie de entradas de blog en algún lugar (no estoy seguro de dónde). Si conoce el lugar (idealmente permitiendo alguna discusión), sería un lugar natural para permitir que el contenido sea referenciado, discutido y finalmente posiblemente (si sobrevive) escrito en piedra :-).
  • Propongo a @uranusjr tomar el control de este problema (cerrarlo, decidir qué hacer a continuación, redirigir a las personas a otra parte o lo que parezca práctico)

Gracias a todos por una discusión tan inspiradora. Me parece un mensaje "Estoy totalmente perdido en este tema" refactorizado tres veces, lo que significa que, naturalmente, mejoramos.

@vlcinsky poetry no tiene nada que ver con pyenv . Es muy parecido a pipenv (pero con una implementación mucho mejor en cuanto a la gestión de bibliotecas y aplicaciones, IMO) pero con la parte de empaquetado y publicación.

Tiene un archivo pyproject.toml que define su proyecto y sus dependencias (dependencias abstractas) y un pyproject.lock que describe las dependencias ancladas y están ancladas para cualquier versión y plataforma de Python el pyproject.toml file ha especificado para tener solo un archivo de bloqueo determinista para evitar los problemas que enfrenta pipenv . Solo durante la instalación, poetry comprobará qué paquetes instalar al compararlos con el entorno.

Y cuando empaqueta su biblioteca, usará las dependencias abstractas (y no la anclada) para que mantenga la flexibilidad al distribuir su paquete (a través de PyPI, por ejemplo).

La ventaja de esto es que usará dependencias abstractas para bibliotecas y el archivo de bloqueo para aplicaciones. Esto es lo mejor de ambos mundos.

La poesía anulando todo el propósito. Pipenv es _idempotente_ y esto requiere _reproducción_ de un entorno. Deje de usar este problema como plataforma para intentar venderle a todos algo que se ha enumerado como su primera razón de por qué usarlo en lugar de pipenv que al autor no le gusta el cli. Al final del día, nuestro software se implementa en cientos de miles de máquinas y, de hecho, reconoce y utiliza las mejores prácticas en materia de empaque. Si no desea un entorno idempotente y desea difuminar las líneas entre desarrollo y empaquetado, no participe en esta discusión porque no nos estamos moviendo en esa dirección y no será productivo.

Esencialmente, dedicamos mucho tiempo y esfuerzo a la capacidad de recuperación en la que los proyectos pequeños que hacen afirmaciones elevadas no tienen que gastar tanto esfuerzo porque las personas no están llegando a casos extremos. Si realmente cree que otra herramienta le ofrece lo mejor de todos los mundos, le animo a que la utilice: pipenv no se encargará del embalaje por usted a corto plazo, si es que lo hace.

@techalchemy No estoy vendiendo nada, en realidad, simplemente me estoy dirigiendo hacia ideas que podrían usarse en pipenv .

Y poetry fija dependencias en pyproject.lock , al igual que pipenv en Pipfile.lock . Por lo tanto, tiene una reproducción como la que proporciona pipenv . Si tiene un archivo de bloqueo, se usará e instalará la dependencia anclada y, si no me equivoco, también es lo que hace pipenv .

La única vez que usa dependencias abstractas es cuando empaqueta el proyecto para su distribución (básicamente para bibliotecas) ya que en este caso no desea dependencias ancladas.

@vlcinsky Todavía hay algunos puntos que deben resolverse, corregirse o ampliarse, pero todavía estoy muy interesado en que esto pase a la forma de documentación, Pipenv o de otro tipo. ¿Estaría interesado en enviar una solicitud de extracción? Estaría más que feliz de ayudar a desarrollar el artículo.

En cuanto a la poesía, personalmente no soy un fanático en su conjunto, pero hace muchas cosas correctas. Probablemente no debería mencionarse en los documentos de Pipenv porque viola algunas de las mejores prácticas a las que los desarrolladores de Pipenv quieren impulsar a la gente, pero debería mencionarse si la discusión se lleva a cabo en pypa-dev o similar, para proporcionar una imagen completa de cómo el empaquetado ecosistema actualmente es.

la poesía también puede requerir más atención y contribución. Esto sería lo mejor para la comunidad, incluido Pipenv. Con opciones viables, la gente puede considerar sus opciones en lugar de ir a Pipenv de cabeza y quejarse de que no está haciendo lo que esperan. La buena competencia entre bibliotecas también puede impulsar mejoras técnicas en el frente de la resolución de dependencias, lo que Pipenv y la poesía hacen (y no a la perfección). Podemos aprender mucho unos de otros.

@uranusjr Sí, creo que se aclararon pocas cosas y merecen ser compartidas con una audiencia más amplia. Su ayuda es realmente bienvenida.

¿Qué pasa con la "redacción de documentación por pares"? Creo que en este momento sería más eficaz trabajar en él en pequeña escala de solo dos personas.

Lo que se piensa hacer son (posiblemente con una o dos iteraciones):

  • dónde exactamente podríamos publicar eso
  • identificar elementos de documentación (artículos, secciones)
  • aclarar el alcance y el objetivo de cada elemento
  • acordar el esquema
  • identificar problemas abiertos
  • resolverlos
  • escribe el doc
  • publicar (y espero que sea aceptado)

Si tiene ganas de escribirlo por su cuenta (basado en lo que se discutió) y me tiene como revisor, no me quejaría.

Me pondré en contacto con usted por correo electrónico para acordar las próximas acciones.

@vlcinsky También estoy disponible como @uranusjr en PySlackers (un espacio de trabajo de Slack) si prefieres la interacción en tiempo real. Pipenv tiene un canal allí ( #pipenv ).

@uranusjr Eso es lo que quise decir con reunir esfuerzo. Python necesita desesperadamente un buen administrador de paquetes como cargo. El ecosistema de Python palidece en comparación con los otros lenguajes debido a la falta de una forma estándar de hacer las cosas. Y creo que pipenv no ayudará con eso.

Lo que me molesta es que pipenv anuncia a sí mismo como the officially recommended Python packaging tool mientras que no es una herramienta de empaquetado, ni mucho menos, lo cual es engañoso para los usuarios. Es simplemente un administrador de dependencias junto con un administrador virtualenv.

Además, dice que se inspiró en cargo, npm, hilo que son herramientas de empaque junto con administradores de dependencia, mientras que las tuberías no lo son.

Y aquí está el defecto de pipenv , simplemente enturbia el agua ya que la gente seguirá cometiendo los mismos errores que antes con requirements.txt vs setup.py . Los proyectos aún estarán mal empaquetados con dependencias mal definidas en sus setup.py debido a eso. Eso es lo que hicieron bien los proyectos como Cargo: manejan todos los aspectos del desarrollo de proyectos / aplicaciones para garantizar la coherencia, mientras que un proyecto como pipenv no lo hace.

Y cuando dices:

lo que Pipenv y la poesía hacen (y ninguno perfectamente)

¿Qué quieres decir? Por lo que he visto, su administrador de dependencias es mucho más resistente que el proporcionado por pipenv . El único inconveniente es que utilizan la API JSON de PyPI, que a veces no tiene información de dependencia debido a paquetes mal publicados.

De todos modos, creo, como dijiste, que ambos proyectos pueden aprender el uno del otro.

Y, una cosa más, ¿cuál es el futuro de pipenv si, en última instancia, pip maneja el Pipfile ? ¿Será solo un gerente virtualenv?

Si el administrador de dependencias de poesía se basa en la api json, no solo a veces es incorrecto debido a 'paquetes mal publicados', sino que va a ser muy limitado en lo que realmente puede resolver correctamente. El almacén json api publica las dependencias _más recientes_ incluso si está tratando con una versión anterior, y eso es si tiene esa información. También solíamos incorporar la api json, era genial porque era rápido, pero el equipo de infraestructura nos dijo que no confiáramos en él. Parece un poco falso llamar a algo resistente si se basa en una fuente poco confiable para empezar.

En última instancia, los desafíos están en torno a la construcción de un gráfico de dependencia que ejecutó un archivo de configuración porque actualmente, así es como funciona el empaquetado. Simplemente no hay forma de evitarlo. Un gráfico de dependencia que se resuelve en mi máquina puede ser diferente de uno que se resuelve en su máquina incluso para el mismo paquete.

Es fácil saludar con la mano y decir 'bueno, ¿eso no convierte a pipenv en un administrador virtualenv si pip puede leer un archivo pip?' No. Pipenv es un administrador de dependencias. Gestiona entornos idempotentes y genera un archivo de bloqueo reproducible. Me doy cuenta de que esto debe parecerle trivial porque lo está descartando y reduciendo esta herramienta a un administrador virtual, pero no lo es. Resolvemos archivos de bloqueo e incluimos marcadores para las versiones de Python que no tiene, que no está usando y lo mantenemos disponible para que pueda implementarlo y reproducirlo con precisión en todas las plataformas y versiones de Python. Usamos varios métodos de resolución, incluido el manejo de ruedas y archivos locales, repositorios vcs (también resolvemos el gráfico allí) artefactos remotos, paquetes pypi, índices privados, etc.

Al final del día, pip _will_ manejará pipfiles, ese es el plan, ha sido el plan desde que se creó el formato. Pero eso es lo mismo que preguntar "pero ¿qué pasa cuando pip puede manejar archivos de requisitos?" La pregunta es básicamente idéntica. Pip puede instalar ese formato. No es realmente relevante para ninguna de las funciones que describí, aparte de que también instalamos los archivos (usando pip, por cierto).

@techalchemy

El almacén json api publica las dependencias más recientes incluso si está tratando con una versión anterior, y eso es si tiene esa información

Esto es simplemente incorrecto, puede obtener dependencias de una versión específica llamando a https://pypi.org/pypi/{project}/{release}/json . Si solo llama a https://pypi.org/pypi/{project}/json seguro que solo obtendrá las últimas dependencias, pero en realidad puede obtener el conjunto correcto de dependencias.

Y la parte de empaquetado / publicación de los proyectos de Python realmente necesita mejorarse porque al final beneficiará a todos, ya que permitirá usar la API JSON de manera confiable.

Gestiona entornos idempotentes y genera un archivo de bloqueo reproducible.
Resolvemos archivos de bloqueo e incluimos marcadores para las versiones de Python que no tiene, que no está usando y lo mantenemos disponible para que pueda implementarlo y reproducirlo con precisión en todas las plataformas y versiones de Python.

Y también poetry . Y puede hacer que no use la API JSON para proporcionar el mismo método de resolución que pipenv (usando pip-tools). Consulte https://github.com/sdispater/poetry/issues/37#issuecomment -379071989 y seguirá siendo más resistente que pipenv (https://github.com/sdispater/poetry#dependency-resolution )

@zface Diré esto una última vez, por favor llévelo a un lugar más alto en la jerarquía. Pipenv no se autoproclama como la herramienta de empaquetado de Python recomendada oficialmente; dice eso porque lo es . Si cree que es inapropiado, dígaselo a los funcionarios que recomiendan Pipenv . Por favor, no pongas estas cosas en Pipenv dev. Este es el lugar equivocado para presentar una queja y no es posible que obtenga aquí una resolución para sus quejas. También puede obtener mejores respuestas a las preguntas técnicas que tenga allí. Este es un rastreador de problemas para Pipenv, no un panel de discusión para las herramientas de empaquetado de Python y cómo se realiza el empaquetado de Python.

Pipenv no solo confía en las herramientas pip para la resolución, por favor deje de reducir nuestro software a una sola línea que demuestre una falta de comprensión. Sé muy bien cómo funciona la api de PyPI, hablé directamente con el equipo que la implementó.

Esto simplemente está mal

Este tipo de actitud no es bienvenida aquí. No asuma que no entendemos de lo que estamos hablando. Practique la cortesía.

seguirá siendo más resistente que pipenv (https://github.com/sdispater/poetry#dependency-resolution)

Actualmente, Pipenv no aplana los gráficos de dependencia. Señalar un problema específico en el que se ha aplanado un árbol y afirmar que toda la herramienta es, por lo tanto, mejor y más resistente es una tontería, está demostrando una y otra vez que simplemente está aquí para insultar a pipenv y promover la poesía. Por favor, siga su camino, este comportamiento no es bienvenido.

Estoy de acuerdo en que la discusión está fuera de tema, que estaba tratando de capitalizar las "buenas prácticas" en torno a pipenv.

Sin embargo,

[...] seguirá cometiendo los mismos errores que antes con requirements.txt vs setup.py. Los proyectos aún estarán mal empaquetados con dependencias mal definidas en su setup.py debido a eso.

Comparto esta opinión, lograr que los nuevos desarrolladores empaqueten con éxito su propio código Python es realmente complejo, demasiado complejo, requiere leer mucha documentación en línea.
Pero no depende de pipenv ni de ninguna otra dependencia del paquete lidiar con eso por completo. No pudimos reescribir la historia. Nosotros, como comunidad, necesitamos encontrar una manera de modernizar la cadena de herramientas de Python, paso a paso.

Y pipenv (y probablemente la poesía) es un muy buen paso adelante.

Tener que mantener en un lado Pipfile para la aplicación y setup.py para las bibliotecas en el otro lado, es una obviedad. No importa lo difícil que lo expliquemos con muchas palabras y artículos largos y guías de buenas prácticas, es demasiado complejo para lo que es. Estoy completamente de acuerdo en que es así por el momento, pero eso no debería impedirnos imaginar una forma mejor y más segura.
Al final, como desarrollador, quiero una sola herramienta, tal vez con dos modos diferentes, que me ayude y me haga la vida lo más fácil posible.

Debería ser una forma de extraer solo la parte que hace el requirements.txt/Pipfile de bibliotecas como PBR para proponer una especie de 'setup.py fácil', un contenedor compatible con Pipfile alrededor de install_requires , sin todo el comportamiento no deseado que trae pbr, y empaquetarlo en un contenedor dedicado de setuptools que solo hace eso.

Para que podamos tener lo mejor de cada mundo:

  • pipenv para mantener Pipfile (versionado tanto en bibliotecas como en aplicaciones)
  • pipenv para mantener Pipfile.lock (versionado solo para aplicaciones)
  • uno usaría este paquete de envoltura mágica ( pipfile_setuptools , install_requires_pipfile ?) que sería una dependencia de primer nivel cuyo trabajo es solo inyectar Pipfile en install_requires .

Este es otro proyecto que no estaría relacionado con pipenv , pero aún necesita una biblioteca de analizador genérica Pipfile . ¿Qué piensas?

@gsemet Según tengo entendido, PyPA ha estado tratando de llenar eso con pyproject.toml, liderado por flit . Primero deberá hablar con ellos (en pypa-dev o distutils-sig) sobre esto antes de proceder a usar Pipfile como formato fuente. En cuanto al análisis de Pipfile (y el archivo de bloqueo), eso se maneja en pypa / pipfile (que los proveedores de Pipenv proporcionan la lógica de análisis central).


Editar: Envíeme un mensaje si decide iniciar una discusión sobre esto en cualquiera de las listas de correo. Tengo algunas ideas sobre cómo podemos unir las dos partes de la distribución de paquetes de Python.

Debo admitir que estoy un poco triste al ver las dependencias declaradas en pyproject.toml (que toma los roles como setup.cfg hecho por PBR), mientras que PyPa también admite Pipfile ....

Gracias por el puntero para revolotear y pipfile. También está el pipenvlib de Kennethreitz que parece más ligero.

Setup.cfg de PBR parece más completo en comparación con la documentación oficial (por ejemplo: data_files ) y reutilizar un archivo ya compartido con varias herramientas (flake8, pytest, ...) puede usar el mismo archivo, reduciendo el número de archivo en la raíz de un proyecto de Python)

¿Fue útil esta página
0 / 5 - 0 calificaciones