Pip: Resolver problemas relacionados con compilaciones fuera del árbol

Creado en 4 ene. 2020  ·  68Comentarios  ·  Fuente: pypa/pip

Abro este número como un intento de consolidar la discusión sobre las compilaciones fuera del árbol, los problemas relacionados y las posibles soluciones.

¿Cuál es el problema que resolverá esta función?

Al crear proyectos desde directorios locales, pip primero los copia en una ubicación temporal .

Este enfoque ha planteado una serie de problemas a lo largo del tiempo:

  • Cuando setup.py / pyproject.toml no está en la raíz del proyecto y cuando la compilación depende de recursos fuera del subárbol setup.py / pyproject.toml (# 3500, # 7549, # 6276), por ejemplo:

    • la compilación necesita recursos que son enlaces simbólicos a archivos / directorios fuera del subárbol

    • build necesita el repositorio de git (por ejemplo, cuando se usa setuptools_scm), y .git / está en un directorio principal no copiado al directorio temporal por pip

    • build se basa en el nombre del subdirectorio (quizás algo exótico, pero tengo un caso en el que quiero crear un backend de compilación personalizado y parte de los metadatos depende del nombre del subdirectorio)

  • Problemas de rendimiento cuando el directorio del proyecto es grande (# 2195).

¿Por qué pip copia a un directorio temporal antes de compilar? Advertencia: esto no me queda claro, esto es lo que recopilé hasta ahora:

  • Para evitar depender de algo fuera de la fuente (https://github.com/pypa/pip/issues/2195#issuecomment-524606986), aunque la definición de "fuera de la fuente" es la causa de algunos problemas anteriores
  • Para evitar contaminar el directorio de origen con artefactos de compilación o residuos (?)
  • ¿Algo más?

Soluciones posibles

  1. Construya una sdist en su lugar, desempaquete la sdist en una ubicación temporal y luego construya a partir de eso.
  2. Agregue una opción de pip para construir en su lugar.
  3. Actualice PEP 517 con algún tipo de mecanismo para permitir que los back-end se comuniquen con los front-end si son "seguros" para las compilaciones in situ.
  4. Cambie el pip para construirlo siempre en su lugar.
  5. Cambie pip para construir en su lugar de forma predeterminada con una opción para construir fuera del árbol.

Contexto adicional

Más información sobre la construcción a través de sdist en discus.python.org .

needs discussion

Comentario más útil

Viniendo de https://github.com/pypa/pip/issues/2195#issuecomment -664728481 Puedo decir que estoy más que feliz de volver a hacer # 7882 detrás de --use-feature=in-tree-build .

Todos 68 comentarios

Mirando esto desde la perspectiva del back-end, la idea de una "construcción fuera del árbol" en realidad no tiene sentido. El back-end recibe un "árbol de fuentes" en la forma del directorio actual del proceso, y se le pide que ejecute una compilación. No tiene forma de saber si ese directorio fue extraído de un sdist, extraído de un VCS o copiado de algún otro lugar. Todo lo que puede hacer es construir, y si no tiene lo que necesita para construir, reportar fallas.

De manera que mata a más o menos posible solución (3) de la OMI - el servidor no tiene un concepto de lo que es una acumulación en el lugar es, por lo que no puede decir si una tal construcción es seguro 1.

Con respecto a (2), generalmente me opongo a opciones adicionales como esta. Si sabemos qué es lo mejor que podemos hacer, deberíamos hacerlo, y si no lo sabemos, pasar el problema al usuario no es una opción particularmente amigable. El problema aquí es lo suficientemente sutil como para esperar que pocos usuarios sepan cuál es la opción correcta, por lo que es probable que veamos a la gente simplemente probando las 2 opciones y usando ciegamente "lo que funcione". Además, las implicaciones de soporte son significativas: ofrecer una opción implica claramente que esperamos que los resultados de la compilación sean diferentes en al menos algunos casos, entonces, ¿cómo probamos que las diferencias sean las que esperamos? ¿Somos responsables de educar a los usuarios sobre cuándo se necesitaría o no la marca de compilación en el lugar (ya sea a través de nuestra documentación o como resultado de problemas planteados por usuarios que no saben cuál usar)?

Habiendo dicho eso, no estoy en contra de que pip simplemente construya en su lugar. Creo que la razón por la que no lo hacemos es porque tuvimos casos en los que los artefactos que quedaron de una compilación anterior se usaron en una compilación posterior, pero se compilaron con diferentes opciones (por ejemplo, archivos de objetos compilados en modo de depuración vinculados a una compilación de lanzamiento hecho desde el mismo árbol). Sin embargo, es razonable decir que los backends deben garantizar que problemas como este no sucedan, y si lo hacen, es un error de backend y no está a la altura para intentar defenderse de él. Sin embargo, no estoy seguro de si adoptar tal postura es particularmente fácil de usar; esto surgió durante las discusiones de PEP 517 bajo el tema de compilaciones incrementales, y fue lo suficientemente controvertido como para diferirlo en ese momento.

Mi preferencia ha sido construir una sdist, y luego construir la rueda a partir de eso, desde hace mucho tiempo (su opción 1). Estoy de acuerdo con la construcción de pip en su lugar (si podemos estar de acuerdo en que hacerlo es seguro, dado que, como señalé anteriormente, no podemos esperar que el back-end nos lo haga saber), pero creo que se necesitaría una comunidad. discusión para analizar las implicaciones más amplias (en backends, pip / frontends y usuarios finales).

1 Por supuesto, sería posible actualizar PEP 517 para definir lo que constituye un árbol de fuentes "en el lugar" en lugar de una compilación "fuera del árbol", pero sospecho que sería un concepto muy difícil de precisar.

Agregué las soluciones 4. (Cambiar pip para construir siempre en su lugar) y 5. (Cambiar pip para construir en su lugar por defecto con una opción para construir fuera del árbol).

Tengo sentimientos encontrados con respecto a la construcción a través de sdist (solución 1) por las siguientes razones:

  1. algunos proyectos pueden tener un camino roto entre el disco y la rueda; Si bien veo el valor de validar que la construcción desde sdists funciona, hacerlo de forma predeterminada ahora ciertamente romperá muchas compilaciones de usuarios finales
  2. todavía tiene implicaciones de rendimiento para proyectos con grandes sdists, y debido a llamadas de subproceso adicionales
  3. Personalmente, creo que no deberíamos poner demasiado énfasis en los sdists, ya que es bastante común que el proyecto publique artefactos construidos y se refiera a su plataforma de alojamiento de código favorita para sus versiones de origen (por ejemplo, backends que dependen de la presencia de un checkout de VCS para edificio necesita saltar a través de aros para producir una lista de trabajo). Y PEP 517 permite específicamente que los backends recauden UnsupportedOperation por build_sdist .

Esta publicación sobre discutir también resume argumentos similares.

Estoy de acuerdo en que el camino hacia la solución 3. está lejos de ser obvio.

También estoy de acuerdo en que deberíamos evitar opciones adicionales si podemos.

También observo que hubo algunas voces a favor de las compilaciones en el lugar en el hilo de discusión vinculado, pero de hecho es necesaria una discusión comunitaria enfocada en ese tema específico si queremos explorar ese enfoque.

  • Construya una sdist en su lugar, desempaquete la sdist en una ubicación temporal y luego construya a partir de eso.

Creo que este es un buen enfoque.

En mi opinión, lo más probable es que esto se ejecute para un solo paquete en la mayoría de las ejecuciones pip install involucren directorios locales; más comúnmente imagino pip install . . Probablemente, esto se haría como parte del flujo de trabajo de desarrollo del paquete.

Esto es lo que creo que debería ser este comportamiento:

  • Si el backend no puede crear un sdist, haga local-dir -> wheel (en el lugar)

    • Creo que la responsabilidad recae principalmente en el backend para asegurarse de que local-dir -> wheel sea una operación idempotente en este caso.

  • Si el backend es capaz de crear un sdist, haga local-dir -> sdist -> unpacked-sdist -> wheel.

Haciendo local-dir -> sdist -> wheel, tenemos un conjunto adicional de llamadas. Sin embargo, creo que es razonable validar que los sdists generados están cuerdos, especialmente durante el desarrollo. tox ya hace esto como parte de su flujo de trabajo, verifique el manifiesto para cubrir las interfaces no tan amigables de setuptools aquí.

En general, creo que los costos de construir un sdist cuando se le da un directorio local valen la pena para evitar tales errores en los proyectos, especialmente porque las personas que instalan desde directorios locales probablemente sean los desarrolladores del proyecto.

Con respecto al lanzamiento, creo que nos gustaría esperar y ver cómo aparece el # 6536. Definitivamente aprenderíamos algunas cosas que se pueden transferir.

Prefiero construir / instalar en el lugar sin sdist (entonces setup.py install o setup.py bdist_wheel o llamar a build_wheel en el backend PEP 517 según corresponda) en lugar de construir un sdist, desempaquetar e instalar a partir de ese. Mis razones específicas son:

  1. Admite la mayor cantidad de usuarios listos para usar.

    1. Suponga que pip hace local-dir -> instalado (a través de la construcción de la rueda en el lugar o setup.py install ). Los usuarios que quieran ir desde local-dir -> instalado pueden ejecutar pip install local-dir . Los usuarios que quieran ir desde local-dir -> sdist -> installed son libres de crear una sdist y luego ejecutar pip install ./path/to/sdist . Hay muchas herramientas alternativas que pueden construir una sdist, y esto es algo que los usuarios probablemente ya tengan a menos que estén creando manualmente las distribuciones que cargan en PyPI.

    2. Ahora suponga que pip hace local-dir -> sdist -> instalado. Los usuarios que quieran ir desde local-dir -> instalado no tienen opciones que involucren pip. Los usuarios que quieran ir desde local-dir -> sdist -> instalado pueden ejecutar pip install local-dir . Los usuarios sin opciones le pedirán a pip opciones para controlar el comportamiento o tendrán que encontrar otra herramienta que de otra manera no hubieran necesitado.

  2. Si implementamos local-dir -> sdist -> installed, presumiblemente también lo haríamos para los requisitos basados ​​en VCS. Si es así, es más trabajo. De lo contrario, son rutas adicionales a través del código y desviaciones en la forma en que se maneja la instalación que los usuarios deben recordar o al brindar soporte.
  3. Mínima cantidad de trabajo para implementar. Hay tres lugares para cambiar para implementar local-dir -> installed ( aquí , aquí y aquí ). Para local-dir -> sdist -> instalado, ni siquiera quisiera tocar la implementación de esto hasta que # 6607 esté listo, de lo contrario, creo que terminaría en muchos lugares en la base de código similar a la descarga de artefactos / verificación de hash.
  4. La menor cantidad de trabajo para probar ya que, en mi opinión, las pruebas existentes son suficientes para cubrir la ruta del código local-dir -> instalado. Para la ruta local-dir -> sdist -> instalado, querríamos verificar que en realidad estamos construyendo una sdist, y que las alternativas funcionan para construir una rueda directamente.
  5. Mínima cantidad de trabajo (computacionalmente). Como se mencionó en otra parte, local-dir -> sdist -> installed es una llamada de subproceso adicional (y ese subproceso está funcionando). También significa que pip tiene que descomprimir el sdist (hola, escáneres de virus y otros discos lentos) antes de hacer la construcción de la rueda.

Independientemente del enfoque, el único problema que veo al hacer las cosas en el lugar es que para las compilaciones de setuptools (creo que esto se aplica para legacy y PEP 517) terminaríamos con .egg-info en el directorio del proyecto que se equivocará como un "paquete instalado" cuando se invoca pip con python -m pip en ese directorio. Esto sería arreglado por # 4575 que presumiblemente NO incluiría el directorio actual en la consulta de paquetes instalados para ningún esquema.

Observando que he llegado a estar de acuerdo en que la idea de omitir la compilación sdist y hacer directamente una compilación en el árbol es un mejor enfoque para que pip lo tome por defecto, y no intentar hacer local-dir -> sdist -> wheel.

En Fedora, cuando construimos paquetes Python RPM, somos dinosaurios y la forma estándar de hacerlo es usar python setup.py build . Con PEP 517 agregamos una forma "provisional" de usar pip wheel lugar. Sin embargo, con los módulos de extensión tenemos un problema con el enfoque de "mover fuentes a tmp, construir desde allí" que usa pip para construirlas.

Nuestra maquinaria de compilación está inyectando algunos indicadores del compilador para que los artefactos de compilación (módulos de extensión .so en este caso) contengan metadatos sobre sus fuentes. Más tarde hay un script de shell que atraviesa los artefactos de compilación, extrae esta información y copia las fuentes en /usr/src/debug para ser instaladas a través de un *-debugsource RPM especial. La mahcinery espera que todo se construya dentro del árbol de trabajo y realmente no funciona bien cuando se construye afuera. Estas son las cosas que se pueden hacer (juntas) para mitigar el problema de nuestro lado:

  1. configure la variable de entorno $TMPDIR para tenerla dentro del lugar donde la secuencia de comandos RPM la espera (es decir, export TMPDIR=%{_builddir}/.tmp (y créelo))
  2. use pip wheel con la opción --no-clean para mantener las fuentes copiadas en $TMPDIR
  3. ejecute un poco de shell kung fu para reescribir la información de "cuál es mi fuente" en la ubicación correcta: find %{buildroot} -iname '*.so' -print0 | xargs --no-run-if-empty -0 -n1 /usr/lib/rpm/debugedit -b "%{_builddir}/.tmp/pip-req-build-"* -d "$PWD"
  4. (Opcional: limpie $TMPDIR manualmente).

No nos gusta especialmente el tercer paso porque se basa en demasiados detalles de implementación:

  • /usr/lib/rpm/debugedit API y ubicación (y existencia)
  • el pip-req-build nombre
  • el mecanismo con el que pip construye las fuentes

Si pip siempre se construyera en su lugar o si hubiera un cambio de línea de comando para esto, el problema desaparecería.

Informe posterior: https://bugzilla.redhat.com/show_bug.cgi?id=1806625

Observando que he llegado a estar de acuerdo en que la idea de omitir la compilación sdist y hacer directamente una compilación en el árbol es un mejor enfoque para que pip lo tome por defecto, y no intentar hacer local-dir -> sdist -> wheel.

También me inclino más a aceptar la idea de hacer una construcción de ruedas en el lugar. Mis reservas restantes son:

  1. Confiaríamos en que el backend se comporte "correctamente", por ejemplo, que no dé resultados diferentes basados ​​en datos sobrantes de compilaciones anteriores, o lo que sea. Estoy de acuerdo con hacer esa suposición, pero me preocupa el costo de soporte si comenzamos a hacer que la gente diga "pip construyó mi rueda incorrectamente" y tenemos que depurar solo para descubrir que es un problema de backend.
  2. Creo que cualquiera que
  3. Como corolario de lo anterior, debemos asegurarnos de que no hay proyectos que sepamos que dependan de nuestro enfoque actual de "copiar y compilar", ya que si los hubiera, los romperemos con este cambio.

... y, por supuesto, alguien tendrá que escribir un PR implementando este cambio (con pruebas, documentos, etc., lo habitual) de lo contrario, todo lo que estamos haciendo es hablar 🙂

Confiaríamos en que el backend se comporte "correctamente", por ejemplo, que no dé resultados diferentes basados ​​en datos sobrantes de compilaciones anteriores, o lo que sea. Estoy de acuerdo con hacer esa suposición, pero me preocupa el costo de soporte si comenzamos a hacer que la gente diga "pip construyó mi rueda incorrectamente" y tenemos que depurar solo para descubrir que es un problema de backend.

¿Tendría sentido ampliar la interfaz PEP 517 para incluir un gancho "limpio"? Probablemente lo querríamos como un punto de todos modos para permitir otros esfuerzos (por ejemplo, implementar la instalación editable, construir una herramienta de desarrollo de paquetes que construya cualquier proyecto PEP 517). pip puede llamarlo aquí para asegurarse de que no haya basura sobrante antes de realizar la compilación en el árbol.

¿Tendría sentido ampliar la interfaz PEP 517 para incluir un gancho "limpio"?

¿Quizás? Pero si pip llama automáticamente a clean , es probable que haya alguien que quiera no hacerlo, para hacer compilaciones incrementales o algo así. Y luego terminamos con otra opción.

Mi inclinación es mantenerme en mi posición "debemos ser capaces de asumir que es responsabilidad del backend garantizar que las compilaciones in situ funcionen correctamente". Incluso si eso resulta insostenible, obtener ejemplos concretos de por qué no funciona nos ayudará a comprender mejor qué hacer con el problema, en lugar de simplemente adivinar.

  1. Confiaríamos en que el backend se comporte "correctamente", por ejemplo, que no dé resultados diferentes basados ​​en datos sobrantes de compilaciones anteriores, o lo que sea.

Me sentiría tentado a ampliar el PEP-517 para convertirlo en un requisito explícito.

Me sentiría tentado a ampliar el PEP-517 para convertirlo en un requisito explícito.

Ya dice esto:

El backend puede almacenar artefactos intermedios en ubicaciones de caché o directorios temporales. La presencia o ausencia de cualquier caché no debería marcar una diferencia sustancial en el resultado final de la construcción.

No es tanto que es probable que los backends violen este requisito deliberadamente, ya que los usuarios, naturalmente, informarán el problema como un problema de pip y serán redirigidos al proyecto de backend, que es un poco de sobrecarga adicional.

Al intentar encontrar una solución para Fedora, nos ha golpeado https://github.com/pypa/pip/issues/7872

Ya que hemos resuelto el problema de ".egg-info en cwd" con # 7731 y amigos, eso es una cosa menos de la que preocuparse cuando se construye en su lugar.

Entonces, la opción 4 (siempre construir en el lugar) se implementó en # 7882.

Ahora hemos publicado (según # 7951) una versión beta de pip, pip 20.1b1. Esta versión incluye # 7882, que implementó una solución para este problema.

Espero que los participantes en este número nos ayuden probando la versión beta y buscando nuevos errores. Nos gustaría identificar y solucionar cualquier problema potencial antes del lanzamiento principal de 20.1 el martes.

También agradezco la retroalimentación positiva como "¡Sí, funciona mejor ahora!" también, dado que el rastreador de problemas suele estar lleno de "problemas". :)

Planeamos probarlo en Fedora (ya lo habíamos planeado antes de su comentario), sin embargo, la fecha límite del martes probablemente no sea realista.

@hroncok ¿ Alguna idea de cuándo Fedora podría probar estos cambios?

Haré todo lo posible para hacerlo de alguna manera el lunes, sin embargo, no puedo hacer ninguna promesa.

De hecho, 20.1b1 hace que nuestros problemas desaparezcan.

Más comentarios generales sobre 20.1b1 en https://mail.python.org/archives/list/[email protected]/message/5EAUIYYIRKXEHTAG5GQ7EJHSXGZIW2F7/

¡Viva! ¡Muchas gracias por probar la beta @hroncok! ¡Muy apreciado! ^> ^

Un resultado de la construcción en el lugar: había estado construyendo ruedas para múltiples versiones de python en paralelo (dentro de un contenedor docker manylinux). Con las compilaciones en el lugar, las compilaciones paralelas no funcionan porque las diferentes versiones entran en conflicto. Con las compilaciones fuera del árbol, cada versión hizo un árbol separado y no tuvo ningún problema.

@manthey esa discusión está bajo # 8168

Así que ya han pasado más de 10 días. Hubo algunos problemas planteados sobre el cambio (todos esperados diría: # 8165, # 8168, # 8196). También hubo personas que mencionaron explícitamente que el cambio los está ayudando.

  • Además de los problemas de rendimiento, el comportamiento anterior (copiar a directorio temporal) tenía problemas de corrección (vinculados arriba) que son imposibles de solucionar por pip sin el conocimiento del contexto que solo tiene la persona que llama (y, como nota al margen, ese código de árbol de copia ya estaba lleno de curitas para hacer frente a situaciones extrañas (tmpdir en árbol, enchufes, etc.).
  • Una opción para activar el comportamiento anterior aún tendría problemas de corrección y rendimiento.
  • Una solución correcta implicará compilar soporte de back-end para controlar el directorio de compilación que no existe completamente hoy (por ejemplo, setuptools bdist_wheel tiene --bdist-dir , pero aún escribe .egg-info en lugar, consulte también https://github.com/pypa/setuptools/issues/1816, https://github.com/pypa/setuptools/issues/1825). Entonces, ahora que pip se comporta correctamente, tal vez la discusión pueda cambiar para ver si, por ejemplo, setuptools puede desarrollar una opción para hacer una compilación sin tocar el directorio fuente y luego ver si se necesita un cambio de PEP 517 o no para controlar esa opción.
  • Mientras tanto, las personas que llaman pueden solucionar los problemas informados con relativa facilidad (por ejemplo, copiarse a sí mismos en un directorio temporal o crear un tarball temporal, lo que pueden hacer correctamente con pleno conocimiento del contexto).
  • Por último, es difícil decirlo con certeza con los datos que tenemos, pero mi intuición es que este cambio ayuda a más personas de las que duele.

Odio romper los cambios, pero este no se hizo a la ligera y, por estas razones, personalmente me inclino a mantenerlo.

No estoy de acuerdo en que el nuevo comportamiento sea que pip se comporte "correctamente", de manera diferente con seguridad, y aparentemente en algunos casos de manera diferente, y creo que enmarcarlo como tal es incorrecto. Representa una compensación para un conjunto de usuarios defectuosos por un conjunto diferente, en ambos casos aquí había soluciones que se podían hacer.

No habría fusionado este cambio y me habría perdido que sucediera o habría argumentado en contra (y creo que ahora, habiéndolo fusionado, hace que algunos usen pip como una función forzada para ayudar a prevenir ciertos tipos de paquetes rotos significativamente más difíciles ). Dicho esto, no sé realmente si revertir es lo correcto aquí. Puede resultar aún más confuso para los usuarios si el comportamiento cambia mucho. Si vamos a revertir, deberíamos hacerlo rápidamente, si no, entonces el comportamiento actual probablemente debería ser para bien o para mal.

Usé el término "correctamente", porque antes pip wheel <localdir> y pip install <localdir> generaban una rueda diferente a cd <localdir> ; setup.py bdist_wheel en algunos casos: con diferentes archivos faltantes en presencia de enlaces simbólicos (# 3500), o una versión diferente con setuptools_scm (# 7549), o https://github.com/pypa/pip/issues/7555#issuecomment -595180864, ​​o # 6276, o errores simples. No creo que pip 20.1 genere ruedas / instalaciones tan malas, así que en ese sentido creo que es más correcto.

Por supuesto, sabíamos que el cambio interrumpiría algunos flujos de trabajo y la compensación debe reevaluarse ahora que tenemos comentarios y revertirse o confirmarse para siempre.

Y aún puede producir ruedas diferentes a setup.py sdist && pip install dist/*.tar.gz .

Mi sugerencia sería revertir el PR e implementar la solución al mezclar primero una sdist y luego construir una rueda a partir de la sdist resultante.

Esto debería resolver todos los problemas de corrección, excepto en los casos en que el proyecto sea incapaz de construir correctamente una sdist y, en mi opinión, no es un caso de uso importante que resolver.

La compensación es que será más lento. Sin embargo, una vez que lo tengamos implementado, podemos refinar aún más la interfaz PEP 517 para agregar API opcionales que permitan aceleraciones. Es probable que nunca sea tan rápido como lo hace este cambio, pero ciertamente podemos acercarnos a él.

Este cambio, tal como está, hace que sea efectivamente imposible seguir avanzando en la corrección sin introducir regresiones de rendimiento con las que es poco probable que los usuarios estén contentos. Sin embargo, si lo corregimos y luego lo mejoramos con el rendimiento, podemos llegar a un término medio feliz que satisfaga a ambas partes.

Como dice el viejo adagio, primero hazlo correcto, luego hazlo rápido. Me temo que con este PR lo hemos hecho rápido y bloqueado nuestra capacidad para hacerlo correcto.

Todavía estoy de acuerdo en que es deseable validar sdists, pero no en el momento de la instalación de pip. Tal vez esa sea una característica de una futura herramienta de construcción de sdist o un comando de compilación de pip.

Además, setup.py sdist crea .egg-info en el directorio local, por lo que los problemas informados con el directorio de origen de solo lectura o las compilaciones simultáneas se mantendrían.

Si no sucede en el momento de la instalación de pip, funcionalmente no sucede hasta que el momento de la instalación de pip de otra persona. Omitirlo solo significa que tenemos múltiples rutas en las que un proyecto puede pasar para pasar de VCS al paquete instalado y cada ruta es otra oportunidad para las diferencias. Esto no es algo nuevo, básicamente todas las opciones que tenemos que cambian la ruta de instalación terminan con un conjunto diferente de bytes en el disco, incluso para los proyectos más rigurosos. Las diferencias sutiles siempre existen y solo espera que esas diferencias no sean significativas, o puede hacer lo que pueda para eliminar esas diferencias al hacer que estructuralmente sea imposible tenerlas desde el principio.

De hecho, algunos problemas de rendimiento podrían reaparecer si / al compilar a través de sdist, pero probablemente serían un orden de magnitud más bajo que lo que teníamos en pip <20.1. De hecho, la mayor parte a menudo surgió al copiar .git , o un venv , u otras cosas voluminosas no relacionadas que no estarían en la lista sdist.

Independientemente de lo que eventualmente termine pip , ¿podríamos hacer del otro una opción, ya que es poco probable que cualquiera de ellos pueda satisfacer a todos? Me imagino que si se va a mantener el enfoque actual (realmente no tengo una opinión sobre cuál debería ser el predeterminado), deberíamos poder proporcionar una reserva de último resultado donde un usuario puede elegir crear una sdist e instalar el paquete de allí.

Además, setup.py sdist crea .egg-info en el directorio local, por lo que los problemas informados con el directorio de origen de solo lectura o las compilaciones simultáneas se mantendrían.

Creo (al menos una prueba rápida está de acuerdo conmigo) que solo setuptools (no distutils ) lo hace, y este comportamiento se puede configurar para crear el directorio en otro lugar. Al igual que con otros backends, deberíamos poder recomendarlos para hacer una generación de sdist limpia .

FWIW, no creo que necesitemos volcar el directorio sdist-generation --egg-info en el directorio de trabajo, si bajamos el enfoque generate-sdist-unpack-it-build-wheel, ya que podemos volcar eso en un directorio temporal, como lo hacemos con generate_metadata .

@pradyunsg, ¿eso no requiere un cambio en las herramientas de configuración? La última vez que verifiqué el comando sdist no tenía una opción para especificar la ubicación base .egg-info , al contrario de egg_info que tiene una opción --egg-base que aprovechamos en # 7978.

¡En efecto! Estaba viendo el archivo incorrecto en setuptools. 🙈 Me quedo corregido.

¿Por qué todo es tan complejo en este espacio? :(

$  ls -la
total 8
drwxr-xr-x  3 dstufft  staff   96 May  6 14:26 .
drwxr-xr-x  9 dstufft  staff  288 Apr 28 15:46 ..
-rw-r--r--  1 dstufft  staff   85 Apr 23 16:23 setup.py

$  py setup.py egg_info --egg-base /tmp/foo sdist
/Users/dstufft/.pyenv/versions/3.8.2/lib/python3.8/site-packages/setuptools/dist.py:471: UserWarning: Normalizing '2020.04.23.3' to '2020.4.23.3'
  warnings.warn(
running egg_info
creating /tmp/foo/dstufft.testpkg.egg-info
writing /tmp/foo/dstufft.testpkg.egg-info/PKG-INFO
writing dependency_links to /tmp/foo/dstufft.testpkg.egg-info/dependency_links.txt
writing top-level names to /tmp/foo/dstufft.testpkg.egg-info/top_level.txt
writing manifest file '/tmp/foo/dstufft.testpkg.egg-info/SOURCES.txt'
reading manifest file '/tmp/foo/dstufft.testpkg.egg-info/SOURCES.txt'
writing manifest file '/tmp/foo/dstufft.testpkg.egg-info/SOURCES.txt'
running sdist
warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md

running check
warning: Check: missing required meta-data: url

warning: Check: missing meta-data: either (author and author_email) or (maintainer and maintainer_email) must be supplied

creating dstufft.testpkg-2020.4.23.3
copying files to dstufft.testpkg-2020.4.23.3...
copying setup.py -> dstufft.testpkg-2020.4.23.3
Writing dstufft.testpkg-2020.4.23.3/setup.cfg
creating dist
Creating tar archive
removing 'dstufft.testpkg-2020.4.23.3' (and everything under it)

$ ls -la                                        
total 8
drwxr-xr-x  4 dstufft  staff  128 May  6 14:28 .
drwxr-xr-x  9 dstufft  staff  288 Apr 28 15:46 ..
drwxr-xr-x  3 dstufft  staff   96 May  6 14:28 dist
-rw-r--r--  1 dstufft  staff   85 Apr 23 16:23 setup.py

https://github.com/pypa/pip/issues/8165#issuecomment -624669107 esto se siente como un bonito error que detiene el espectáculo, podría decirse que no es nuestro error, pero es un tipo de error que, según yo, sucedería durante la discusión de PEP 517 cuando hacer construcciones en el lugar de forma predeterminada surgió.

bdist_wheel Se le pidió a

Si fuera SCons, recordaría los archivos que le importaban y omitiría archivos adicionales en el directorio build / de la rueda, incluso si estuvieran presentes en el sistema de archivos.

Creo que el problema anterior no solo afecta a manylinux. Debería suceder siempre que el directorio de compilación no sea lo suficientemente específico para capturar la ABI (en el caso de setuptools, parece que la plataforma y la versión de Python es todo lo que se captura en la etiqueta ABI en el directorio de compilación). Creo que esto se extiende más allá de ABI con el intérprete actual también, si algo se vincula con NumPy, por ejemplo, creo que tiene un ABI que funcionará en NumPy más nuevo, pero no más antiguo, y a menos que lo codifiquen en el nombre del directorio de compilación, entonces esto funcionará el efecto también se usa así.

La limpieza automática del directorio de compilación no resuelve el problema, solo lo hace menos probable (por ejemplo, ejecutar dos invocaciones pip wheel en paralelo aún podría desencadenar el problema), además de una de las supuestas razones para implementar de esta manera (al menos durante la discusión de PEP 517) fue que esto proporcionaría más rendimiento al permitir el almacenamiento en caché entre invocaciones para compilaciones incrementales. IOW, el comportamiento actual es lo que algún subconjunto quería, reutilizando artefactos de compilación entre ejecuciones, da la casualidad de que, con mucho, el backend de compilación más común lo hace muy mal (y posiblemente en algunos casos no tiene suficiente información para hacerlo bien sin per personalización del paquete).

Por supuesto, con suficientes indicadores para el comando setuptools subyacente, puede remediar esto (algo como py setup.py egg_info --egg-base /tmp/foo build --build-base /tmp/foo/build-base bdist_wheel --bdist-dir /tmp/foo/bdist lo haría).

Sin embargo, reiteraría que el problema no son archivos adicionales, es que el ABI esperado con el que la rueda era compatible cambió, y el .so no se reconstruyó. Si SCons es lo suficientemente inteligente como para saber que Python compilado con pymalloc necesita un directorio de compilación y Python compilado con otro (incluidas cosas como las versiones de NumPy a las que se puede vincular .so ), entonces no se verá afectado. Si reutilizara un artefacto construido previamente con un ABI diferente, se verá afectado.

Intenté probar enscons pero no pude hacer que rsalette construyera sin errores.

Intenté probar scikit-build para ver cómo manejaba las compilaciones incrementales, y no importa lo que hice, solo vomitó sobre sí mismo en la segunda compilación y tuve que eliminar manualmente el directorio _skbuild cada vez para conseguirlo. ejecutar sin error.

Frio. Lo sentimos, por desgracia, enscons se ha actualizado y rsalette no.

El miércoles 6 de mayo de 2020 a las 4:18 p.m., Donald Stufft escribió:

Intenté probar enscons pero no pude hacer que rsalette construyera sin errores.

Intenté probar scikit-build para ver cómo manejaba las compilaciones incrementales, y no importa lo que hice, solo vomitó sobre sí mismo en la segunda compilación y tuve que eliminar manualmente el directorio _skbuild cada vez para conseguirlo. ejecutar sin error.

-
Estás recibiendo esto porque hiciste un comentario.
Responda a este correo electrónico directamente, véalo en GitHub https://github.com/pypa/pip/issues/7555#issuecomment-624867490 , o cancele la suscripción https://github.com/notifications/unsubscribe-auth/AABSZERIEDAPUIXCPAKBBUDRQHAXRANCNFSM4KCV5MHQ .

Lo sentimos, por desgracia, enscons se ha actualizado y rsalette no.

¿Existe un buen C ext que use enscons que esté actualizado? Elegí rsalette porque era el primero en la lista y no tenía ganas de depurarlo, feliz de probar con otra cosa.

El único problema con rsalette es que no debería pasar ROOT_IS_PURELIB al entorno en SConstruct. No tiene extensión C. cryptacular ve bien.

# 8165 (comentario)

Con esto, creo que estoy de acuerdo en que deberíamos revertir este cambio.

Creo que los nuevos problemas son mucho mayores de lo previsto. ¿Quizás un rápido 20.1.1 para revertir y luego podamos tener una discusión más larga sobre cómo resolver los problemas de compilaciones dentro y fuera del árbol?

También voto por revertir y seguir https://discuss.python.org/t/proposal-adding-a-persistent-cache-directory-to-pep-517-hooks/2303/15 como solución para esto (eso permitir que los backends no se construyan en el lugar, por lo que no exponga tales problemas). Participa en ese hilo también si estás de acuerdo con la sugerencia allí.

Eso también me parece sensato. Creo que un enfoque in-tree (o build-from-sdist) tiene algunos beneficios extremadamente significativos (estoy bastante seguro de que recibiremos aullidos de queja de parte de la base de usuarios cuando revertimos 🙂) pero las desventajas también son significativo.

No tengo claro cuál debería ser la interfaz de usuario aquí (¿cuál es el enfoque predeterminado? ¿Qué tipo de opciones deberíamos tener?), Pero creo que deberíamos tomarnos un poco más de tiempo para decidir eso, en lugar de tomar decisiones mientras combatimos los problemas actuales. .

¡Muy bien! Creo que el consenso general es revertir y reevaluar. Presentaré un PR para esto. :)

Participa en ese hilo también si estás de acuerdo con la sugerencia allí.

Por favor, hágalo. He estado comentando, pero he llegado al punto en el que no sé lo suficiente para ofrecer sugerencias significativas, por lo que las aportaciones de personas con más experiencia serían valiosas.

Acabo de soltar un par de grandes manchas de texto en https://github.com/pypa/pip/issues/8165#issuecomment -625401463. Voy a alejarme por hoy ... Terminé sintiéndome un poco frustrado mientras escribía las notas personales al final. Terminar con el número 5599 y leer los comentarios negativos de los usuarios ciertamente no ayudó.

Hola amigos, pensé un poco más en esto, aquí está mi punto de vista actual sobre este asunto.

  1. Construya una sdist en su lugar, desempaquete la sdist en una ubicación temporal y luego construya a partir de eso.

Sigo creyendo que pip install / pip wheel no es el lugar adecuado para intentar atrapar sdists malos. ¿No debería ser una responsabilidad de fondo no crear malos sdists en primer lugar? Además, creo que la compilación incondicional a través de sdist es probablemente tan disruptiva como la compilación en el lugar.

  1. Agregue una opción de pip para construir en su lugar.

El que más me gusta a corto plazo, ya que la solución 4 no lo logró. ¿Es prematuro agregar eso en el pip 20.1.1?

  1. Actualice PEP 517 con algún tipo de mecanismo para permitir que los back-end se comuniquen con los front-end si son "seguros" para las compilaciones in situ.

Con este pip todavía necesitaría volver a su copytree roto y no reparable, así que no estoy a favor de este.

  1. Cambie el pip para construirlo siempre en su lugar.

Así que este se considera demasiado perturbador y lo revertiremos en 20.1.1.

  1. Cambie pip para construir en su lugar de forma predeterminada con una opción para construir fuera del árbol.

Ese podría ser el objetivo a largo plazo, ¿la opción de construir una combinación fuera del árbol con el concepto de directorio de caché?

Realmente no me gustan las opciones de CLI, particularmente las como esta. ¿Qué sucede si enumero dos paquetes que están en mi FS local y necesito construir uno en su lugar y otro no? Si brindamos una opción para hacer uno u otro, terminarán existiendo paquetes que solo se pueden construir con uno u otro.

También me huele como el tipo de opción que existe por completo porque un proyecto no pudo tomar una decisión y decidió simplemente trasladar esa decisión al usuario final.

Construir a través de sdist no se trata exactamente de detectar malos sdists. En gran parte de lo que se trata es de reducir las posibles variaciones de "ruta" que puede atravesar un proyecto antes de acabar instalado. Agregar una bandera de alguna manera hace que el problema empeore, no mejore.

Por lo que quiero decir, tenemos algunas "rutas" por las que pueden pasar las instalaciones:

  1. VCS -> Sdist -> Rueda -> Instalado
  2. VCS -> Rueda -> Instalado
  3. VCS -> instalado (heredado)

Hay algunos caminos adicionales que están siendo eliminados, pero en general esos son nuestros 3 (e idealmente 3 también se eliminan). También hay instalaciones editables, pero no desaparecerán pronto.

Podemos considerar que un sdist o una rueda se cargan en PyPI y se instalan a partir de ahí para que formen parte de la misma "ruta", simplemente lo pausas y lo terminas en otra computadora.

El problema de tener múltiples "rutas" como esta es que introduce inconsistencias en la instalación final resultante. No siempre ocurren, pero es un caso fácilmente observable que sucede con frecuencia. A menudo, estas inconsistencias no son un gran problema, pero a veces lo son.

Si estamos haciendo compilaciones en el lugar como esta, entonces efectivamente estamos diciendo que nunca podremos colapsar en una sola ruta, y siempre tendremos que lidiar con este extraño caso límite en el que a veces las personas obtendrán resultados diferentes en función de cómo se realizó la instalación.

Como beneficio adicional, esto también puede actuar como una función de fuerza para ayudar a garantizar que el camino feliz se mantenga feliz.

Principalmente estoy de acuerdo con @dstufft , y en particular estoy de acuerdo en que el enfoque build-from-sdist no debe verse como "intentar validar sdists" sino como "todo sigue el" árbol de fuentes -> sdist -> wheel -> install route (solo algunas cosas omiten algunos pasos iniciales) ".

Sin embargo, quiero retomar un punto:

¿Qué sucede si enumero dos paquetes que están en mi FS local y necesito construir uno en su lugar y otro no?

¿¡¿Simplemente ejecute los dos paquetes en dos ejecuciones separadas de pip con diferentes opciones?!? Sé que es posible que uno sea una dependencia del otro y su punto en general se mantiene, pero parece haber una inclinación general para que la gente asuma que cada escenario de instalación debe colapsarse en una sola ejecución de pip, y yo no ' No creo que sea razonable (hemos tenido soluciones perfectamente buenas para los problemas que son rechazados por el usuario porque "significa que tendría que dividir mi lista de requisitos en dos").

Tenga en cuenta que cuando (si) revertimos, necesitaremos reabrir problemas como el # 6276 que se cerró como resultado de la implementación de compilaciones en el árbol.

Parte del problema es que pip no considera lo que ya está instalado al resolver las dependencias (no estoy seguro de si el nuevo trabajo de resolución cambia eso), por lo que debe tener todo contenido dentro de una sola invocación de pip si desea que se resuelva. dependencias "correctamente" (en la medida en que nuestro solucionador actual haga algo correctamente).

Si el nuevo solucionador tiene en cuenta lo que ya está instalado, entonces pip install foo bar y pip install foo && pip install bar serían aproximadamente iguales y no importarían en absoluto, pero si no lo hace (y lo mismo es más o menos cierto ahora) si ambos proyectos dependían de "spam" pero foo requería <2 y bar required> 1, obtendríamos una instalación no válida.

Aunque eso es una tangente :)

(¿No estoy seguro de si el nuevo trabajo de resolución cambia eso?)

Las entradas son bienvenidas en # 7744. :)

  1. Cambie el pip para construirlo siempre en su lugar.

Así que este se considera demasiado perturbador y lo revertiremos en 20.1.1.

Para ser claros, también es que "lo implementamos demasiado rápido" y el enfoque de implementación que tomamos es definitivamente parte de por qué esto terminó siendo demasiado perturbador.

  1. Agregue una opción de pip para construir en su lugar.

@dstufft @pfmoore Veo ese tipo de opción como un mecanismo de https://github.com/pypa/pip/issues/8165#issuecomment -625501216

Presentaré un PR para esto. :)

8221 lo es.

20.1.1 se ha publicado, que contiene los cambios revertidos.

En Fedora, cuando construimos paquetes Python RPM, somos dinosaurios y la forma estándar de hacerlo es usar python setup.py build . Con PEP 517 agregamos una forma "provisional" de usar pip wheel lugar. Sin embargo, con los módulos de extensión tenemos un problema con el enfoque de "mover fuentes a tmp, construir desde allí" que usa pip para construirlas.

Nuestra maquinaria de compilación está inyectando algunos indicadores del compilador para que los artefactos de compilación (módulos de extensión .so en este caso) contengan metadatos sobre sus fuentes. Más tarde hay un script de shell que atraviesa los artefactos de compilación, extrae esta información y copia las fuentes en /usr/src/debug para ser instaladas a través de un *-debugsource RPM especial. La mahcinery espera que todo se construya dentro del árbol de trabajo y realmente no funciona bien cuando se construye afuera. Estas son las cosas que se pueden hacer (juntas) para mitigar el problema de nuestro lado:

1. set the `$TMPDIR` environment variable to have it within the place where the RPM script expects it (i.e. `export TMPDIR=%{_builddir}/.tmp` (and create it))

2. use `pip wheel` with the `--no-clean` option to keep the copied sources in `$TMPDIR`

3. run some shell kung fu to rewrite the "what is my source" information to the correct location: `find %{buildroot} -iname '*.so' -print0 | xargs --no-run-if-empty -0 -n1 /usr/lib/rpm/debugedit -b "%{_builddir}/.tmp/pip-req-build-"* -d "$PWD"`

4. (Optional: clean `$TMPDIR` manually.)

Este es efectivamente el camino que comencé cuando busqué la integración de pip, # 6505, etc.

Las compilaciones iterativas con pip se rompen efectivamente hoy, lo cual es una pérdida importante para los grupos que tienen una gran cantidad de código Python en forma de extensión C, por lo que recurrí a invocar la compilación con setup.py .

pip necesita un comando build y el resultado final del comando build debe ser aceptable junto con otros subcomandos, como wheel , install etc.

En este momento, pip trata efectivamente install como build y install , que no es lo que quieren algunas personas que están construyendo y almacenando en caché artefactos binarios, instalando binarios sobre la lectura -sólo soportes, etc.

Realmente desearía que hubiera una forma de usar setup.py para construir los binarios, luego pip install sin tener que recurrir a crear un bdist , pero eso no parece ser posible hoy , ya que pip y distutils / setuptools no se ponen de acuerdo sobre dónde encontrar los artefactos binarios.

Realmente desearía que hubiera una forma de usar setup.py para construir los binarios, luego pip instalarlos sin recurrir a crear un bdist, pero eso no parece ser posible hoy, ya que pip y distutils / setuptools no están de acuerdo dónde encontrar los artefactos binarios.

No estoy seguro de seguirlo: estás diciendo que quieres una forma de usar binarios pero no quieres usar los formatos de distribución binarios que ya existen. ¿Porqué es eso?

Realmente desearía que hubiera una forma de usar setup.py para construir los binarios, luego pip instalarlos sin recurrir a crear un bdist, pero eso no parece ser posible hoy, ya que pip y distutils / setuptools no están de acuerdo dónde encontrar los artefactos binarios.

No estoy seguro de seguirlo: estás diciendo que quieres una forma de usar binarios pero no quieres usar los formatos de distribución binarios que ya existen. ¿Porqué es eso?

Los formatos bdist son extremadamente limitantes. Mi grupo tiene que recurrir a un formato tonto, como tar, y luego descomprimirlo literalmente (ninguno de los BSD es compatible, Debian no es compatible, etc.).

Lo que descubrí anoche es que usar un bdist tonto no se puede instalar a través de pip . Los binarios tontos carecen de los metadatos necesarios para ser instalados a través de pip , AFAICT, que es donde supongo que entran en juego las ruedas de pip.

También probé egg y zip, pero carecen de los metadatos necesarios para instalar usando solo un file:// URI.

He estado jugando con la construcción de calzador a través de distutils, setuptools en un sistema de compilación más grande usando make, así que no puedo decir si he hecho "todas las cosas correctas" para que las cosas funcionen de la manera que un bdist estándar

Viniendo de https://github.com/pypa/pip/issues/2195#issuecomment -664728481 Puedo decir que estoy más que feliz de volver a hacer # 7882 detrás de --use-feature=in-tree-build .

¡Hurra! ¡Suena como un plan!

Actualicemos también esta vez la cadena de documentación de --build . ;)

Viniendo del # 2195 (comentario), puedo decir que estoy más que feliz de rehacer el # 7882 detrás de --use-feature = in-tree-build.

Curioso si, además de por línea de comandos, sería razonable tener una opción in-tree-build configurada en pyproject.toml ? Esto sería bastante bueno para resolver # 6276 sin necesidad de hacer un script bash o un archivo make para envolver pip. (No es que ese sea un problema particularmente importante).

tener una opción de construcción en árbol establecida en pyproject.toml

@davidhewitt esta es más o menos la opción 3 en la descripción original de este problema. Tengo entendido que el consenso actual es que es mejor evitar una opción adicional si podemos. Por lo tanto, la idea de habilitar compilaciones en árbol con --use-feature durante un período de transición, con un objetivo a más largo plazo de convertirlo en el mecanismo predeterminado y único.

Por cierto, no podré implementar esto a tiempo para 20.3, pero todavía tengo la intención de hacerlo, con suerte en 20.4.

@sbidoul Escribí un parche para ayudar a incorporar esta función; consulte el n. ° 9091.

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