Pip: Résoudre les problèmes liés aux builds hors de l'arborescence

Créé le 4 janv. 2020  ·  68Commentaires  ·  Source: pypa/pip

J'ouvre ce numéro pour tenter de consolider la discussion sur les versions hors de l'arbre, les problèmes connexes et les solutions possibles.

Quel est le problème que cette fonctionnalité résoudra ?

Lors de la création de projets à partir de répertoires locaux, pip les copie d' abord

Cette approche a soulevé un certain nombre de problèmes au fil du temps :

  • Lorsque setup.py / pyproject.toml n'est pas à la racine du projet et que le build dépend de ressources extérieures au sous-arbre setup.py / pyproject.toml (#3500, #7549, #6276), par exemple :

    • build a besoin de ressources qui sont des liens symboliques vers des fichiers/répertoires en dehors du sous-arbre

    • build a besoin du référentiel git (par exemple lors de l'utilisation de setuptools_scm), et .git/ se trouve dans un répertoire parent non copié dans le répertoire temporaire par pip

    • build repose sur le nom du sous-répertoire (un peu exotique peut-être, mais j'ai un cas où je veux créer un backend de build personnalisé et une partie des métadonnées dépend du nom du sous-répertoire)

  • Problèmes de performances lorsque le répertoire du projet est volumineux (#2195).

Pourquoi pip copie-t-il dans un répertoire temporaire avant la construction ? Avertissement : ce n'est pas clair pour moi - voici ce que j'ai collecté jusqu'à présent :

  • Pour éviter de se fier à quelque chose hors de la source (https://github.com/pypa/pip/issues/2195#issuecomment-524606986) - bien que la définition de "hors de la source" soit la cause de certains problèmes ci-dessus
  • Pour éviter de polluer le répertoire source avec des artefacts de build ou des résidus (?)
  • Autre chose?

Solutions possibles

  1. Construisez un sdist en place, décompressez le sdist dans un emplacement temporaire, puis construisez à partir de là.
  2. Ajoutez une option de pip à construire sur place.
  3. Mettez à jour PEP 517 avec une sorte de mécanisme permettant aux back-ends de communiquer avec les front-ends s'ils sont "sûrs" pour les builds sur place.
  4. Changez le pip pour toujours construire en place.
  5. Changez le pip pour construire sur place par défaut avec une option pour construire hors de l'arbre.

Contexte supplémentaire

Plus de discussions sur la construction via sdist sur discute.python.org .

needs discussion

Commentaire le plus utile

Venant de https://github.com/pypa/pip/issues/2195#issuecomment -664728481 je peux dire que je suis plus qu'heureux de refaire #7882 derrière --use-feature=in-tree-build .

Tous les 68 commentaires

En regardant cela du point de vue du back-end, l'idée d'une "construction hors de l'arborescence" n'a en fait aucun sens. Le back-end reçoit une « arborescence source » sous la forme du répertoire courant du processus, et il lui est demandé d'exécuter un build. Il n'a aucun moyen de savoir si ce répertoire a été extrait d'un sdist, extrait d'un VCS ou copié d'un autre endroit. Tout ce qu'il peut faire, c'est construire, et s'il n'a pas ce dont il a besoin pour construire, signaler l'échec.

Donc , cette jolie tue beaucoup solution possible (3) OMI - le back - end ne dispose pas d' un concept de ce qu'est une construction en place est, il ne peut pas dire si une telle construction est sûre 1.

En ce qui concerne (2), je suis généralement opposé à des options supplémentaires comme celle-ci. Si nous savons quelle est la meilleure chose à faire, nous devons le faire, et si nous ne le savons pas, alors transmettre le problème à l'utilisateur n'est pas une option particulièrement conviviale. Le problème ici est suffisamment subtil pour que peu d'utilisateurs sachent quel est le bon choix, nous verrions donc probablement des gens essayer les 2 options et utiliser aveuglément "tout ce qui fonctionne". De plus, les implications de support sont importantes - offrir une option implique clairement que nous nous attendons à ce que les résultats de la construction soient différents dans au moins certains cas, alors comment tester que les différences sont comme prévu ? Sommes-nous chargés d'informer les utilisateurs sur le moment où l'indicateur de construction sur place serait nécessaire ou non (soit via notre documentation, soit à la suite de problèmes soulevés par des utilisateurs qui ne savent pas lequel utiliser) ?

Cela dit, je ne suis pas contre le fait que pip se contente de construire sur place. Je crois que la raison pour laquelle nous ne le faisons pas est que nous avons eu des cas où des artefacts laissés par une version précédente ont été utilisés dans une version ultérieure, mais ont été construits avec des options différentes (par exemple, des fichiers objet construits en mode débogage étant liés à une version de version fait à partir du même arbre). Il est, cependant, raisonnable de dire que les backends devraient s'assurer que de tels problèmes ne se produisent pas, et s'ils se produisent, c'est un bug du backend et non pas à essayer de s'en défendre. Cependant, je ne suis pas sûr qu'une telle position soit particulièrement conviviale - cela a été soulevé lors des discussions sur le PEP 517 sous le thème des versions incrémentielles, et était suffisamment controversé pour qu'il soit reporté à l'époque.

Ma préférence a été de construire un sdist, puis de construire la roue à partir de cela, depuis longtemps maintenant (votre option 1). Je suis d'accord avec la construction de pip en place (si nous pouvons convenir que cela est sûr, étant donné que, comme je l'ai noté ci-dessus, nous ne pouvons pas nous attendre à ce que le back-end nous le fasse savoir), mais je pense que cela aurait besoin d'une communauté discussion pour débattre des implications plus larges (sur les backends, les pip/frontends et les utilisateurs finaux).

1 Il serait bien sûr possible de mettre à jour le PEP 517 pour définir ce qui constitue une arborescence source "sur place" par opposition à une construction "hors arborescence", mais je soupçonne que ce serait un concept très difficile à cerner.

J'ai ajouté les solutions 4. (Changer pip pour toujours construire en place) et 5. (Changer pip pour construire en place par défaut avec une option pour construire hors de l'arbre).

J'ai des sentiments mitigés concernant la construction via sdist (solution 1.) pour les raisons suivantes :

  1. certains projets peuvent avoir un chemin sdist-roue interrompu ; alors que je vois l'intérêt de valider cette construction à partir de sdists, le faire par défaut maintenant va certainement casser beaucoup de constructions d'utilisateurs finaux
  2. il a toujours des implications en termes de performances pour les projets avec de grands sdists, et en raison d'appels de sous-processus supplémentaires
  3. Personnellement, je pense que nous ne devrions pas trop insister sur les sdists car il est assez courant pour un projet de publier des artefacts construits et de se référer à leur plate-forme d'hébergement de code préférée pour leurs versions source (par exemple, les backends qui reposent sur la présence d'un contrôle VCS pour le bâtiment doit sauter à travers des cerceaux pour produire un sdist fonctionnel). Et PEP 517 permet spécifiquement aux backends de collecter UnsupportedOperation pour build_sdist .

Cet article sur discuter résume également des arguments similaires.

Je suis d'accord que le chemin de la solution 3. est loin d'être évident.

Je suis également d'accord que nous devrions éviter les options supplémentaires si nous le pouvons.

Je note également qu'il y a eu des voix en faveur des constructions sur place dans le fil de discussion lié, mais en effet, une discussion communautaire ciblée sur ce sujet spécifique est nécessaire si nous voulons explorer cette approche.

  • Construisez un sdist en place, décompressez le sdist dans un emplacement temporaire, puis construisez à partir de là.

Je pense que c'est une bonne approche.

Il est probable que l'OMI soit exécuté pour un seul package dans la plupart des exécutions de pip install impliquant des répertoires locaux - le plus souvent, j'imagine pip install . . Cela serait probablement fait dans le cadre du flux de travail de développement du package.

Voici ce que je pense que ce comportement devrait être:

  • Si le backend est incapable de créer un sdist, faites local-dir -> wheel (in-place)

    • Je pense qu'il incombe en grande partie au backend de s'assurer que local-dir -> wheel est une opération idempotente dans ce cas.

  • Si le backend est capable de créer un sdist, faites local-dir -> sdist -> unpacked-sdist -> wheel.

En faisant local-dir -> sdist -> wheel, nous avons un ensemble supplémentaire d'appels. Cependant, je pense qu'il est raisonnable de valider que les sdists générés sont sains, en particulier pendant le développement. tox le fait déjà dans le cadre de son flux de travail, check-manifest pour couvrir les interfaces pas si conviviales de setuptools ici.

Dans l'ensemble, je pense que les coûts de construction d'un sdist lorsqu'on lui donne un répertoire local en valent la peine pour éviter de telles erreurs dans les projets, d'autant plus que les personnes qui installent à partir de répertoires locaux sont probablement les développeurs du projet eux-mêmes.

En ce qui concerne le déploiement, je pense que nous voudrions attendre et voir comment le #6536 se présente. Nous apprendrions certainement quelques choses qui peuvent être transférées.

Je préfère construire/installer sur place sans sdist (donc setup.py install ou setup.py bdist_wheel ou appeler build_wheel sur le backend PEP 517, selon le cas) plutôt que de construire un sdist, de le déballer et de l'installer à partir de ce. Mes raisons spécifiques sont :

  1. Prend en charge le plus grand nombre d'utilisateurs prêts à l'emploi.

    1. Supposons que pip fasse local-dir -> installé (via la construction de roue sur place ou setup.py install ). Les utilisateurs qui souhaitent passer de local-dir -> installé peuvent exécuter pip install local-dir . Les utilisateurs souhaitant passer de local-dir -> sdist -> installé sont libres de créer un sdist, puis d'exécuter pip install ./path/to/sdist . Il existe de nombreux outils alternatifs qui peuvent créer un sdist, et c'est quelque chose que les utilisateurs seront plus susceptibles d'avoir déjà à moins qu'ils ne créent à la main les distributions qu'ils téléchargent sur PyPI.

    2. Supposons maintenant que pip fasse local-dir -> sdist -> installé. Les utilisateurs qui souhaitent passer de local-dir -> installé n'ont pas d'options impliquant pip. Les utilisateurs souhaitant passer de local-dir -> sdist -> installé peuvent exécuter pip install local-dir . Les utilisateurs sans options demanderont à pip des options pour contrôler le comportement ou devront trouver un autre outil dont ils n'auraient pas eu besoin autrement.

  2. Si nous implémentons local-dir -> sdist -> installé, nous le ferions probablement également pour les exigences basées sur VCS ? Si c'est le cas, c'est plus de travail. Si ce n'est pas le cas, il s'agit de chemins supplémentaires dans le code et d'écarts dans la gestion de l'installation qui doivent être mémorisés par les utilisateurs ou lors de la fourniture d'une assistance.
  3. Moins de travail à mettre en œuvre. Il y a trois endroits à modifier pour implémenter local-dir -> installé ( ici , ici et ici ). Pour local-dir -> sdist -> installé, je ne voudrais même pas toucher à l'implémentation jusqu'à ce que #6607 soit terminé, sinon je pense que cela se retrouverait à beaucoup d'endroits dans la base de code similaire au téléchargement d'artefacts/vérification de hachage.
  4. Moins de travail à tester puisque, selon l'OMI, les tests existants sont suffisants pour couvrir le chemin du code local-dir -> installé. Pour le chemin local-dir -> sdist -> installé, nous voudrions vérifier que nous construisons réellement un sdist et que les solutions de secours fonctionnent pour construire directement une roue.
  5. Moins de travail (calculé). Comme mentionné ailleurs, local-dir -> sdist -> installé est un appel de sous-processus supplémentaire (et ce sous-processus fonctionne). Cela signifie également que pip doit décompresser le sdist (bonjour les antivirus et autres disques lents) avant de construire la roue.

Quelle que soit l'approche, le seul problème que je vois à faire les choses sur place est que pour les builds setuptools (je pense que cela s'applique à l'héritage et au PEP 517), nous nous retrouverions avec .egg-info dans le répertoire du projet qui se trompera en tant que "paquet installé" lorsque pip est invoqué avec python -m pip dans ce répertoire. Cela serait corrigé par # 4575 qui n'inclurait vraisemblablement PAS le répertoire actuel dans la requête pour les packages installés pour n'importe quel schéma.

Notant que je suis devenu d'accord pour dire que l'idée de sauter la construction sdist et de faire directement une construction dans l'arborescence est une meilleure approche pour pip à prendre par défaut, et de ne pas essayer de faire local-dir -> sdist -> wheel.

Dans Fedora, lorsque nous construisons des packages Python RPM, nous sommes des dinosaures et la façon standard de procéder est d'utiliser python setup.py build . Avec PEP 517, nous avons ajouté une manière "provisoire" d'utiliser pip wheel place. Cependant, avec les modules d'extension, nous avons un problème avec l'approche "déplacer les sources vers tmp, construire à partir de là" que pip utilise pour les construire.

Notre machine de construction injecte des drapeaux de compilateur afin que les artefacts de construction (les modules d'extension .so dans ce cas) contiennent des métadonnées sur leurs sources. Plus tard, un script shell parcourt les artefacts de construction, extrait ces informations et copie les sources dans /usr/src/debug pour être installé via un RPM spécial *-debugsource . La mahcinery s'attend à ce que tout soit construit dans l'arbre de travail et cela ne fonctionne pas vraiment bien lorsqu'il est construit à l'extérieur. Voici les choses qui peuvent être faites (ensemble) pour atténuer le problème de notre côté :

  1. définissez la variable d'environnement $TMPDIR pour l'avoir à l'endroit où le script RPM l'attend (c'est- export TMPDIR=%{_builddir}/.tmp dire
  2. utilisez pip wheel avec l'option --no-clean pour conserver les sources copiées dans $TMPDIR
  3. exécutez un shell kung fu pour réécrire les informations "quelle est ma source" à l'emplacement correct : 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. (Facultatif : nettoyez $TMPDIR manuellement.)

Nous n'aimons pas particulièrement la troisième étape car elle repose sur trop de détails d'implémentation :

  • /usr/lib/rpm/debugedit API et emplacement (et existence)
  • le nom pip-req-build
  • le mécanisme avec lequel pip construit les sources

Si pip était toujours construit en place ou s'il y avait un commutateur de ligne de commande pour cela, le problème disparaîtrait.

Rapport en aval : https://bugzilla.redhat.com/show_bug.cgi?id=1806625

Notant que je suis devenu d'accord pour dire que l'idée de sauter la construction sdist et de faire directement une construction dans l'arborescence est une meilleure approche pour pip à prendre par défaut, et de ne pas essayer de faire local-dir -> sdist -> wheel.

Je suis également de plus en plus enclin à accepter l'idée de construire une roue sur place. Mes réservations restantes sont :

  1. Nous comptons sur le comportement du backend "correctement" - par exemple, ne donnant pas de résultats différents en fonction des données restantes des versions précédentes, ou autre. Je suis d'accord pour faire cette hypothèse, mais je suis préoccupé par le coût du support si nous commençons à faire dire aux gens "pip a mal construit ma roue" et nous devons déboguer seulement pour découvrir qu'il s'agit d'un problème de backend.
  2. Je pense que quelle que
  3. En corollaire à ce qui précède, nous devons nous assurer qu'il n'y a pas de projets à notre connaissance qui reposent sur notre approche actuelle de « copier et construire », comme s'il y en avait, nous les briserons avec ce changement.

... et bien sûr, quelqu'un devra rédiger un PR mettant en œuvre ce changement (avec des tests, des documents, etc. - les trucs habituels) sinon tout ce que nous faisons c'est parler

Nous comptons sur le comportement du backend "correctement" - par exemple, ne donnant pas de résultats différents en fonction des données restantes des versions précédentes, ou autre. Je suis d'accord pour faire cette hypothèse, mais je suis préoccupé par le coût du support si nous commençons à faire dire aux gens "pip a mal construit ma roue" et nous devons déboguer seulement pour découvrir qu'il s'agit d'un problème de backend.

Serait-il judicieux d'étendre l'interface PEP 517 pour inclure un crochet « propre » ? Nous voudrions probablement que ce soit un point de toute façon pour permettre d'autres efforts (par exemple, implémenter une installation modifiable, créer un outil de développement de packages qui construit n'importe quel projet PEP 517). pip peut l'appeler ici pour s'assurer qu'il n'y a pas de déchets indésirables avant de faire la construction dans l'arborescence.

Serait-il judicieux d'étendre l'interface PEP 517 pour inclure un crochet « propre » ?

Peut-être? Mais si pip appelle automatiquement clean , il y a forcément quelqu'un qui veut ne pas le faire, faire des builds incrémentiels ou quelque chose du genre. Et puis nous nous retrouvons avec une autre option.

Mon inclination est de rester avec ma position "nous devons être en mesure de supposer qu'il est de la responsabilité du backend de s'assurer que les builds sur place fonctionnent correctement". Même si cela s'avère intenable, obtenir des exemples concrets des raisons pour lesquelles cela ne fonctionne pas nous aidera à mieux comprendre ce qu'il faut faire à propos du problème, plutôt que de simplement deviner.

  1. Nous comptons sur le comportement du backend "correctement" - par exemple, ne donnant pas de résultats différents en fonction des données restantes des versions précédentes, ou autre.

Je serais tenté d'étendre PEP-517 pour en faire une exigence explicite.

Je serais tenté d'étendre PEP-517 pour en faire une exigence explicite.

Il dit déjà ceci :

Le backend peut stocker des artefacts intermédiaires dans des emplacements de cache ou des répertoires temporaires. La présence ou l'absence de caches ne devrait pas avoir d'incidence importante sur le résultat final de la construction.

Ce n'est pas tant que les backends sont susceptibles de violer délibérément cette exigence, car les utilisateurs signaleront naturellement le problème comme un problème de pip et seront redirigés vers le projet backend, ce qui représente un peu plus de frais généraux.

En essayant de trouver une solution de contournement pour Fedora, nous avons été touchés par https://github.com/pypa/pip/issues/7872

Depuis que nous avons résolu le problème ".egg-info in cwd" avec #7731 et ses amis, c'est une chose de moins à se soucier lors de la construction en place.

Ainsi, l'option 4 (toujours construite en place) a été implémentée dans #7882 .

Nous avons maintenant (par #7951) publié une version bêta de pip, pip 20.1b1. Cette version inclut #7882, qui a mis en œuvre une solution à ce problème.

J'espère que les participants à ce numéro nous aideront en testant la version bêta et en recherchant de nouveaux bogues. Nous aimerions identifier et résoudre tout problème potentiel avant la sortie de la version principale 20.1 mardi.

J'accueille également les commentaires positifs du type « yay, ça marche mieux maintenant ! » aussi, puisque le traqueur de problèmes est généralement plein de "problèmes". :)

Nous prévoyons totalement de le tester dans Fedora (nous l'avions déjà prévu avant votre commentaire), cependant la date limite de mardi n'est probablement pas réaliste.

@hroncok Une idée de quand Fedora pourra-t-il tester ces changements ?

Je ferai de mon mieux pour le faire d'une manière ou d'une autre lundi, mais je ne peux faire aucune promesse.

En effet, 20.1b1 fait disparaître nos problèmes.

Commentaires plus généraux 20.1b1 dans https://mail.python.org/archives/list/[email protected]/message/5EAUIYYIRKXEHTAG5GQ7EJHSXGZIW2F7/

Hourra! Merci beaucoup d'avoir essayé la version bêta de @hroncok ! Très appréciée! ^>^

Un résultat de la construction en place : j'avais construit des roues pour plusieurs versions de python en parallèle (à l'intérieur d'un conteneur docker manylinux). Avec les builds sur place, les builds parallèles ne fonctionnent pas car les différentes versions sont en conflit. Avec les versions hors de l'arborescence, chaque version créait une arborescence distincte et n'avait pas de problème.

@manthey cette discussion est sous #8168

Cela fait donc plus de 10 jours maintenant. Quelques problèmes ont été soulevés à propos du changement (tous attendus, je dirais - #8165, #8168, #8196). Il y avait aussi des gens qui mentionnaient explicitement que le changement les aidait.

  • Outre les problèmes de performances, le comportement précédent (copier dans le répertoire temporaire) présentait des problèmes d'exactitude (liés ci-dessus) impossibles à résoudre par pip sans connaissance du contexte que seul l'appelant possède (et, en passant, ce code copytree était déjà plein de pansements pour faire face à des situations étranges - tmpdir dans l'arborescence, sockets, etc.).
  • Une option pour activer le comportement précédent aurait toujours des problèmes d'exactitude et de performances.
  • Une solution correcte impliquera la prise en charge du back-end de build pour contrôler le répertoire de build qui n'existe pas complètement aujourd'hui (par exemple, setuptools bdist_wheel a --bdist-dir , mais écrit toujours .egg-info dans place, voir aussi https://github.com/pypa/setuptools/issues/1816, https://github.com/pypa/setuptools/issues/1825). Alors maintenant que pip se comporte correctement, la discussion peut changer pour voir si, par exemple, setuptools peut faire évoluer une option pour faire une construction sans toucher au répertoire source, puis regarder si une modification PEP 517 est nécessaire ou non pour contrôler cette option.
  • En attendant, les problèmes signalés sont probablement relativement faciles à contourner par les appelants (par exemple, en se copiant dans un répertoire temporaire ou en créant une archive temporaire, ce qu'ils peuvent faire correctement en toute connaissance du contexte).
  • Enfin, il est difficile de le dire avec certitude avec les données dont nous disposons, mais mon intuition est que ce changement aide plus de gens qu'il n'en blesse.

Je déteste donc interrompre les changements, mais celui-ci n'a pas été fait à la légère, et pour ces raisons, je suis personnellement enclin à le garder.

Je ne suis pas d'accord pour dire que le nouveau comportement est que pip se comporte "correctement", différemment à coup sûr, et apparemment dans certains cas différemment cassé et je pense que le cadrer comme tel est incorrect. Cela représente un compromis pour un ensemble d'utilisateurs cassés pour un ensemble différent, dans les deux cas, des solutions de contournement pouvaient être effectuées.

Je n'aurais pas fusionné ce changement et je l'aurais manqué ou je me serais opposé à cela (et je pense que maintenant, l'ayant fusionné, cela rend certains l'utilisation de pip comme fonction de forçage pour aider à empêcher certains types de paquets cassés de manière beaucoup plus difficile ). Cela étant dit, je ne sais pas vraiment si revenir en arrière est la bonne chose ici. Cela peut devenir encore plus déroutant pour les utilisateurs si le comportement change beaucoup. Si nous voulons revenir en arrière, nous devons le faire rapidement, sinon le comportement actuel devrait probablement être meilleur ou pire.

J'ai utilisé le terme "correctement", car avant pip wheel <localdir> et pip install <localdir> généraient une roue différente de cd <localdir> ; setup.py bdist_wheel dans certains cas : avec différents fichiers manquants en présence de liens symboliques (# 3500) ou une version différente avec setuptools_scm (#7549), ou https://github.com/pypa/pip/issues/7555#issuecomment -595180864, ​​ou #6276, ou de simples erreurs. Je ne pense pas que le pip 20.1 génère de si mauvaises roues/installations, donc dans ce sens, je pense que c'est en effet plus correct.

Bien sûr, nous savions que le changement interromprait certains flux de travail et le compromis doit être réévalué maintenant que nous avons des commentaires, et être annulé ou confirmé pour de bon.

Et il peut toujours produire des roues différentes de setup.py sdist && pip install dist/*.tar.gz .

Ma suggestion serait de rétablir le PR et de mettre en œuvre le correctif en parcourant d'abord un sdist, puis en créant une roue à partir du sdist résultant.

Cela devrait résoudre tous les problèmes d'exactitude, sauf dans les cas où le projet est incapable de créer correctement un sdist et qui, selon l'OMI, n'est pas un cas d'utilisation important à résoudre.

Le compromis est que ce sera plus lent. Cependant, une fois cela mis en œuvre, nous pouvons ensuite affiner davantage l'interface PEP 517 pour ajouter des API facultatives qui permettent des accélérations. Cela ne sera probablement jamais aussi rapide que ce changement, mais nous pouvons certainement nous en rapprocher.

Ce changement tel quel rend effectivement impossible de pousser plus loin l'exactitude sans introduire des régressions de performances dont les utilisateurs ne seront probablement pas satisfaits. Cependant, si nous corrigeons et améliorons les performances, nous pouvons arriver à un juste milieu qui satisfasse les deux parties.

Comme le dit le vieil adage, corrigez d'abord les choses, puis faites vite. Je crains qu'avec cette RP nous l'ayons accélérée et bloqué notre capacité à la corriger.

Je suis toujours d'accord pour que la validation des sdists soit souhaitable, mais pas au moment de l'installation de pip. C'est peut-être une fonctionnalité d'un futur outil de création de sdist ou d'une commande de construction pip.

De plus, setup.py sdist crée .egg-info dans le répertoire local, de sorte que les problèmes signalés avec le répertoire source en lecture seule ou les versions simultanées resteraient.

Si cela ne se produit pas au moment de l'installation du pip, cela ne se produit fonctionnellement qu'au moment de l'installation du pip de quelqu'un d'autre. Le sauter signifie simplement que nous avons plusieurs chemins qu'un projet peut suivre pour aller de VCS au package installé et chaque chemin est une autre chance de différences. Ce n'est pas une nouveauté, fondamentalement, chaque option que nous avons qui modifie le chemin d'installation se retrouve avec un ensemble différent d'octets sur le disque, même pour les projets les plus rigoureux. Des différences subtiles existent toujours et vous espérez simplement que ces différences ne sont pas significatives - ou vous pouvez faire ce que vous pouvez pour supprimer ces différences en rendant structurellement impossible de les avoir dès le départ.

Certains problèmes de performances pourraient en effet réapparaître si/lors de la construction via sdist mais ils seraient probablement d'un ordre de grandeur inférieur à ce que nous avions dans pip < 20,1. En effet, la plupart d'entre eux résultaient souvent de la copie de .git , ou d'un venv , ou d'autres éléments volumineux sans rapport qui ne seraient pas dans le sdist.

Indépendamment de ce que pip finira par finir, pourrions-nous faire de l'autre une option, car il est peu probable que l'un ou l'autre soit capable de satisfaire tout le monde ? J'imagine que si l'approche actuelle doit être conservée (je n'ai pas vraiment d'avis sur laquelle devrait être la valeur par défaut), nous devrions être en mesure de fournir un dernier résultat de secours où un utilisateur peut choisir de créer un sdist et d'installer le paquet à partir de là.

De plus, setup.py sdist crée .egg-info dans le répertoire local, de sorte que les problèmes signalés avec le répertoire source en lecture seule ou les builds simultanés resteraient.

Je pense (au moins un test rapide est d'accord avec moi) que seul setuptools (pas distutils ) le fait, et ce comportement est configurable pour créer le répertoire ailleurs. Semblable à d'autres backends, nous devrions être en mesure de leur recommander de faire une génération de sdist propre .

FWIW, je ne pense pas que nous aurions besoin de vider le répertoire sdist-generation --egg-info dans le répertoire de travail, si nous adoptons l'approche generate-sdist-unpack-it-build-wheel, puisque nous pouvons déchargez-le dans un répertoire temporaire, comme nous le faisons pour generate_metadata .

@pradyunsg cela ne nécessite-t-il pas un changement dans les outils de configuration ? La dernière fois que j'ai vérifié la commande sdist n'avait pas d'option pour spécifier l'emplacement de base .egg-info , contrairement à egg_info qui a une option --egg-base que nous avons exploitée dans #7978.

En effet! Je regardais le mauvais fichier dans setuptools. Je suis corrigé.

Pourquoi tout est-il si complexe dans cet espace ? :(

$  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 cela ressemble à un joli bogue d'arrêt, sans doute pas notre bogue mais c'est un type de bogues qui, selon moi, se produirait lors de la discussion PEP 517 lorsque faire sur place des versions par défaut est venu.

bdist_wheel a été invité à nettoyer automatiquement son répertoire de construction dans le passé. Cette fonctionnalité devrait être intégrée. Les autres versions de distutils sont-elles propres ?

S'il s'agissait de SCons, il se souviendrait des fichiers dont il se souciait et omettrait les fichiers supplémentaires du répertoire build/ de la roue, même s'ils étaient présents dans le système de fichiers.

Je crois que le problème ci-dessus n'affecte pas seulement manylinux. Cela devrait arriver chaque fois que le répertoire de construction n'est pas assez spécifique pour capturer l'ABI (dans le cas de setuptools, il apparaît que la plate-forme et la version python sont tout ce qui est capturé dans la balise ABI du répertoire de construction). Je pense que cela s'étend au-delà de l'ABI avec l'interpréteur actuel aussi, si quelque chose est lié à NumPy par exemple, je pense qu'il a une ABI qui fonctionnera sur les NumPy plus récents, mais pas plus anciens, et à moins qu'ils ne codent cela dans le nom du répertoire de construction, cela fonctionnera effet utilise comme ça aussi.

Nettoyer automatiquement le répertoire de construction ne résout pas le problème, cela le rend juste moins probable (par exemple, exécuter deux invocations pip wheel en parallèle pourrait toujours déclencher le problème), en plus de cela l'une des raisons supposées de l'implémentation de cette manière (au moins pendant la discussion PEP 517) était que cela fournirait plus de performances en permettant la mise en cache entre les appels pour les builds incrémentiels. IOW, le comportement actuel est ce que certains sous-ensembles voulaient, en réutilisant les artefacts de construction entre les exécutions, il se trouve que le backend de construction de loin le plus courant se trompe (et sans doute, dans certains cas, n'a pas assez d'informations pour bien faire les choses sans par personnalisation du package).

Bien sûr, avec suffisamment d'indicateurs pour la commande setuptools sous-jacente, vous pouvez y remédier (quelque chose comme py setup.py egg_info --egg-base /tmp/foo build --build-base /tmp/foo/build-base bdist_wheel --bdist-dir /tmp/foo/bdist ferait).

Je répète cependant que le problème ne vient pas des fichiers supplémentaires, c'est que l'ABI attendue avec laquelle la roue était compatible a changé et que le .so n'a pas été reconstruit. Si SCons est suffisamment intelligent pour savoir que Python construit avec pymalloc a besoin d'un répertoire de construction et Python construit avec un autre (y compris des choses comme les versions NumPy auxquelles le .so pourrait être lié), alors cela n'est pas affecté. S'il réutilise un artefact précédemment construit avec une ABI différente, il est affecté.

J'ai essayé de tester enscons mais je n'ai pas réussi à faire construire la rsalette sans erreur.

J'ai essayé de tester scikit-build pour voir comment il gérait les versions incrémentielles, et peu importe ce que j'ai fait, il a vomi sur lui-même lors de la 2ème version et j'ai dû supprimer manuellement le répertoire _skbuild chaque fois pour l'obtenir exécuter sans erreur.

Frais. Désolé malheureusement, enscons a mis à jour et pas rsalette.

Le mercredi 6 mai 2020 à 16h18, Donald Stufft a écrit :

J'ai essayé de tester enscons mais je n'ai pas réussi à faire construire la rsalette sans erreur.

J'ai essayé de tester scikit-build pour voir comment il gérait les versions incrémentielles, et peu importe ce que j'ai fait, il a vomi sur lui-même lors de la 2ème version et j'ai dû supprimer manuellement le répertoire _skbuild chaque fois pour l'obtenir exécuter sans erreur.

-
Vous recevez ceci parce que vous avez commenté.
Répondez directement à cet e-mail, consultez-le sur GitHub https://github.com/pypa/pip/issues/7555#issuecomment-624867490 , ou désabonnez-vous https://github.com/notifications/unsubscribe-auth/AABSZERIEDAPUIXCPAKBBUDRQHAXRANCNFSM4KCV5MHQ .

Désolé malheureusement, enscons a mis à jour et pas rsalette.

Existe-t-il un bon poste C qui utilise des enscons mis à jour ? J'ai juste choisi rsalette parce qu'elle était la première de la liste et je n'avais pas envie de la déboguer, contente d'essayer avec autre chose.

Le seul problème avec rsalette est qu'il ne devrait pas passer ROOT_IS_PURELIB à Environment dans SConstruct. Il n'a pas d'extension C. cryptacular semble correct.

#8165 (commentaire)

Sur ce, je pense que je suis d'accord pour dire que nous devrions annuler ce changement.

Je pense que les nouveaux problèmes sont beaucoup plus importants que prévu. Peut-être un rapide 20.1.1 pour revenir et ensuite nous pourrons avoir une discussion plus longue sur la façon de résoudre les problèmes de builds in-tree et out-of-tree ?

Je vote également pour revenir et poursuivre https://discuss.python.org/t/proposal-adding-a-persistent-cache-directory-to-pep-517-hooks/2303/15 comme solution pour cela (ce serait permettre aux backends de se construire pas en place, donc ne pas exposer de tels problèmes). Chim dans ce fil aussi si vous êtes d'accord avec la suggestion là-bas.

Cela me semble également judicieux. Je pense qu'une approche in-tree (ou build-from-sdist) présente des avantages extrêmement importants (je suis presque sûr que nous aurons des plaintes d'une partie de la base d'utilisateurs lorsque nous reviendrons 🙂) mais les inconvénients sont également important.

Je ne sais pas ce que l'interface utilisateur devrait être ici (à quelle approche par défaut ? quel type d'options devrions-nous avoir ?) mais je pense que nous devrions prendre un peu plus de temps pour décider cela, plutôt que de prendre des décisions tout en combattant les problèmes actuels .

D'accord ! Je pense que le consensus général est de revenir en arrière et de réévaluer. Je vais déposer un PR pour cela. :)

Chim dans ce fil aussi si vous êtes d'accord avec la suggestion là-bas.

S'il vous plaît faites - j'ai commenté, mais j'en suis au point où je n'en sais pas assez pour offrir des suggestions significatives, donc la contribution de personnes ayant plus d'expérience serait précieuse.

Je viens de déposer quelques gros blobs de texte dans https://github.com/pypa/pip/issues/8165#issuecomment -625401463. Je vais m'éloigner pour aujourd'hui... J'ai fini par me sentir un peu frustré en écrivant les notes personnelles à la fin. Se retrouver sur #5599 et lire les commentaires négatifs des utilisateurs n'a certainement pas aidé.

Salut les gens, j'ai réfléchi un peu plus à cela, voici mon point de vue actuel sur cette question.

  1. Construisez un sdist en place, décompressez le sdist dans un emplacement temporaire, puis construisez à partir de là.

Je pense toujours que pip install / pip wheel n'est pas le bon endroit pour essayer d'attraper les mauvais sdists. Cela ne devrait-il pas être une responsabilité en arrière-plan de ne pas créer de mauvais sdists en premier lieu ? De plus, je pense que la construction inconditionnelle via sdist est probablement aussi perturbatrice que la construction en place.

  1. Ajoutez une option de pip à construire sur place.

Celui que je préfère à court terme, puisque la solution 4 ne l'a pas fait. Est-il prématuré d'ajouter cela dans le pip 20.1.1 ?

  1. Mettez à jour PEP 517 avec une sorte de mécanisme permettant aux back-ends de communiquer avec les front-ends s'ils sont "sûrs" pour les builds sur place.

Avec ce pip, il faudrait encore se rabattre sur son copytree cassé et irréparable, donc je ne suis pas en faveur de celui-ci.

  1. Changez le pip pour toujours construire en place.

Celui-ci est donc jugé trop perturbateur et nous y reviendrons en 20.1.1.

  1. Changez le pip pour construire sur place par défaut avec une option pour construire hors de l'arbre.

Cela pourrait être l'objectif à long terme, l'option de créer un mélange hors de l'arbre avec le concept de répertoire de cache ?

Je n'aime vraiment pas les options CLI, en particulier celles comme celle-ci. Que se passe-t-il si je répertorie deux packages qui se trouvent sur mon FS local et que je dois en créer un en place et un non ? Si nous proposons une option pour faire l'un ou l'autre, il va se retrouver avec des packages existants qui ne peuvent être construits qu'avec l'un ou l'autre.

Cela me sent aussi comme le genre d'option qui existe entièrement parce qu'un projet n'a pas pu prendre de décision et a décidé de simplement repousser cette décision à l'utilisateur final.

Construire via sdist ne consiste pas exactement à attraper les mauvais sdist. Il s'agit en grande partie de réduire les variations possibles de "chemin" qu'un projet peut parcourir avant d'être installé. L'ajout d'un indicateur à certains égards aggrave ce problème, pas mieux.

Pour ce que je veux dire, nous avons quelques "chemins" par lesquels les installations peuvent passer :

  1. VCS -> Sdist -> Roue -> Installé
  2. VCS -> Roue -> Installé
  3. VCS -> Installé (Héritage)

Certains chemins supplémentaires sont en quelque sorte supprimés, mais en général, ce sont nos 3 (et idéalement, 3 sont également supprimés). Il existe également des installations modifiables, mais elles ne vont pas disparaître de sitôt.

Nous pouvons considérer qu'un sdist ou une roue sont téléchargés sur PyPI et installés à partir de là pour faire partie du même "chemin", vous le mettez simplement en pause et le terminez sur un autre ordinateur.

Le problème d'avoir plusieurs "chemins" comme celui-ci, c'est qu'il introduit des incohérences dans l'installation finale résultante. Ils ne se produisent pas toujours, mais c'est un cas facilement observable que cela se produit fréquemment. Souvent, ces incohérences ne sont pas importantes, mais parfois elles le sont.

Si nous faisons des constructions sur place comme celle-ci, alors nous disons en fait que nous ne pourrons jamais nous effondrer sur un seul chemin, et nous devrons toujours faire face à ce cas limite étrange où parfois les gens obtiendront des résultats différents en fonction de la façon dont l'installation a été effectuée.

En tant qu'avantage supplémentaire, cela peut également agir comme une fonction de forçage pour aider à garantir que le chemin heureux reste heureux.

La plupart du temps, je suis d'accord avec @dstufft , et en particulier je suis d'accord pour que l'approche build-from-sdist ne soit pas considérée comme "essayer de valider sdists" mais comme "tout suit l'arborescence des sources -> sdist -> wheel -> install route (juste certaines choses sautent certaines étapes initiales)".

Cependant, je veux revenir sur un point :

Que se passe-t-il si je répertorie deux packages qui se trouvent sur mon FS local et que je dois en créer un en place et un non ?

Exécutez simplement les deux packages dans deux séries de pip distinctes avec des options différentes ?!? Je sais qu'il est possible que l'un soit une dépendance de l'autre et votre point de vue est en général valable, mais il semble y avoir une tendance générale pour les gens à supposer que chaque scénario d'installation doit être réduit en une seule série de pip, et je ne ' Je pense que c'est raisonnable (nous avons eu de très bonnes solutions de contournement pour que les problèmes soient rejetés par l'utilisateur car "cela signifie que je devrais diviser ma liste d'exigences en deux")

Notez que lorsque (si) nous revenons, nous devrons rouvrir des problèmes comme #6276 qui a été fermé à la suite de la mise en œuvre de builds in-tree.

Une partie du problème est que pip ne tient pas compte de ce qui est déjà installé lors de la résolution des dépendances (je ne sais pas si le nouveau travail du résolveur change cela ?), vous devez donc avoir tout contenu dans une seule invocation pip si vous voulez le résoudre dépendances "correctement" (dans la mesure où notre résolveur actuel fait quelque chose correctement).

Si le nouveau résolveur prend en compte ce qui est déjà installé, alors pip install foo bar et pip install foo && pip install bar seraient à peu près égaux et n'auraient aucune importance, mais si ce n'est pas le cas (et c'est à peu près vrai maintenant) si les deux projets dépendaient de "spam" mais que foo requis < 2 et bar requis > 1, nous obtiendrions une installation invalide.

C'est une tangente cependant :)

(Je ne sais pas si le nouveau travail de résolution change cela ?)

Entrées bienvenues au #7744. :)

  1. Changez le pip pour toujours construire en place.

Celui-ci est donc jugé trop perturbateur et nous y reviendrons en 20.1.1.

Pour être clair, c'est aussi que nous l'avons "déployé trop vite" et l'approche de déploiement que nous avons adoptée explique certainement en partie pourquoi cela a fini par être trop perturbateur.

  1. Ajoutez une option de pip à construire sur place.

@dstufft @pfmoore Je vois ce type d'option comme un mécanisme d'inscription afin que nous puissions progressivement inciter les utilisateurs à se tourner vers des versions sur place dans le but d'en faire la valeur par défaut à un moment donné. Dans l'esprit de ce commentaire : https://github.com/pypa/pip/issues/8165#issuecomment -625501216

Je vais déposer un PR pour cela. :)

8221 c'est.

20.1.1 a été publié, contenant les modifications annulées.

Dans Fedora, lorsque nous construisons des packages Python RPM, nous sommes des dinosaures et la façon standard de procéder est d'utiliser python setup.py build . Avec PEP 517, nous avons ajouté une manière "provisoire" d'utiliser pip wheel place. Cependant, avec les modules d'extension, nous avons un problème avec l'approche "déplacer les sources vers tmp, construire à partir de là" que pip utilise pour les construire.

Notre machine de construction injecte des drapeaux de compilateur afin que les artefacts de construction (les modules d'extension .so dans ce cas) contiennent des métadonnées sur leurs sources. Plus tard, un script shell parcourt les artefacts de construction, extrait ces informations et copie les sources dans /usr/src/debug pour être installé via un RPM spécial *-debugsource . La mahcinery s'attend à ce que tout soit construit dans l'arbre de travail et cela ne fonctionne pas vraiment bien lorsqu'il est construit à l'extérieur. Voici les choses qui peuvent être faites (ensemble) pour atténuer le problème de notre côté :

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

C'est effectivement le chemin que j'ai emprunté en cherchant l'intégration de pip, #6505, etc.

Les versions itératives avec pip sont effectivement cassées aujourd'hui, ce qui est une perte majeure pour les groupes qui ont une grande quantité de code python sous forme d'extension C, j'ai donc eu recours à la construction avec setup.py .

pip besoin d'une commande build et le résultat final de la commande build doit être transmissible à d'autres sous-commandes, comme wheel , install , etc.

À l'heure actuelle, pip traite efficacement install comme build et install , ce qui n'est _pas_ ce que veulent certaines personnes qui créent et mettent en cache des artefacts binaires, installant des binaires sur read -seulement les montures, etc.

Je souhaite vraiment qu'il y ait un moyen d'utiliser setup.py pour construire les binaires, puis pip install sans recourir à la création d'un bdist , mais cela ne semble pas être possible aujourd'hui , puisque pip et distutils / setuptools ne sont pas d'accord sur l'endroit où trouver les artefacts binaires.

Je souhaite vraiment qu'il y ait un moyen d'utiliser setup.py pour construire les binaires, puis pip les installe sans avoir recours à la création d'un bdist, mais cela ne semble pas être possible aujourd'hui, car pip et distutils/setuptools ne sont pas d'accord sur où trouver les artefacts binaires.

Je ne suis pas sûr d'avoir suivi - vous dites que vous voulez un moyen d'utiliser des binaires mais que vous ne voulez pas utiliser les formats de distribution binaires qui existent déjà. Pourquoi donc?

Je souhaite vraiment qu'il y ait un moyen d'utiliser setup.py pour construire les binaires, puis pip les installe sans avoir recours à la création d'un bdist, mais cela ne semble pas être possible aujourd'hui, car pip et distutils/setuptools ne sont pas d'accord sur où trouver les artefacts binaires.

Je ne suis pas sûr d'avoir suivi - vous dites que vous voulez un moyen d'utiliser des binaires mais que vous ne voulez pas utiliser les formats de distribution binaires qui existent déjà. Pourquoi donc?

Les formats bdist sont extrêmement limitatifs. Mon groupe doit recourir à un format stupide, comme tar, puis le décompresser mot pour mot (aucun des BSD n'est pris en charge, Debian n'est pas pris en charge, etc.).

Ce que j'ai découvert hier soir, c'est que l'utilisation d'un bdist stupide n'est pas installable via pip . Les binaires stupides ne disposent pas des métadonnées nécessaires pour être installés via pip , AFAICT, c'est là que je suppose que les roues de pip entrent en jeu.

J'ai également essayé egg et zip, mais il leur manque les métadonnées nécessaires à l'installation à l'aide d'un URI file:// .

J'ai essayé d'essayer de construire via distutils, setuptools dans un système de construction plus large en utilisant make, donc je ne peux pas dire si j'ai fait "tout ce qu'il fallait" pour que les choses fonctionnent comme un bdist standard

Venant de https://github.com/pypa/pip/issues/2195#issuecomment -664728481 je peux dire que je suis plus qu'heureux de refaire #7882 derrière --use-feature=in-tree-build .

Hourra! Ça ma l'air bon!

Mettons également à jour la docstring de --build cette fois. ;)

Venant de #2195 (commentaire), je peux dire que je suis plus qu'heureux de refaire #7882 derrière --use-feature=in-tree-build.

Curieux de savoir si cela aussi bien que par ligne de commande, il serait raisonnable d'avoir une option in-tree-build définie dans pyproject.toml ? Ce serait plutôt sympa pour résoudre #6276 sans avoir besoin de créer un script bash ou un makefile pour envelopper pip. (Ce n'est pas un problème particulièrement important.)

avoir une option in-tree-build définie dans pyproject.toml

@davidhewitt c'est plus ou moins l'option 3 dans la description originale de ce problème. Je crois comprendre que le consensus actuel est qu'il vaut mieux éviter une option supplémentaire si nous le pouvons. Par conséquent, l'idée d'activer les builds in-tree avec --use-feature pendant une période de transition, avec un objectif à plus long terme d'en faire le seul et unique mécanisme par défaut.

BTW, je ne pourrai pas implémenter cela à temps pour 20.3, mais j'ai toujours l'intention de le faire, j'espère en 20.4.

@sbidoul J'ai écrit un patch pour aider à

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

ncoghlan picture ncoghlan  ·  3Commentaires

therefromhere picture therefromhere  ·  3Commentaires

dstufft picture dstufft  ·  3Commentaires

jiapei100 picture jiapei100  ·  3Commentaires

imzi picture imzi  ·  3Commentaires