Pip: Resolvendo problemas relacionados a compilações fora da árvore

Criado em 4 jan. 2020  ·  68Comentários  ·  Fonte: pypa/pip

Abro esta edição como uma tentativa de consolidar a discussão sobre compilações fora da árvore, os problemas relacionados e as possíveis soluções.

Qual é o problema que esse recurso resolverá?

Ao construir projetos de diretórios locais, o pip primeiro os copia para um local temporário .

Essa abordagem levantou uma série de questões ao longo do tempo:

  • Quando setup.py / pyproject.toml não está na raiz do projeto e quando a compilação depende de recursos fora da subárvore setup.py / pyproject.toml (# 3500, # 7549, # 6276), por exemplo:

    • o build precisa de recursos que são links simbólicos para arquivos / diretórios fora da subárvore

    • build precisa do repositório git (por exemplo, ao usar setuptools_scm), e .git / está em um diretório pai não copiado para o diretório temporário por pip

    • build depende do nome do subdiretório (um pouco exótico, talvez, mas tenho um caso em que desejo criar um back-end de build personalizado e parte dos metadados depende do nome do subdiretório)

  • Problemas de desempenho quando o diretório do projeto é grande (# 2195).

Por que o pip copia para um diretório temporário antes de construir? Advertência: isso não está claro para mim - aqui está o que coletei até agora:

  • Para evitar confiar em algo fora da fonte (https://github.com/pypa/pip/issues/2195#issuecomment-524606986) - embora a definição de "fora da fonte" seja a causa de alguns problemas acima
  • Para evitar poluir o diretório de origem com artefatos de construção ou resíduos (?)
  • Algo mais?

Soluções possíveis

  1. Construa um sdist no local, descompacte o sdist em um local temporário e, em seguida, construa a partir dele.
  2. Adicione uma opção de pip para construir no local.
  3. Atualize o PEP 517 com algum tipo de mecanismo para permitir que os back-ends se comuniquem com os front-ends se forem "seguros" para compilações no local.
  4. Mude o pip para sempre construir no lugar.
  5. Altere o pip para construir no local por padrão com uma opção para construir fora da árvore.

Contexto adicional

Mais discussão sobre a construção via sdist em discuss.python.org .

needs discussion

Comentários muito úteis

Vindo de https://github.com/pypa/pip/issues/2195#issuecomment -664728481, posso dizer que estou mais do que feliz em refazer # 7882 atrás de --use-feature=in-tree-build .

Todos 68 comentários

Olhando para isso da perspectiva do back-end, a ideia de uma "construção fora da árvore" é realmente sem sentido. O back end recebe uma "árvore de código-fonte" na forma do diretório atual do processo e é solicitado a executar uma compilação. Não há como saber se esse diretório foi extraído de um sdist, retirado de um VCS ou copiado de outro lugar. Tudo o que ele pode fazer é construir e, se não tiver o que precisa para construir, relatar a falha.

Assim que praticamente mata solução possível (3) IMO - o backend não tem um conceito do que uma compilação no local é, por isso não posso dizer se tal construção é segura 1.

No que diz respeito a (2), geralmente me oponho a opções adicionais como esta. Se sabemos qual é a melhor coisa a fazer, devemos fazê-lo, e se não sabemos, então passar o problema para o usuário não é uma opção particularmente amigável. O problema aqui é sutil o suficiente para que eu esperasse que poucos usuários soubessem qual é a escolha correta, então provavelmente veríamos pessoas apenas tentando as 2 opções e usando cegamente "o que quer que funcione". Além disso, as implicações de suporte são significativas - oferecer uma opção implica claramente que esperamos que os resultados da compilação sejam diferentes em pelo menos alguns casos, então como testamos se as diferenças são as que esperávamos? Somos responsáveis ​​por instruir os usuários sobre quando o sinalizador de compilação local seria necessário ou não (por meio de nossa documentação ou como resultado de problemas levantados por usuários que não sabem qual usar)?

Dito isso, não sou contra o pip simplesmente construir no lugar. Acredito que a razão de não fazermos isso é porque tivemos casos em que artefatos remanescentes de uma compilação anterior foram usados ​​em uma compilação subsequente, mas foram construídos com opções diferentes (por exemplo, arquivos-objeto compilados no modo de depuração sendo vinculados a uma compilação de lançamento feito da mesma árvore). É, no entanto, razoável dizer que os back-ends devem garantir que problemas como esse não aconteçam e, se acontecerem, é um bug de back-end e não tem capacidade para tentar se defender contra ele. No entanto, não tenho certeza se essa postura é particularmente amigável - isso surgiu durante as discussões do PEP 517 sob o tópico de compilações incrementais e foi controverso o suficiente que foi adiado na época.

Minha preferência tem sido construir um sdist, e depois construir a roda a partir dele, há muito tempo (sua opção 1). Não tenho problemas com a construção de pip (se concordarmos que isso é seguro, visto que, como mencionei acima, não podemos esperar que o back-end nos informe), mas acho que seria necessária uma comunidade discussão para eliminar as implicações mais amplas (em back-ends, pip / front-ends e usuários finais).

1 Claro que seria possível atualizar o PEP 517 para definir o que constitui uma árvore de origem "in-loco" em oposição a uma construção "fora da árvore", mas suspeito que seria um conceito muito difícil de definir.

Eu adicionei as soluções 4. (Mude o pip para sempre construir no local) e 5. (Mude o pip para construir no lugar por padrão com uma opção para construir fora da árvore).

Tenho sentimentos confusos em relação à construção via sdist (solução 1.) pelos seguintes motivos:

  1. alguns projetos podem ter um caminho sdist-to-wheel quebrado; embora eu veja o valor de validar que a construção de sdists funciona, fazê-lo por padrão agora certamente quebrará muitas construções do usuário final
  2. ainda tem implicações de desempenho para projetos com grandes sdists e devido a chamadas de subprocesso adicionais
  3. Pessoalmente, acho que não devemos colocar muita ênfase em sdists, pois é bastante comum que o projeto publique artefatos construídos e se refira à sua plataforma de hospedagem de código favorita para seus lançamentos de código-fonte (por exemplo, back-ends que dependem da presença de um checkout VCS para edifício precisa saltar obstáculos para produzir um sdist funcional). E o PEP 517 permite especificamente que os back-ends levantem UnsupportedOperation para build_sdist .

Esta postagem sobre discussão também resume argumentos semelhantes.

Concordo que o caminho para a solução 3. está longe de ser óbvio.

Também concordo que devemos evitar opções adicionais, se possível.

Também observo que houve algumas vozes a favor de compilações no local no tópico de discussão vinculado, mas de fato uma discussão da comunidade focada nesse assunto específico é necessária se quisermos explorar essa abordagem.

  • Construa um sdist no local, descompacte o sdist em um local temporário e, em seguida, construa a partir dele.

Acho que essa é uma boa abordagem.

As chances da IMO são de que isso seria executado para um único pacote na maioria das execuções de pip install envolvendo diretórios locais - mais comumente eu imagino pip install . . Isso provavelmente seria feito como parte do fluxo de trabalho de desenvolvimento do pacote.

Aqui está o que eu acho que esse comportamento deveria ser:

  • Se o back-end não for capaz de criar um sdist, faça local-dir -> wheel (no local)

    • Acho que a responsabilidade recai sobre o back-end para garantir que local-dir -> wheel seja uma operação idempotente neste caso.

  • Se o backend é capaz de criar um sdist, faça local-dir -> sdist -> unpacked-sdist -> wheel.

Fazendo local-dir -> sdist -> wheel, temos um conjunto adicional de chamadas. No entanto, acho que é razoável validar que os sdists gerados são lógicos, especialmente durante o desenvolvimento. tox já faz isso como parte de seu fluxo de trabalho, verifique o manifesto para cobrir as interfaces não tão amigáveis ​​de setuptools aqui.

No geral, eu acho que os custos de construir um sdist quando dado um diretório local valem a pena para evitar tais erros em projetos, especialmente porque as pessoas que instalam a partir de diretórios locais são provavelmente os próprios desenvolvedores do projeto.

Com relação ao lançamento, acho que gostaríamos de esperar e ver como o # 6536 será resolvido. Definitivamente aprenderíamos algumas coisas que podem ser transferidas.

Eu prefiro construir / instalar no local sem sdist (então setup.py install ou setup.py bdist_wheel ou chamar build_wheel no back-end PEP 517 conforme aplicável) vs construir um sdist, desempacotar e instalar a partir desse. Meus motivos específicos são:

  1. Oferece suporte ao maior número de usuários pronto para uso.

    1. Suponha que o pip faça local-dir -> installed (via wheel build in-place ou setup.py install ). Os usuários que desejam ir de local-dir -> instalado podem executar pip install local-dir . Os usuários que desejam ir de local-dir -> sdist -> installed são livres para criar um sdist e então executar pip install ./path/to/sdist . Existem muitas ferramentas alternativas que podem construir um sdist, e isso é algo que os usuários provavelmente já terão, a menos que estejam elaborando manualmente as distribuições que carregam para o PyPI.

    2. Agora assuma que o pip faz local-dir -> sdist -> installed. Os usuários que desejam ir de local-dir -> installed não têm opções que envolvam pip. Os usuários que desejam ir de local-dir -> sdist -> installed podem executar pip install local-dir . Os usuários sem opções pedirão ao pip opções para controlar o comportamento ou terão que encontrar outra ferramenta que de outra forma não precisariam.

  2. Se implementarmos local-dir -> sdist -> installed, presumivelmente também faríamos isso para requisitos baseados em VCS. Se for assim, dá mais trabalho. Caso contrário, são caminhos adicionais através do código e desvios na maneira como a instalação é tratada que devem ser lembrados pelos usuários ou ao fornecer suporte.
  3. Menor quantidade de trabalho para implementar. Existem três lugares para alterar para implementar local-dir -> installed ( aqui , aqui e aqui ). Para local-dir -> sdist -> installed, eu nem gostaria de implementar isso até que # 6607 esteja pronto, caso contrário, acho que iria acabar em muitos lugares na base de código semelhante ao download de artefato / verificação de hash.
  4. Menos trabalho para testar desde, IMO, os testes existentes são suficientes para cobrir o local-dir -> caminho do código instalado. Para o caminho local-dir -> sdist -> installed, gostaríamos de verificar se estamos realmente construindo um sdist e se os substitutos funcionam para construir uma roda diretamente.
  5. Menor quantidade de trabalho (computacionalmente). Como mencionado em outro lugar, local-dir -> sdist -> installed é uma chamada de subprocesso adicional (e esse subprocesso está funcionando). Isso também significa que o pip precisa descompactar o sdist (hello virus scanners e outros discos lentos) antes de fazer o wheel build.

Independentemente da abordagem, o único problema que vejo ao fazer as coisas no local é que para compilações de ferramentas de instalação (acho que isso se aplica a legado e PEP 517), acabaríamos com .egg-info no diretório do projeto, o que se confundirá como um "pacote instalado" quando o pip é invocado com python -m pip naquele diretório. Isso seria corrigido por # 4575 que presumivelmente NÃO incluiria o diretório atual na consulta de pacotes instalados para qualquer esquema.

Observando que comecei a concordar que a ideia de pular a compilação sdist e fazer diretamente uma compilação na árvore é uma abordagem melhor para o pip tomar por padrão, e não tentar fazer local-dir -> sdist -> wheel.

No Fedora, quando construímos pacotes Python RPM, somos dinossauros e a maneira padrão de fazer isso é usar python setup.py build . Com o PEP 517, adicionamos uma forma "provisória" de usar pip wheel . No entanto, com os módulos de extensão, temos um problema com a abordagem "mover fontes para tmp, compilar a partir daí" que o pip usa para criá-los.

Nossa máquina de construção está injetando alguns sinalizadores de compilador para que os artefatos de construção (módulos de extensão .so neste caso) contenham metadados sobre suas fontes. Posteriormente, há um script de shell que percorre os artefatos de construção, extrai essas informações e copia as fontes para /usr/src/debug para ser instalado por meio de um *-debugsource RPM especial. A mahcinery espera que tudo seja construído dentro da árvore de trabalho e realmente não funciona bem quando construído fora. Aqui estão as coisas que podem ser feitas (juntos) para mitigar o problema do nosso lado:

  1. defina a variável de ambiente $TMPDIR para tê-lo dentro do lugar onde o script RPM espera (ou seja, export TMPDIR=%{_builddir}/.tmp (e criá-lo))
  2. use pip wheel com a opção --no-clean para manter as fontes copiadas em $TMPDIR
  3. execute algum shell kung fu para reescrever as informações de "qual é minha fonte" no local correto: 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: limpe $TMPDIR manualmente.)

Não gostamos particularmente da terceira etapa porque ela depende de muitos detalhes de implementação:

  • /usr/lib/rpm/debugedit API e localização (e existência)
  • o pip-req-build name
  • o mecanismo com o qual o pip constrói as fontes

Se pip sempre fosse compilado no local ou se houvesse uma opção de linha de comando para isso, o problema iria embora.

Relatório downstream: https://bugzilla.redhat.com/show_bug.cgi?id=1806625

Observando que comecei a concordar que a ideia de pular a compilação sdist e fazer diretamente uma compilação na árvore é uma abordagem melhor para o pip tomar por padrão, e não tentar fazer local-dir -> sdist -> wheel.

Também estou ficando mais inclinado a aceitar a ideia de fazer uma construção de roda no local. Minhas reservas restantes são:

  1. Estaríamos contando com o comportamento do back-end "corretamente" - por exemplo, não fornecendo resultados diferentes com base em dados remanescentes de compilações anteriores ou qualquer outra coisa. Estou bem em fazer essa suposição, mas estou preocupado com o custo do suporte se começarmos a receber as pessoas dizendo "pip construiu minha roda incorretamente" e tivermos que depurar apenas para descobrir que é um problema de back-end.
  2. Acho que, qualquer que
  3. Como corolário do acima exposto, devemos ter certeza de que não há projetos que conhecemos que dependam de nossa abordagem atual de "copiar e construir", pois se houver, estaremos quebrando-os com essa mudança.

... e é claro que alguém precisará escrever um PR implementando essa mudança (com testes, documentos, etc - o material usual), caso contrário, tudo o que estamos fazendo é conversando 🙂

Estaríamos contando com o comportamento do back-end "corretamente" - por exemplo, não fornecendo resultados diferentes com base em dados remanescentes de compilações anteriores ou qualquer outra coisa. Estou bem em fazer essa suposição, mas estou preocupado com o custo do suporte se começarmos a receber as pessoas dizendo "pip construiu minha roda incorretamente" e tivermos que depurar apenas para descobrir que é um problema de back-end.

Faria sentido estender a interface PEP 517 para incluir um gancho “limpo”? Provavelmente quereríamos isso como um ponto de qualquer maneira para permitir outros empreendimentos (por exemplo, implementar instalação editável, construção de uma ferramenta de desenvolvimento de pacote que constrói qualquer projeto PEP 517). pip pode chamá-lo aqui para se certificar de que não há sobras de lixo antes de fazer a compilação na árvore.

Faria sentido estender a interface PEP 517 para incluir um gancho “limpo”?

Pode ser? Mas se pip chamar automaticamente clean , deve haver alguém que não quer fazer isso, fazer builds incrementais ou algo assim. E então acabamos com outra opção.

Minha inclinação é manter minha posição "precisamos ser capazes de assumir que é responsabilidade do back-end garantir que as compilações no local funcionem corretamente". Mesmo que isso se revele insustentável, obter exemplos concretos de por que isso não funciona nos ajudará a entender melhor o que fazer sobre o problema, em vez de apenas adivinhar.

  1. Estaríamos contando com o comportamento do back-end "corretamente" - por exemplo, não fornecendo resultados diferentes com base em dados remanescentes de compilações anteriores ou qualquer outra coisa.

Eu ficaria tentado a estender o PEP-517 para tornar isso um requisito explícito.

Eu ficaria tentado a estender o PEP-517 para tornar isso um requisito explícito.

Já diz isso:

O backend pode armazenar artefatos intermediários em locais de cache ou diretórios temporários. A presença ou ausência de quaisquer caches não deve fazer uma diferença material no resultado final da construção.

Não é tanto que os back-ends possam violar esse requisito deliberadamente, pois os usuários irão naturalmente relatar o problema como um problema de pip e serem redirecionados para o projeto de back-end, o que é um pouco de sobrecarga extra.

Ao tentar descobrir uma solução alternativa para o Fedora, fomos atingidos por https://github.com/pypa/pip/issues/7872

Como resolvemos o problema ".egg-info in cwd" com # 7731 e amigos, isso é uma coisa a menos com que se preocupar ao construir no local.

Portanto, a opção 4 (sempre construída no local) foi implementada em # 7882.

Agora (por # 7951) publicamos uma versão beta do pip, pip 20.1b1. Esta versão inclui o nº 7882, que implementou uma solução para esse problema.

Espero que os participantes desta edição nos ajudem testando o beta e verificando se há novos bugs. Gostaríamos de identificar e solucionar quaisquer problemas em potencial antes do lançamento principal do 20.1 na terça-feira.

Também agradeço o feedback positivo ao longo das linhas de "yay, funciona melhor agora!" também, uma vez que o rastreador de problemas geralmente está cheio de "problemas". :)

Estamos totalmente planejando testá-lo no Fedora (já planejamos isso antes do seu comentário), no entanto, o prazo de terça-feira provavelmente não é realista.

@hroncok Tem alguma ideia de quando o Fedora poderá testar essas mudanças?

Vou tentar o nosso melhor para fazer de alguma forma na segunda-feira, mas não posso fazer promessas.

Na verdade, 20.1b1 faz com que nossos problemas desapareçam.

Feedback 20.1b1 mais geral em https://mail.python.org/archives/list/[email protected]/message/5EAUIYYIRKXEHTAG5GQ7EJHSXGZIW2F7/

Viva! Muito obrigado por experimentar o beta @hroncok! Muito apreciado! ^> ^

Um resultado da construção no local: eu estava construindo rodas para várias versões do python em paralelo (dentro de um contêiner docker manylinux). Com compilações in-loco, compilações paralelas não funcionam porque o conflito de versões diferentes. Com compilações fora da árvore, cada versão criou uma árvore separada e não teve problemas.

@manthey essa discussão está em # 8168

Então, já se passaram mais de 10 dias. Houve alguns problemas levantados sobre a mudança (todos esperados, eu diria - # 8165, # 8168, # 8196). Também houve pessoas que mencionaram explicitamente que a mudança os está ajudando.

  • Além de problemas de desempenho, o comportamento anterior (copiar para dir temporário) tinha problemas de correção (vinculados acima) que são impossíveis de corrigir por pip sem conhecimento de contexto que apenas o chamador tem (e, como uma nota lateral, esse código de copytree já estava cheio de band-aids para lidar com situações estranhas - tmpdir em árvore, sockets, etc).
  • Uma opção para ativar o comportamento anterior ainda teria os problemas de correção e desempenho.
  • Uma solução correta envolverá a construção de suporte de back-end para controlar o diretório de construção que não existe totalmente hoje (por exemplo, setuptools bdist_wheel tem --bdist-dir , mas ainda escreve .egg-info em local, consulte também https://github.com/pypa/setuptools/issues/1816, https://github.com/pypa/setuptools/issues/1825). Portanto, agora que o pip se comporta corretamente, talvez a discussão possa mudar para ver se, por exemplo, o setuptools pode desenvolver uma opção para fazer uma construção sem tocar no diretório de origem e, em seguida, verificar se uma alteração PEP 517 é necessária ou não para controlar essa opção.
  • Enquanto isso, os problemas relatados são provavelmente relativamente fáceis de contornar pelos chamadores (por exemplo, copiando-se para um diretório temporário ou criando um tarball temporário, o que eles podem fazer corretamente com pleno conhecimento do contexto).
  • Por último, é difícil ter certeza com os dados que temos, mas minha intuição é que essa mudança ajuda mais pessoas do que machuca.

Por isso, odeio interromper alterações, mas essa não foi feita levianamente e, por esses motivos, estou pessoalmente inclinado a mantê-lo.

Não concordo que o novo comportamento pip esteja se comportando "corretamente", de forma diferente, com certeza, e aparentemente em alguns casos com falhas diferentes e acho que enquadrá-lo como tal é incorreto. Isso representa uma compensação para um conjunto de usuários interrompidos para um conjunto diferente; em ambos os casos, havia soluções alternativas que poderiam ser feitas.

Eu não teria mesclado essa mudança e deixado de acontecer ou teria argumentado contra ela (e acho que agora, tendo mesclado, isso torna alguns usar o pip como uma função de força para ajudar a prevenir certos tipos de pacotes quebrados significativamente mais difícil ) Dito isso, não sei se reverter é a coisa certa aqui. Pode ficar ainda mais confuso para os usuários se o comportamento mudar muito. Se vamos reverter, devemos fazê-lo rapidamente; do contrário, o comportamento atual provavelmente deve ser melhor ou pior.

Usei o termo "corretamente", porque antes de pip wheel <localdir> e pip install <localdir> estavam gerando uma roda diferente de cd <localdir> ; setup.py bdist_wheel em alguns casos: com diferentes arquivos ausentes na presença de links simbólicos (# 3500), ou uma versão diferente com setuptools_scm (# 7549) ou https://github.com/pypa/pip/issues/7555#issuecomment -595180864 ou # 6276 ou erros simples. Não acho que o pip 20.1 gere essas rodas / instalações ruins, então, nesse sentido, acredito que seja realmente mais correto.

É claro que sabíamos que a mudança interromperia alguns fluxos de trabalho e a compensação precisa ser reavaliada agora que temos feedback e ser revertida ou confirmada para sempre.

E ainda pode produzir rodas diferentes de setup.py sdist && pip install dist/*.tar.gz .

Minha sugestão seria reverter o PR e implementar a correção, primeiro embaralhando um sdist e, em seguida, construindo uma roda a partir do sdist resultante.

Isso deve resolver todos os problemas de correção, exceto nos casos em que o projeto é incapaz de construir corretamente um sdist e, isto é, IMO, não é um caso de uso importante a ser resolvido.

A desvantagem é que será mais lento. No entanto, depois de implementado, podemos refinar ainda mais a interface PEP 517 para adicionar APIs opcionais que permitem acelerações. Provavelmente, isso nunca será tão rápido quanto essa mudança, mas certamente podemos chegar mais perto disso.

Esta mudança torna efetivamente impossível forçar ainda mais a correção sem introduzir regressões de desempenho com as quais os usuários provavelmente não ficarão satisfeitos. No entanto, se fizermos isso correto e, em seguida, melhorarmos com desempenho, podemos chegar a um meio-termo feliz que satisfaça ambas as partes.

Como diz o velho ditado, primeiro corrija, depois torne-o rápido. Temo que com este PR o tenhamos tornado rápido e bloqueado nossa capacidade de corrigi-lo.

Ainda concordo que validar sdists é desejável, mas não no momento da instalação do pip. Talvez seja um recurso para uma futura ferramenta sdist builder ou comando pip build.

Além disso, setup.py sdist cria .egg-info no diretório local, de modo que os problemas relatados com diretório de origem somente leitura ou compilações simultâneas permaneceriam.

Se isso não acontecer no momento da instalação do pip, funcionalmente não acontecerá até o momento da instalação do pip de outra pessoa. Ignorá-lo significa apenas que temos vários caminhos que um projeto pode seguir para ir do VCS ao pacote instalado e cada caminho é uma outra chance para diferenças. Isso não é uma coisa nova, basicamente cada opção que temos que altera o caminho de instalação acaba com um conjunto diferente de bytes no disco, mesmo para os projetos mais rigorosos. Sempre existem diferenças sutis e você só espera que essas diferenças não sejam significativas - ou você pode fazer o que puder para remover essas diferenças, tornando estruturalmente impossível tê-las desde o início.

Alguns problemas de desempenho podem realmente reaparecer se / durante a construção via sdist, mas provavelmente seriam uma ordem de magnitude menor do que o que tínhamos no pip <20.1. Na verdade, a maior parte disso muitas vezes veio da cópia de .git , ou venv , ou outro material volumoso não relacionado que não estaria no sdist.

Independentemente de com que pip acabe, poderíamos fazer do outro uma opção, já que é improvável que qualquer um seja capaz de satisfazer a todos? Eu imagino que se a abordagem atual for mantida (eu realmente não tenho uma opinião sobre qual deveria ser o padrão), devemos ser capazes de fornecer um fallback de último resultado onde um usuário pode escolher criar um sdist e instalar o pacote de lá.

Além disso, setup.py sdist cria .egg-info no diretório local, de modo que os problemas relatados com diretório de origem somente leitura ou compilações simultâneas permaneceriam.

Eu acho (pelo menos um teste rápido concorda comigo) que apenas setuptools (não distutils ) faz isso, e este comportamento é configurável para criar o diretório em outro lugar. Semelhante a outros back-ends, devemos ser capazes de recomendá-los para fazer uma geração sdist limpa .

FWIW, não acho que precisaríamos despejar o diretório sdist-generation --egg-info no diretório de trabalho, se seguirmos a abordagem generate-sdist-unpack-it-build-wheel, já que podemos despeje isso em um diretório temporário, como fazemos para generate_metadata .

@pradyunsg isso não requer uma mudança nas sdist não tinha a opção de especificar a localização de base .egg-info , ao contrário de egg_info que tem uma opção --egg-base que aproveitamos em # 7978.

De fato! Eu estava olhando para o arquivo errado nas ferramentas de instalação. 🙈 Eu estou corrigido.

Por que tudo é tão complexo neste espaço? :(

$  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 parece um bug bonito de parar o show, sem dúvida não é o nosso bug, mas é um tipo de bug que eu argumentei que aconteceria durante a discussão PEP 517 quando fazer compilações no local por padrão surgiu.

bdist_wheel foi solicitado a limpar seu diretório de compilação automaticamente no passado. Esse recurso deve ser incluído. As outras compilações de distutils estão limpas?

Se fosse o SCons, ele se lembraria dos arquivos importantes e omitiria arquivos extras no diretório build / da roda, mesmo se eles estivessem presentes no sistema de arquivos.

Eu acredito que o problema acima não afeta apenas o manylinux. Isso deve acontecer sempre que o diretório de construção não for específico o suficiente para capturar a ABI (no caso do setuptools, parece plataforma e versão python é tudo o que é capturado na tag ABI no diretório de construção). Eu acho que isso se estende além de ABI com o interpretador atual também, se algo se vincula a NumPy por exemplo, eu acho que tem uma ABI que funcionará em NumPy mais recente, mas não mais antigo, e a menos que eles codifiquem isso na nomenclatura do diretório de construção, então efeito usa assim também.

Limpar automaticamente o diretório de compilação não resolve o problema, apenas o torna menos provável (por exemplo, executar duas invocações pip wheel em paralelo ainda pode desencadear o problema), além de um dos supostos motivos para a implementação desta forma (pelo menos durante a discussão PEP 517) era que isso forneceria mais desempenho, permitindo o armazenamento em cache entre as chamadas para compilações incrementais. IOW, o comportamento atual é o que algum subconjunto queria, reutilizando artefatos de compilação entre as execuções. Acontece que, de longe, o back-end de compilação mais comum fica muito errado (e sem dúvida em alguns casos não tem informações suficientes para acertar sem personalização do pacote).

Claro, com sinalizadores suficientes para o comando setuptools subjacente, você pode remediar isso (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 resolveria).

Eu reiteraria, porém, que o problema não são os arquivos extras, mas a ABI esperada com a qual a roda era compatível mudou e o .so não foi reconstruído. Se o SCons for inteligente o suficiente para saber que o Python construído com pymalloc precisa de um diretório de construção e o Python construído com outro (incluindo coisas como versões NumPy para as quais .so pode se vincular), então ele não é afetado. Se fosse reutilizar um artefato criado anteriormente com uma ABI diferente, ele seria afetado.

Tentei testar enscons, mas não consegui fazer o rsalette construir sem erros.

Eu tentei testar o scikit-build para ver como ele lidava com compilações incrementais, e não importa o que eu fizesse, apenas vomitou na segunda compilação e tive que excluir manualmente o diretório _skbuild cada vez para colocá-lo executado sem erros.

Legal. Infelizmente, o enscons foi atualizado e o rsalette não.

Na quarta-feira, 6 de maio de 2020, às 16h18, Donald Stufft escreveu:

Tentei testar enscons, mas não consegui fazer o rsalette construir sem erros.

Eu tentei testar o scikit-build para ver como ele lidava com compilações incrementais, e não importa o que eu fizesse, apenas vomitou na segunda compilação e tive que excluir manualmente o diretório _skbuild cada vez para colocá-lo executado sem erros.

-
Você está recebendo isto porque comentou.
Responda a este e-mail diretamente, visualize-o no GitHub https://github.com/pypa/pip/issues/7555#issuecomment-624867490 ou cancele a inscrição https://github.com/notifications/unsubscribe-auth/AABSZERIEDAPUIXCPAKBBUDRQHAXRANCNFSM4KCV5MHQ .

Infelizmente, o enscons foi atualizado e o rsalette não.

Existe um bom ext ext que usa enscons que é atualizado? Só escolhi o rsalette porque era o primeiro da lista e não queria depurá-lo, fico feliz em tentar outra coisa.

O único problema com rsalette é que ele não deve passar ROOT_IS_PURELIB para o ambiente em SConstruct. Não tem extensão C. cryptacular parece bem.

# 8165 (comentário)

Com isso, acho que estou de acordo que devemos reverter essa mudança.

Acho que os novos problemas são muito maiores do que o previsto. Talvez um 20.1.1 rápido para reverter e então poderemos ter uma discussão mais longa sobre como resolver os problemas de compilações dentro e fora da árvore?

Também voto por reverter e buscar https://discuss.python.org/t/proposal-adding-a-persistent-cache-directory-to-pep-517-hooks/2303/15 como uma solução para isso (isso permitir que back-ends não sejam compilados no local, portanto, não exponha esses problemas). Chim neste tópico também se você concordar com a sugestão lá.

Isso também parece sensato para mim. Eu acho que uma abordagem em árvore (ou build-from-sdist) tem alguns benefícios extremamente significativos (tenho certeza que vamos receber reclamações de parte da base de usuários quando revertermos 🙂), mas as desvantagens também são significativo.

Não tenho certeza de qual interface deve ser aqui (padrão para qual abordagem? Que tipo de opções devemos ter?), Mas acho que devemos levar um pouco mais de tempo para decidir isso, em vez de tomar decisões enquanto combatemos os problemas atuais .

Tudo bem! Acho que o consenso geral é reverter e reavaliar. Vou registrar um PR para isso. :)

Chim neste tópico também se você concordar com a sugestão lá.

Por favor, faça - estive comentando, mas cheguei ao ponto em que não sei o suficiente para oferecer sugestões significativas, portanto, contribuições de pessoas com mais experiência seriam valiosas.

Acabei de deixar cair um par de grandes bolhas de texto em https://github.com/pypa/pip/issues/8165#issuecomment -625401463. Vou me afastar por hoje ... Acabei me sentindo um pouco frustrado enquanto escrevia as notas pessoais no final. Terminar no # 5599 e ler comentários negativos de usuários certamente não ajudou.

Olá pessoal, pensei mais sobre isso, aqui está meu ponto de vista atual sobre o assunto.

  1. Construa um sdist no local, descompacte o sdist em um local temporário e, em seguida, construa a partir dele.

Eu ainda acredito que pip install / pip wheel não é o lugar certo para tentar pegar sdists ruins. Isso não deveria ser uma responsabilidade de back-end para não criar sdists ruins em primeiro lugar? Além disso, eu acho que a compilação incondicional via sdist é provavelmente tão perturbadora quanto a compilação local.

  1. Adicione uma opção de pip para construir no local.

O que eu gosto mais de curto prazo, já que a solução 4 não deu certo. É prematuro adicionar isso no pip 20.1.1?

  1. Atualize o PEP 517 com algum tipo de mecanismo para permitir que os back-ends se comuniquem com os front-ends se forem "seguros" para compilações no local.

Com este pip ainda precisaria voltar para sua árvore de cópia quebrada e não corrigível, então eu não sou a favor deste.

  1. Mude o pip para sempre construir no lugar.

Portanto, este é considerado muito perturbador e reverteremos em 20.1.1.

  1. Altere o pip para construir no local por padrão com uma opção para construir fora da árvore.

Esse poderia ser o objetivo de longo prazo, a opção de construir uma combinação fora da árvore com o conceito de diretório de cache?

Eu realmente não gosto das opções de CLI, especialmente aquelas como esta. E se eu listar dois pacotes que estão em meu FS local e precisar construir um no local e outro não? Se fornecermos a opção de fazer um ou outro, acabaremos com os pacotes existentes que só podem ser construídos com um ou outro.

Também me parece o tipo de opção que existe inteiramente porque um projeto não conseguiu chegar a uma decisão e decidiu simplesmente empurrar essa decisão para o usuário final.

Construir via sdist não é exatamente pegar sdists ruins. Em grande parte trata-se de reduzir as possíveis variações de "caminho" que um projeto pode percorrer antes de terminar instalado. Adicionar um sinalizador de algumas maneiras torna o problema pior, e não melhor.

O que quero dizer é que temos alguns "caminhos" pelos quais as instalações podem seguir:

  1. VCS -> Sdist -> Roda -> Instalado
  2. VCS -> Roda -> Instalado
  3. VCS -> Instalado (Legado)

Existem alguns caminhos adicionais que estão sendo eliminados, mas em geral esses são os nossos 3 (e de preferência, o 3 também será eliminado). Também há instalações editáveis, mas elas não serão removidas tão cedo.

Podemos considerar um sdist ou uma roda sendo carregada para PyPI e instalada a partir daí até fazer parte do mesmo "caminho", você está apenas pausando e finalizando em outro computador.

O problema de ter vários "caminhos" como esse é que introduz inconsistências na instalação final resultante. Eles nem sempre acontecem, mas é um caso facilmente observável que acontece com frequência. Muitas vezes, essas inconsistências não são grande coisa, mas às vezes são.

Se estivermos fazendo builds no local como este, estamos efetivamente dizendo que nunca seremos capazes de colapsar em um único caminho, e que sempre teremos que lidar com esse estranho caso extremo em que às vezes as pessoas obterão resultados diferentes com base em como a instalação foi feita.

Como um benefício adicional, isso também pode atuar como uma função de força para ajudar a garantir que o caminho da felicidade continue feliz.

Em geral, eu concordo com @dstufft e, em particular, concordo que a abordagem build-from-sdist deve ser vista não como "tentando validar sdists", mas como "tudo segue a" árvore de origem -> sdist -> roda -> rota de instalação (apenas algumas coisas ignoram alguns passos iniciais) ".

No entanto, quero pegar em um ponto:

E se eu listar dois pacotes que estão em meu FS local e precisar construir um no local e outro não?

Basta executar os dois pacotes em duas execuções separadas de pip com opções diferentes?!? Eu sei que é possível que um seja uma dependência do outro e seu ponto em geral é válido, mas parece haver uma tendência geral para as pessoas presumirem que todo cenário de instalação deve ser reduzido em uma única execução de pip, e eu não acho que é razoável (tivemos soluções perfeitamente boas para problemas serem rejeitados pelo usuário porque "isso significa que eu teria que dividir minha lista de requisitos em dois")

Observe que quando (se) revertermos, precisaremos reabrir problemas como o # 6276, que foi fechado como resultado da implementação de compilações na árvore.

Parte do problema é que o pip não considera o que já está instalado ao resolver dependências (não tenho certeza se o novo resolvedor de trabalho muda isso?), Então você precisa ter tudo contido em uma única invocação de pip se quiser resolver dependências "corretamente" (na medida em que nosso resolvedor atual faz algo corretamente).

Se o novo resolvedor levar em conta o que já está instalado, pip install foo bar e pip install foo && pip install bar seriam praticamente iguais e não teriam nenhuma importância, mas se não o fizessem (e o mesmo é praticamente verdade agora) se ambos os projetos dependessem de "spam", mas foo required <2 and bar required> 1 então teríamos uma instalação inválida.

No entanto, é uma tangente :)

(Não tenho certeza se o novo trabalho de resolução muda isso?)

As entradas são bem-vindas em # 7744. :)

  1. Mude o pip para sempre construir no lugar.

Portanto, este é considerado muito perturbador e reverteremos em 20.1.1.

Para ser claro, também é porque "implementamos muito rápido" e a abordagem de implementação que adotamos é definitivamente parte do motivo pelo qual isso acabou sendo muito perturbador.

  1. Adicione uma opção de pip para construir no local.

@dstufft @pfmoore Eu vejo esse tipo de opção como um mecanismo opcional para que possamos empurrar progressivamente os usuários para construções no local com o objetivo de torná-lo o padrão em algum ponto. Seguindo o espírito deste comentário: https://github.com/pypa/pip/issues/8165#issuecomment -625501216

Vou registrar um PR para isso. :)

8221 então.

20.1.1 foi lançado, contendo as alterações revertidas.

No Fedora, quando construímos pacotes Python RPM, somos dinossauros e a maneira padrão de fazer isso é usar python setup.py build . Com o PEP 517, adicionamos uma forma "provisória" de usar pip wheel . No entanto, com os módulos de extensão, temos um problema com a abordagem "mover fontes para tmp, compilar a partir daí" que o pip usa para criá-los.

Nossa máquina de construção está injetando alguns sinalizadores de compilador para que os artefatos de construção (módulos de extensão .so neste caso) contenham metadados sobre suas fontes. Posteriormente, há um script de shell que percorre os artefatos de construção, extrai essas informações e copia as fontes para /usr/src/debug para ser instalado por meio de um *-debugsource RPM especial. A mahcinery espera que tudo seja construído dentro da árvore de trabalho e realmente não funciona bem quando construído fora. Aqui estão as coisas que podem ser feitas (juntos) para mitigar o problema do nosso 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 é efetivamente o caminho que eu comecei ao examinar a integração do pip, # 6505, etc.

Compilações iterativas com pip estão efetivamente interrompidas hoje, o que é uma grande perda para grupos que têm uma grande quantidade de código python na forma de extensão C, portanto, recorri a invocar a compilação com setup.py .

pip precisa de um comando build e o resultado final do comando build deve ser transmitido a outros subcomandos, como wheel , install etc.

No momento pip trata efetivamente install como build e install , que _não_ é o que algumas pessoas querem que estão construindo e armazenando em cache artefatos binários, instalando binários durante a leitura - apenas montagens, etc.

Eu realmente gostaria que houvesse uma maneira de usar setup.py para construir os binários e, em seguida, pip install sem recorrer à criação de bdist , mas isso não parece ser possível hoje , uma vez que pip e distutils / setuptools não concordam sobre onde encontrar os artefatos binários.

Eu realmente gostaria que houvesse uma maneira de usar setup.py para construir os binários, então pip instalá-los sem recorrer à criação de um bdist, mas isso não parece ser possível hoje, uma vez que pip e distutils / setuptools não concordam com onde encontrar os artefatos binários.

Não tenho certeza se entendi - você está dizendo que quer uma maneira de usar binários, mas não quer usar os formatos de distribuição de binários que já existem. Por que é que?

Eu realmente gostaria que houvesse uma maneira de usar setup.py para construir os binários, então pip instalá-los sem recorrer à criação de um bdist, mas isso não parece ser possível hoje, uma vez que pip e distutils / setuptools não concordam com onde encontrar os artefatos binários.

Não tenho certeza se entendi - você está dizendo que quer uma maneira de usar binários, mas não quer usar os formatos de distribuição de binários que já existem. Por que é que?

Os formatos bdist são extremamente limitantes. Meu grupo tem que recorrer a um formato idiota, como tar, então descompactá-lo literalmente (nenhum dos BSDs são suportados, Debian não é suportado, etc).

O que descobri ontem à noite é que usar um bdist burro não pode ser instalado por meio de pip . Os binários burros não têm os metadados necessários para serem instalados por meio de pip , AFAICT, que é onde eu acho que as rodas pip entram em ação.

Tentei o egg e zip também, mas faltam os metadados necessários para instalar usando apenas um URI file:// .

Eu tenho me enganado tentando construir uma calçadeira por meio de distutils, ferramentas de configuração em um sistema de construção maior usando make, então não posso afirmar se fiz "todas as coisas certas" para fazer as coisas funcionarem da maneira que um bdist padrão

Vindo de https://github.com/pypa/pip/issues/2195#issuecomment -664728481, posso dizer que estou mais do que feliz em refazer # 7882 atrás de --use-feature=in-tree-build .

Viva! Soa como um plano!

Vamos também atualizar a docstring de --build desta vez. ;)

Vindo de # 2195 (comentário), posso dizer que estou mais do que feliz em refazer o # 7882 atrás de --use-feature = in-tree-build.

Curioso se, além da linha de comando, seria razoável ter uma opção in-tree-build definida em pyproject.toml ? Isso seria muito bom para resolver # 6276 sem a necessidade de fazer um script bash ou makefile para embrulhar pip. (Não que isso seja um problema particularmente grande.)

tem uma opção de construção na árvore definida em pyproject.toml

@davidhewitt esta é mais ou menos a opção 3 na descrição original deste problema. Meu entendimento é que o consenso atual é que é melhor evitar uma opção adicional, se possível. Portanto, a ideia de habilitar compilações na árvore com --use-feature durante um período de transição, com um objetivo de longo prazo de torná-lo o padrão e único mecanismo.

A propósito, não serei capaz de implementar isso a tempo para 20.3, mas ainda pretendo fazê-lo, espero que em 20.4.

@sbidoul Escrevi um patch para ajudar a obter esse recurso - consulte # 9091.

Esta página foi útil?
0 / 5 - 0 avaliações