Pip: O novo resolvedor leva muito tempo para ser concluído

Criado em 30 nov. 2020  ·  79Comentários  ·  Fonte: pypa/pip

O que você queria fazer?

Um dos trabalhos de CI para Weblate é instalar versões mínimas de dependências. Usamos o construtor de requisitos para gerar os requisitos mínimos de versão dos intervalos que usamos normalmente.

O comando pip install -r requirements-min.txt parece fazer um loop infinito depois de algum tempo. Isso começou a acontecer com o 20.3, antes de funcionar bem.

Saída

Requirement already satisfied: google-auth<2.0dev,>=1.21.1 in /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages (from google-api-core[grpc]<2.0.0dev,>=1.22.0->google-cloud-translate==3.0.0->-r requirements-min.txt (line 63)) (1.23.0)
Requirement already satisfied: pytz>dev in /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages (from celery[redis]==4.4.5->-r requirements-min.txt (line 3)) (2020.4)
Requirement already satisfied: googleapis-common-protos<2.0dev,>=1.6.0 in /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages (from google-api-core[grpc]<2.0.0dev,>=1.22.0->google-cloud-translate==3.0.0->-r requirements-min.txt (line 63)) (1.52.0)
Requirement already satisfied: six>=1.9.0 in /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages (from bleach==3.1.1->-r requirements-min.txt (line 1)) (1.15.0)
Requirement already satisfied: protobuf>=3.12.0 in /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages (from google-api-core[grpc]<2.0.0dev,>=1.22.0->google-cloud-translate==3.0.0->-r requirements-min.txt (line 63)) (3.14.0)
Requirement already satisfied: grpcio<2.0dev,>=1.29.0 in /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages (from google-api-core[grpc]<2.0.0dev,>=1.22.0->google-cloud-translate==3.0.0->-r requirements-min.txt (line 63)) (1.33.2)
Requirement already satisfied: google-auth<2.0dev,>=1.21.1 in /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages (from google-api-core[grpc]<2.0.0dev,>=1.22.0->google-cloud-translate==3.0.0->-r requirements-min.txt (line 63)) (1.23.0)
Requirement already satisfied: pytz>dev in /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages (from celery[redis]==4.4.5->-r requirements-min.txt (line 3)) (2020.4)
Requirement already satisfied: googleapis-common-protos<2.0dev,>=1.6.0 in /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages (from google-api-core[grpc]<2.0.0dev,>=1.22.0->google-cloud-translate==3.0.0->-r requirements-min.txt (line 63)) (1.52.0)
Requirement already satisfied: six>=1.9.0 in /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages (from bleach==3.1.1->-r requirements-min.txt (line 1)) (1.15.0)
Requirement already satisfied: protobuf>=3.12.0 in /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages (from google-api-core[grpc]<2.0.0dev,>=1.22.0->google-cloud-translate==3.0.0->-r requirements-min.txt (line 63)) (3.14.0)

Isso parece se repetir para sempre (bem por 3 horas até agora, veja https://github.com/WeblateOrg/weblate/runs/1474960864?check_suite_focus=true)

Informações adicionais

Arquivo de requisitos que aciona isso: requirements-min.txt

Demora algum tempo até chegar ao loop acima. Provavelmente há algo problemático no conjunto de dependências ...

new resolver crash

Comentários muito úteis

Obrigado, @dstufft.

Mencionarei aqui algumas dicas úteis de solução alternativa da documentação - em particular, o primeiro e o terceiro pontos podem ser úteis para as pessoas que chegam aqui:

  • Se o pip estiver demorando mais para instalar os pacotes, leia Backtracking da resolução de dependências para obter maneiras de reduzir o tempo que o pip gasta no backtracking devido a conflitos de dependência.

  • Se você não quiser que o pip realmente resolva as dependências, use a opção --no-deps . Isso é útil quando você tem um conjunto de versões de pacotes que funcionam juntas na realidade, mesmo que seus metadados digam que eles são conflitantes. Para obter orientação sobre uma correção de longo prazo, leia Corrigindo dependências conflitantes .

  • Se você encontrar erros de resolução e precisar de uma solução alternativa enquanto estiver corrigindo suas causas raiz, poderá escolher o comportamento antigo do resolvedor usando o sinalizador --use-deprecated=legacy-resolver . Isso funcionará até lançarmos o pip 21.0 (consulte Linha do tempo de descontinuação).

Todos 79 comentários

Vou usar esse problema para centralizar os relatórios de entrada de situações que aparentemente são executadas por um longo tempo, em vez de cada um terminar em seu próprio problema ou espalhado.

@jcrist disse em https://github.com/pypa/pip/issues/8664#issuecomment -735961391

_Nota: fui convidado a comentar aqui sobre nossa experiência do twitter ._

Nós ( prefeito ) estamos um pouco atrasados ​​no teste do novo resolvedor (só conseguindo com a versão 20.3). Estamos descobrindo que os tempos de instalação estão agora na faixa de mais de 20 minutos (na verdade, nunca tive uma finalização), anteriormente isso era no máximo um minuto ou dois. O problema aqui parece estar no grande espaço de pesquisa (o prefeito tem muitas dependências opcionais, para CI e algumas imagens do docker instalamos todas elas) juntamente com o retrocesso.

Habilitei logs detalhados para tentar descobrir quais eram os pacotes ofensivos, mas não consegui entendê-los. Estou vendo muitas tentativas para algumas dependências com diferentes versões de setuptools , bem como diferentes versões de boto3. Para nossas compilações de CI/docker, podemos adicionar restrições para acelerar as coisas (como sugerido aqui ), mas estamos relutantes em aumentar as restrições em nosso setup.py , pois não queremos restringir demais os usuários downstream. Ao mesmo tempo, temos muitos usuários iniciantes que estão acostumados a fazer pip install prefect[all_extras] - dizer a eles que precisam adicionar restrições adicionais para concluir isso em um período de tempo razoável parece desagradável. Eu não tenho certeza qual é o melhor caminho a seguir aqui.

Carreguei logs detalhados de uma execução aqui (morto após vários minutos de retrocesso). Se as pessoas quiserem tentar isso elas mesmas, você pode executar:

pip install "git+https://github.com/PrefectHQ/prefect.git#egg=prefect[all_extras]"

Qualquer conselho aqui seria útil - por enquanto estamos fixando o pip no 20.2.4, mas gostaríamos de atualizar assim que descobrirmos uma solução para o acima. Feliz em fornecer mais logs ou experimentar sugestões conforme necessário.

Obrigado por tudo que vocês fazem no pip e no pypa!

Isso pode acabar sendo resolvido por https://github.com/pypa/pip/issues/9185

Obrigado, @dstufft.

Mencionarei aqui algumas dicas úteis de solução alternativa da documentação - em particular, o primeiro e o terceiro pontos podem ser úteis para as pessoas que chegam aqui:

  • Se o pip estiver demorando mais para instalar os pacotes, leia Backtracking da resolução de dependências para obter maneiras de reduzir o tempo que o pip gasta no backtracking devido a conflitos de dependência.

  • Se você não quiser que o pip realmente resolva as dependências, use a opção --no-deps . Isso é útil quando você tem um conjunto de versões de pacotes que funcionam juntas na realidade, mesmo que seus metadados digam que eles são conflitantes. Para obter orientação sobre uma correção de longo prazo, leia Corrigindo dependências conflitantes .

  • Se você encontrar erros de resolução e precisar de uma solução alternativa enquanto estiver corrigindo suas causas raiz, poderá escolher o comportamento antigo do resolvedor usando o sinalizador --use-deprecated=legacy-resolver . Isso funcionará até lançarmos o pip 21.0 (consulte Linha do tempo de descontinuação).

No meu caso, o comportamento problemático pode ser reproduzido muito mais rápido com pip install 'google-cloud-translate==3.0.0' 'requests==2.20.0' 'setuptools==36.0.1' , então parece que #9185 pode melhorá-lo.

O resolvedor legado resolve isso rapidamente com: google-auth 1.23.0 requires setuptools>=40.3.0, but you'll have setuptools 36.0.1 which is incompatible. .

Uma outra ideia para isso é parar após 100 retrocessos (ou algo assim) com uma mensagem dizendo "ei, o pip está retrocedendo devido a muitos conflitos no pacote $".

Gostaria de saber quanto tempo é gasto baixando e descompactando versus realmente ocorrendo no próprio resolvedor?

Gostaria de saber quanto tempo é gasto baixando e descompactando versus realmente ocorrendo no próprio resolvedor?

A maior parte, da última vez que verifiquei. A menos que estejamos atingindo alguma situação gráfica muito ruim, nesse caso... :shrug: é melhor que os usuários dêem os pinos ao pip.

Estou fazendo com que nossa equipe preencha esse formulário do Google sempre que puder, mas só quero mencionar que praticamente todas as nossas compilações estão enfrentando problemas com isso. Coisas que funcionavam bem e tinham um tempo de compilação de cerca de 90 segundos agora estão expirando nossas compilações de CI. Em teoria, poderíamos aumentar o tempo limite, mas estamos pagando por essas máquinas por minuto, então fazer com que todas as nossas compilações demorem muito mais tempo é uma escolha dolorosa. Por enquanto, passamos a aplicar o resolvedor legado em todas as nossas compilações.

Como nota geral para os usuários que acessam esta página, leia https://pip.pypa.io/en/stable/user_guide/#dependency -resolution-backtracking.

Pediram-me para adicionar mais alguns detalhes do twitter, então aqui estão alguns pensamentos adicionais. No momento, as quatro soluções para esse problema são:

  1. Apenas espere terminar
  2. Use métodos de tentativa e erro para reduzir as versões verificadas usando restrições
  3. Registre e reutilize esses métodos de erro de tentativa em um novo arquivo "constraints.txt"
  4. Reduza o número de versões suportadas "durante o desenvolvimento"

Esperar é literalmente muito caro para considerar

Esta solução parece depender do download de uma tonelada épica de pacotes. Na era da nuvem, isso significa-

  • São necessários discos rígidos maiores para armazenar os pacotes adicionais
  • Mais largura de banda é consumida baixando esses pacotes
  • Demora mais para processar tudo devido à necessidade de descompactar essas imagens

Tudo isso custa dinheiro, embora o equilíbrio exato dependa dos pacotes (pessoas que lutam com uma fera como o tensorflow podem engasgar com o disco rígido e a largura de banda, enquanto as pessoas com pacotes menores são cobradas pelo tempo de compilação).

O que é ainda mais caro é o tempo do desenvolvedor desperdiçado durante uma operação que costumava levar (literalmente) 90s que agora leva mais de 20 minutos (pode levar mais tempo, mas expira em nossos sistemas de CI).

Nós literalmente não podemos nos dar ao luxo de usar esse sistema de resolução de dependência.

Restrições de tentativa e erro são extremamente onerosas

Isso adiciona um novo conjunto de processos ao ciclo de desenvolvimento de todos, onde eles não apenas precisam fazer o trabalho normal de desenvolvimento, mas agora precisam otimizar a caixa preta desse resolvedor. Mesmo o conselho na página é extremamente tentativa e erro, basicamente dizendo para começar com o primeiro pacote que está dando problemas e continuar iterando até que seus tempos de compilação sejam razoáveis.

Adicionar mais arquivos de configuração complica e já supercomplica o ecossistema.

Agora já temos que navegar pelas diferenças entre setup.py , requirements.txt , setup.cfg , pyproject.toml , e agora adicionando constraints.txt apenas adiciona ainda mais carga (e confusão) na manutenção de pacotes python.

A redução de versões verificadas durante o desenvolvimento não escala

Restringir versões durante o desenvolvimento, mas liberar o pacote sem essas restrições, significa que os usuários desse pacote terão que reinventar essas restrições durante o desenvolvimento. Se eu instalar um pacote popular, meus tempos de construção podem explodir até que eu duplique seus esforços. Não há como compartilhar essas restrições além dos métodos copiar/colar, o que aumenta a carga de manutenção.

No final das contas, isso resultará em pessoas que não usam restrições, limitando as versões de dependência diretamente baseadas não na compatibilidade real, mas em uma mistura de compatibilidade e tempos de compilação. Isso tornará mais difícil suportar pacotes menores a longo prazo.

A maior parte, da última vez que verifiquei.

Pode ser um bom motivo para priorizar https://github.com/pypa/warehouse/issues/8254

Pode ser um bom motivo para priorizar pypa/warehouse#8254

Definitivamente. E um equivalente sdist quando o PEP 643 for aprovado e implementado.

Esta solução parece depender do download de uma tonelada épica de pacotes

Ele não depende diretamente do download, mas depende do conhecimento dos metadados dos pacotes e, por várias razões históricas, a única maneira de obter esses dados atualmente é baixando (e no caso de distribuições de origem, compilando) o pacote .

Essa é uma sobrecarga enorme, embora o cache de download do pip ajude muito aqui (talvez você possa persistir o cache do pip em sua configuração de CI?) óbvia" da versão mais recente de um pacote é bloqueada por uma dependência de outro pacote), e só tende a ser realmente significativa nos casos em que não há solução válida de qualquer maneira (embora isso nem sempre seja imediatamente óbvio - o resolvedor antigo felizmente instale conjuntos inválidos de pacotes, para que o problema pareça "o resolvedor antigo funcionou, o novo falhou", onde na verdade é "o antigo quebrou coisas silenciosamente, o novo falha ao instalar").

Isso não ajuda a resolver o problema, eu sei, mas espero que forneça algumas informações sobre por que o novo resolvedor está se comportando como está.

@tedivm , por favor, procure usar pip-tools para executar a resolução de dependência como uma etapa separada da implantação. É essencialmente o ponto 4 - resolução de dependência "local" com a implantação vendo apenas versões fixadas.

Na verdade, seria um experimento interessante de se ver. Esses casos patológicos que as pessoas. estão experimentando, se eles deixarem o resolvedor concluir uma vez, persistir o cache e tentar novamente, é mais rápido? Se ainda durar horas, mesmo com um cache, isso sugere que pypa/warehouse#8254 não ajudará muito.

Não sei exatamente o que estamos fazendo agora, mas também me pergunto se faria sentido parar de pesquisar exaustivamente as versões depois de um certo ponto. Isso seria basicamente uma troca de dizer que vamos começar a fazer suposições sobre como as dependências evoluem ao longo do tempo. Suponho que estamos basicamente começando com a versão mais recente e iterando para trás uma versão de cada vez, está correto? Se sim, e se fizéssemos algo como:

  1. Iterar para trás uma versão de cada vez até falharmos na resolução X vezes.
  2. Inicie uma busca binária, corte os candidatos restantes pela metade e tente com isso.
    2a. Se funcionar, comece a levar a pesquisa binária para o lado "mais novo" (corte isso pela metade, tente novamente, etc).
    2b. Se falhar, comece a fazer a busca binária para o lado "mais antigo" (corte isso pela metade, tente novamente, etc).

Este não é exatamente o uso correto de uma pesquisa binária, porque a lista de versões não é realmente "classificada" dessa maneira, mas funcionaria de maneira semelhante a git bisect? O maior problema com isso é que ele pulará as boas versões se todas as versões N mais recentes falharem e a metade mais antiga das versões falhar, mas a metade do meio estiver "OK".

Outra ideia possível é, em vez de uma pesquisa binária, fazer uma ideia semelhante, mas em vez de agrupar a versão definida em metades, tente agrupar em baldes que correspondam à "cardinalidade" da versão. IOW, se isso tiver muitas versões principais, agrupe-as por versão principal, se tiver poucas versões principais, mas muitas versões secundárias, agrupe-as por isso, etc. Para dividir o espaço do problema, comece a iterar para trás tentando a primeira (ou a última?) versão em cada bucket até encontrar uma que funcione e, em seguida, restrinja o solver apenas a esse bucket (e talvez um bucket mais novo se você estiver testando a última versão em vez da primeira?).

Não sei, parece que pesquisar exaustivamente o espaço é a coisa "correta" a fazer se você quiser sempre encontrar a resposta se existir em algum lugar, mas se não pudermos fazer isso rápido o suficiente, mesmo com alterações no armazém etc. , provavelmente poderíamos tentar ser inteligentes ao usar heurísticas para restringir o espaço de pesquisa, supondo que os intervalos de versão normalmente não mudam com tanta frequência e, quando o fazem, não mudam com frequência a cada versão.

Talvez se entrarmos no modo de heurística, emitimos um aviso de que estamos fazendo isso, sugerimos que as pessoas forneçam mais informações ao solucionador, etc. Talvez forneça um sinalizador como --please-be-exhaustive-its-ok-ill-wait para desabilitar a heurística.

Talvez já estejamos fazendo isso e eu sou burro :)

Nós não estamos fazendo isso, e você não é burro :-) Mas é muito difícil fazer coisas assim - a maioria dos algoritmos de resolução que eu vi são baseados na suposição de que obter dados de dependência é barato (muitos nem são utilizável pelo pip porque eles assumem que todas as informações de dependência estão disponíveis desde o início). Então, estamos entrando no território de "projetar novos algoritmos para problemas de CS difíceis conhecidos" :-(

Outra ideia possível é, em vez de uma pesquisa binária, fazer uma ideia semelhante, mas em vez de agrupar a versão definida em metades, tente agrupar em baldes que correspondam à "cardinalidade" da versão. IOW, se isso tiver muitas versões principais, agrupe-as por versão principal, se tiver poucas versões principais, mas muitas versões secundárias, agrupe-as por isso, etc.

Alguns resolvedores que pesquisei realmente fazem isso, espacialmente de ecossistemas que promovem sempre fortemente (IIRC Cargo?)

A comunidade Python geralmente não adere a isso estritamente, mas ainda podemos fazer isso, já que o resolvedor nunca prometeu retornar a melhor solução, mas apenas uma boa o suficiente (ou seja, se 2.0.1 e 1.9.3 satisfizerem , o resolvedor não precisa escolher 2.0.1).

A outra parte é como lidamos com falhas de construção. Com nossos processos atuais, poderíamos ter que obter deps de compilação, fazer a compilação (ou na melhor das hipóteses chamar prepare_metadata_for_build_wheel para obter as informações).

Com a semântica de busca binária, teríamos que ser tolerantes com falhas de compilação e permitir que o pip tentasse usar uma versão diferente do pacote em falhas (em comparação com as falhas completas como fazemos hoje).

Talvez forneça um sinalizador como --please-be-exhaustive-its-ok-ill-wait para desabilitar a heurística.

Acho que parar depois de retroceder mais de 100 vezes e dizer "ei, isso está demorando muito. Ajude-me reduzindo versões de $packages ou diga-me para tentar mais com --option". é algo que podemos fazer com relativa facilidade agora.

Se as pessoas estão de acordo com isso, vamos escolher um número (eu disse 100, mas tirei isso do ar) e adicionar isso?

Temos uma boa noção se esses casos em que leva muito tempo para resolver são tipicamente casos em que não há resposta e está demorando muito para pesquisar exaustivamente o espaço porque nosso tempo lento por candidato significa que leva horas. ou são esses casos em que há uma resposta bem-sucedida, mas leva algum tempo para chegar lá?

@dstufft no meu caso, não havia solução adequada (consulte https://github.com/pypa/pip/issues/9187#issuecomment-736010650). Eu adivinhei quais poderiam ser as dependências problemáticas e com um conjunto reduzido de pacotes não demora tanto e produz o erro esperado. Com requisitos completos-min.txt , não foi concluído em horas.

Com quase 100 dependências fixadas, o espaço para pesquisar é enorme, e o pip acaba (talvez) imprimindo infinitamente "Requisito já satisfeito:" ao tentar procurar alguma solução (consulte https://github.com/WeblateOrg/weblate/ runs/1474960864?check_suite_focus=true para log longo, foi eliminado após algumas horas). Acabei de perceber que o processo de CI é um pouco mais complexo do que o que descrevi - primeiro ele instala pacotes com base nos intervalos, depois gera uma lista de versões mínimas e tenta ajustar o virtualenv existente. Provavelmente é daí que vêm os logs de "Requisito já satisfeito".

A cadeia de dependência problemática no meu caso foi:

  • google-cloud-translate==3.0.0 da linha de comando
  • setuptools==36.0.1 da linha de comando
  • google-api-core[grpc] >= 1.22.0, < 2.0.0dev de google-cloud-translate==3.0.0
  • google-auth >= 1.19.1, < 2.0dev de google-api-core
  • setuptools>=40.3.0 de google-auth (qualquer versão do intervalo)

No final, acho que o problema é que tenta encontrar solução em áreas onde não pode haver nenhuma. Com cache de pip completo:

$ time pip install  google-cloud-translate==3.0.0 setuptools==36.0.1
...

real    0m6,206s
user    0m5,136s
sys 0m0,242s
$ time pip install  google-cloud-translate==3.0.0 setuptools==36.0.1 requests==2.20.0
...

real    0m28,724s
user    0m25,162s
sys 0m0,283s

Nesse caso, adicionar requests==2.20.0 (que pode ser instalado sem nenhum problema com nenhuma das dependências) multiplica o tempo quase cinco vezes. Isso é causado por pip olhando para diferentes versões chardet e certifi por algum motivo.

Temos uma boa noção se esses casos em que leva muito tempo para resolver são tipicamente casos em que não há resposta e está demorando muito para pesquisar exaustivamente o espaço porque nosso tempo lento por candidato significa que leva horas. ou são esses casos em que há uma resposta bem-sucedida, mas leva algum tempo para chegar lá?

Tenho certeza de que no caso do prefeito com [all_extras] é porque não existe solução, mas ainda não consegui determinar quais são os pacotes incorretos. Em algum momento, sentarei e adicionarei iterativamente dependências à instalação base até que as coisas diminuam, só preciso encontrar o tempo.

Dicas sobre como interpretar os logs podem ser úteis aqui - posso ver quais pacotes o pip está pesquisando, mas não está claro qual restrição está falhando levando a essa pesquisa.


Em relação aos poucos comentários acima sobre desistir após um período ou usar heurísticas/suposições sobre esquemas de versão - para a maioria das coisas em que trabalhei, uma instalação válida geralmente é:

  • Todos os pacotes usam as versões mais recentes (por exemplo, mais recentes A , B e C )
  • Exceto se a versão mais recente de alguma dependência falhar, nesse caso nós geralmente consertamos as coisas rapidamente para fazê-la funcionar e usamos uma versão relativamente recente da versão quebrada (por exemplo A e B mais recentes, C tem 1 ou 2 versões).

Raramente a instalação que estou procurando será "as versões mais recentes de A e B , além de uma versão de C de 3 anos atrás". O único caso em que posso querer isso é se estou depurando algo ou tentando recriar um ambiente antigo, mas nesse caso eu normalmente especificaria que quero C=some-old-version diretamente em vez de fazer o solver para mim.

@brainwane me pediu para postar meu caso aqui do #9126. TLDR: o novo resolvedor é (apenas) 3x mais lento no meu caso.

Basicamente, eu uso
pip list --format freeze | sed 's/==.*//' | xargs --no-run-if-empty pip install --upgrade --upgrade-strategy eager
para converter meu ambiente manual (depois de adicionar e remover pacotes, atualizar, fazer downgrade, experimentar coisas) em algo o mais atualizado possível. Isso falhou com o resolvedor antigo, mas funciona muito bem com o novo. Ele atualiza pacotes antigos que podem ser atualizados e faz downgrade de pacotes que são muito novos para algum outro pacote.

A única coisa que eu me perguntei é o quão mais lento o novo resolvedor era. Trata-se de um fator 3 (42 vs 13 segundos, usando pip==2.3.0 com e sem --use-deprecated legacy-resolver ). Eu pensei que talvez as solicitações de rede fossem o principal problema, mas pip list --outdated leva apenas cerca de 20 segundos com exatamente o mesmo número de solicitações GET (125). Eu queria saber como pip pode gastar ~30s apenas na resolução da versão, mas novamente, no contexto deste tópico, começo a entender qual é o problema.

Sinta-se à vontade para usar ou ignorar este comentário como achar melhor ;)

> time pip list --outdated
Package           Version Latest Type
----------------- ------- ------ -----
gast              0.3.3   0.4.0  wheel
grpcio            1.32.0  1.33.2 wheel
h5py              2.10.0  3.1.0  wheel
lazy-object-proxy 1.4.3   1.5.2  wheel
protobuf          3.13.0  3.14.0 wheel

real    0m19.373s
user    0m19.718s
sys     0m0.721s


> time pip list --format freeze | sed 's/==.*//' | xargs --no-run-if-empty pip install --upgrade --upgrade-strategy eager

[...]

real    0m41.655s
user    0m38.308s
sys     0m1.786s


> time pip list --format freeze | sed 's/==.*//' | xargs --no-run-if-empty pip install --upgrade --upgrade-strategy eager
> --use-deprecated legacy-resolver

[...]

Successfully installed gast-0.4.0 grpcio-1.33.2 h5py-3.1.0 lazy-object-proxy-1.5.2 protobuf-3.14.0

real    0m13.064s
user    0m10.804s
sys     0m0.391s


> time pip list --format freeze | sed 's/==.*//' | xargs --no-run-if-empty pip install --upgrade --upgrade-strategy eager

[...]

Successfully installed gast-0.3.3 grpcio-1.32.0 h5py-2.10.0 lazy-object-proxy-1.4.3 protobuf-3.13.0

real    0m42.860s
user    0m39.015s
sys     0m2.000s

Temos uma boa noção se esses casos em que leva muito tempo para resolver são tipicamente casos em que não há resposta e está demorando muito para pesquisar exaustivamente o espaço porque nosso tempo lento por candidato significa que leva horas. ou são esses casos em que há uma resposta bem-sucedida, mas leva algum tempo para chegar lá?

Bem, ele funciona com o resolvedor antigo sem erros, mas não com o novo - isso responde à pergunta?

Essa é uma sobrecarga enorme, embora o cache de download do pip ajude muito aqui (talvez você possa persistir o cache do pip em sua configuração de CI?) óbvia" da versão mais recente de um pacote é bloqueada por uma dependência de outro pacote), e só tende a ser realmente significativa nos casos em que não há solução válida de qualquer maneira (embora isso nem sempre seja imediatamente óbvio - o resolvedor antigo felizmente instale conjuntos inválidos de pacotes, para que o problema pareça "o resolvedor antigo funcionou, o novo falhou", onde na verdade é "o antigo quebrou coisas silenciosamente, o novo falha ao instalar").

Isso não ajuda a resolver o problema, eu sei, mas espero que forneça algumas informações sobre por que o novo resolvedor está se comportando como está.

Nós persistimos o cache, mas ele literalmente nunca termina.

Eu entendo como o resolvedor funciona, mas meu ponto é que entender isso não faz com que o problema desapareça. Esse nível de sobrecarga é literalmente ordens de magnitude maior do que a versão anterior - ou de qualquer outro gerenciador de pacotes por aí.

Entendo as decisões herdadas que tiveram que ser suportadas aqui, mas francamente até que a questão do desempenho seja abordada, esta versão do resolvedor não deve ser o padrão. O PyPi deve enviar os dados de dependência já computados, não nos forçando a construir dezenas de pacotes repetidamente para gerar os mesmos dados que centenas de outras pessoas também estão regenerando. Entendo que isso está no roteiro, aguardando financiamento, mas é minha opinião que esse resolvedor não está pronto para produção até que esse problema seja resolvido.

Tenho que deixar aqui meus pensamentos: concordo com @tedivm que este resolvedor não está pronto para produção. O UX de ter o pip rodando por 10s de minutos sem saída útil é terrível. No momento, o pip está produzindo uma quantidade ímpia de texto duplicado (o que provavelmente está diminuindo a velocidade da pesquisa) Requirement already satisfied: ... .

Se o resolvedor falhar na primeira tentativa (suponho que o pip tente instalar as versões mais recentes), acho que o pip deve imprimir as violações de restrição imediatamente. Ou adicione opções para limitar a pesquisa a N tentativas ou faça apenas M tentativas para um determinado pacote. E talvez depois de uma certa quantidade de tentativas, o pip deve imprimir a situação com a menor quantidade de violações de restrição. Do jeito que está, tenho que apenas Ctrl-C pip quando ele é executado por muito tempo (10 minutos é muito tempo) e não recebo informações úteis por ter esperado.

Minha instalação de pacotes python está demorando muito e o pipeline Jenkins CI/CD falha após 2 horas.

Coletando amqp==2.5.2
14:27:30 Baixando amqp-2.5.2-py2.py3-none-any.whl (49 kB)
14:27:30 Coletando boto3==1.16.0
14:27:30 Baixando boto3-1.16.0-py2.py3-none-any.whl (129 kB)
14:27:30 Requisito já satisfeito: jmespath<1.0.0,>=0.7.1 em /root/.local/lib/python3.6/site-packages (de boto3==1.16.0->gehc-edison- ai-container-support==3.5.0) (0.10.0)
14:27:30 Requisito já satisfeito: s3transfer<0.4.0,>=0.3.0 em /root/.local/lib/python3.6/site-packages (de boto3==1.16.0->gehc-edison- ai-container-support==3.5.0) (0.3.3)
14:27:30 Coletando botocore==1.19.26
14:27:30 Baixando botocore-1.19.26-py2.py3-none-any.whl (6,9 MB)
14:27:30 Requisito já satisfeito: python-dateutil<3.0.0,>=2.1 em /root/.local/lib/python3.6/site-packages (de botocore==1.19.26->gehc-edison- ai-container-support==3.5.0) (2.8.1)
14:27:30 Requisito já satisfeito: jmespath<1.0.0,>=0.7.1 em /root/.local/lib/python3.6/site-packages (de boto3==1.16.0->gehc-edison- ai-container-support==3.5.0) (0.10.0)
14:27:30 Requisito já satisfeito: urllib3<1.27,>=1.25.4 em /root/.local/lib/python3.6/site-packages (de botocore==1.19.26->gehc-edison-ai- container-support==3.5.0) (1.25.11)
14:27:30 Coletando aipo==5.0.2
14:27:30 Baixando aipo-5.0.2-py3-none-any.whl (392 kB)
14:27:30 INFO: o pip está analisando várias versões do botocore para determinar qual versão é compatível com outros requisitos.
Isso vai demorar um pouco.14:27:31 INFO: o pip está analisando várias versões de <Python from Requires-Python> para determinar qual versão é compatível com outros requisitos.
Isso vai demorar um pouco.

por que o pip está olhando para várias versões?

Existe alguma resolução para isso.

@Nishanksingla vejo um item na saída que você copiou aqui:

14:27:31 INFO: o pip está analisando várias versões para determinar qual versão é compatível com outros requisitos. Isso vai demorar um pouco.

Isso é literalmente o resultado do pip ou você removeu o nome de um pacote?

Além disso, recomendo que você dê uma olhada nas dicas e orientações neste comentário .

Atualizei o comentário, o git não estava mostrando <Python from Requires-Python>

Bem, ele funciona com o resolvedor antigo sem erros, mas não com o novo - isso responde à pergunta?

Não. O resolvedor antigo resolveria regularmente para um conjunto de dependências que violava as restrições de dependência. O novo resolvedor é mais lento, em parte, porque ele para de fazer isso, e parte do trabalho para parar de fazer isso torna as coisas mais lentas (parcialmente por motivos exclusivos da história do empacotamento do Python).

O PyPi deve enviar os dados de dependência já computados, não nos forçando a construir dezenas de pacotes repetidamente para gerar os mesmos dados que centenas de outras pessoas também estão regenerando. Entendo que isso está no roteiro, aguardando financiamento, mas é minha opinião que esse resolvedor não está pronto para produção até que esse problema seja resolvido.

Isso não é realmente possível em todos os casos.

Basicamente, temos rodas, que computam estaticamente informações de dependência. Atualmente, isso requer o download de uma roda do PyPI e a extração dessas informações dessa roda. No momento, temos planos para fazer isso com certeza no Warehouse.

As rodas são o caso fácil... o problema então se resume aos sdists. Historicamente, os sdists podem ter informações de dependência completamente dinâmicas, como algo assim:

setup(
    install_requires=[random.choice(["urllib3", "requests"])]
)

é um arquivo setup.py completamente válido (embora bobo), onde não é possível pré-computar o conjunto de dependências. Um exemplo mais sério seria aquele que analisa o ambiente de execução atual e ajusta o conjunto de dependências com base no que é descoberto sobre o ambiente atual, isso às vezes pode ser tão mundano quanto baseado no sistema operacional ou na versão Python (que nos tempos modernos temos maneiras estáticas de expressar isso, mas nem todo mundo está usando isso ainda) ou coisas como quais bibliotecas C existem em um sistema ou. algo parecido.

Assim, para sdists, temos alguns casos em que alguns deles podem ter informações de dependência estática (mas atualmente não têm, embora tenhamos um plano para isso), mas alguns deles não podem e não terão, e nesses casos retrocedendo essas escolhas são basicamente sempre lentas.

Portanto, nossa esperança aqui é acelerar os casos comuns fazendo com que os conjuntos estáticos de dependências que podemos computar estejam disponíveis como parte da API do repositório, mas sempre há uma chance de que algum projeto possa existir em um estado que desencadeie esse comportamento lento mesmo com essas melhorias (e isso pode acontecer com todos os gerenciadores de pacotes que usam um sistema como este, porém o Python está em uma posição pior por causa de nossa capacidade de dependência dinâmica).

Acho que provavelmente é verdade que mais pessoas estavam atingindo o "caso ruim" do que o esperado, uma das razões pelas quais perguntei se essas coisas lentas eventualmente terminam com um conjunto resolvido de dependências ou se terminam com um conjunto insolúvel de dependências. Se eles são tipicamente erros, então faz sentido apenas bombardear mais cedo com uma mensagem de erro, porque nossa heurística pode ser "se não retrocedermos mais de N vezes, provavelmente estamos caminhando para um fracasso". Se eles normalmente terminam com sucesso, mas demoram um pouco para chegar lá, isso sugere que seria melhor investir na tentativa de tornar nossa heurística para escolher candidatos mais inteligentes de alguma forma, para tentar chegar a uma solução mais rapidamente.

Uma coisa que me surpreende é que não estou recebendo esse problema no meu sistema quando instalo meu requirements.txt com pip 20.3 e python3.6 em um ambiente virtual.
Mas para o mesmo requirements.txt estou tendo problemas no meu pipeline Jenkins.
Alguma ideia?

Bem, ele funciona com o resolvedor antigo sem erros, mas não com o novo - isso responde à pergunta?
Não. O resolvedor antigo resolveria regularmente para um conjunto de dependências que violava as restrições de dependência. O novo resolvedor é mais lento, em parte, porque ele para de fazer isso, e parte do trabalho para parar de fazer isso torna as coisas mais lentas (parcialmente por motivos exclusivos da história do empacotamento do Python).

Quando eu digo "sem erro", eu estava falando literalmente - a violação que você está dizendo que poderia acontecer não aconteceu. Normalmente, quando ele quebra as coisas, ele diz assim; como você recebe uma mensagem dizendo algo como "O pacote quer X, mas instalamos Y". Estou dizendo explicitamente que não recebemos essa mensagem.

Quando executamos o resolvedor legado no mesmo conjunto de dependências que o novo resolvedor, o resolvedor antigo retorna com um conjunto de dependências válido em um minuto e vinte e nove segundos, enquanto o resolvedor antigo falha após o tempo limite de nossos sistemas de CI com 20 minutos sem nada.

Alguma ideia?

Diferenças ambientais talvez? É possível ter dependências condicionais ao ambiente em que o pip está sendo executado (versão python, sistema operacional, plataforma etc)

Dicas sobre como interpretar os logs podem ser úteis aqui - posso ver quais pacotes o pip está pesquisando, mas não está claro qual restrição está falhando levando a essa pesquisa.

Há uma opção não documentada + não suportada que adicionei para minha depuração pessoal: PIP_RESOLVER_DEBUG . Não há promessas de que estará em lançamentos futuros ou que não haverá um impacto no desempenho, mas agora, você provavelmente pode usar isso. Saída Moar! :)

Normalmente, quando ele quebra as coisas, ele diz assim; como você recebe uma mensagem dizendo algo como "O pacote quer X, mas instalamos Y". Estou dizendo explicitamente que não recebemos essa mensagem.

Ah interessante! Tem certeza de que não está suprimindo a mensagem de erro? (há uma opção CLI, env var ou arquivo de configuração que pode fazer isso - pip config list ajudaria a identificar os dois últimos)

Se não, você poderia postar instruções de reprodução em um Github Gist, talvez, e link para isso daqui?

PS: Eu trabalhei / escrevi cada um dos componentes aqui - o aviso, o resolvedor antigo e o novo, e AFAIK o que você está descrevendo não deve ser possível, a menos que eu tenha derped muito e ninguém mais tenha percebido. ;)

Minha experiência é a seguinte:

  1. O novo resolvedor com retrocesso é muito lento para usar (seria muito útil se houvesse um sinalizador para simplesmente falhar assim que ele começasse a retroceder), então a solução óbvia é apenas fazer um instantâneo das dependências que sabemos que funcionam de um legado pip freeze em um arquivo constraints.txt como um paliativo. (Deus sabe como vamos regenerar esse arquivo, mas isso é um problema para outro dia).

  2. Uh oh, parece que ainda temos um conflito mesmo sabendo que as versões funcionam, mas felizmente o projeto do qual dependemos corrigiu suas dependências no master, então vamos apenas depender da URL do git. Ahh, legal, isso não funciona (https://github.com/pypa/pip/issues/8210), esses pertencem a requirements.txt .

  3. Mais alguns problemas, incluindo uma falha grave em metadados ruins para uma versão .post1 (https://github.com/pypa/pip/pull/9085 aparentemente não foi corrigido ou acha que isso é uma falha real) -- então agora estou editando manualmente o constraints.txt e adicionando comentários explicando que esse arquivo precisará ser mantido manualmente daqui para frente.

4; Tudo parece resolvido, e agora estou em um loop aparentemente infinito (quem sabe, parei depois que 33k linhas foram impressas em stdout) no qual as seguintes linhas são impressas repetidamente:

Requirement already satisfied: google-auth<2.0dev,>=0.4.0 in /Users/max/.virtualenvs/internal/lib/python3.7/site-packages (from google-api-core<1.24,>=1.16.0->dbt-bigquery@ git+https://github.com/fishtown-analytics/dbt.git#egg=dbt-bigquery&subdirectory=plugins/bigquery->-r python_modules/elementl-data/requirements.txt (line 4)) (1.23.0)
Requirement already satisfied: google-auth<2.0dev,>=0.4.0 in /Users/max/.virtualenvs/internal/lib/python3.7/site-packages (from google-api-core<1.24,>=1.16.0->dbt-bigquery@ git+https://github.com/fishtown-analytics/dbt.git#egg=dbt-bigquery&subdirectory=plugins/bigquery->-r python_modules/elementl-data/requirements.txt (line 4)) (1.23.0)
Requirement already satisfied: six>=1.14.0 in /Users/max/.virtualenvs/internal/lib/python3.7/site-packages (from dbt-bigquery@ git+https://github.com/fishtown-analytics/dbt.git#egg=dbt-bigquery&subdirectory=plugins/bigquery->-r python_modules/elementl-data/requirements.txt (line 4)) (1.15.0)
Requirement already satisfied: requests<2.24.0,>=2.18.0 in /Users/max/.virtualenvs/internal/lib/python3.7/site-packages (from dbt-core@ git+https://github.com/fishtown-analytics/dbt.git#egg=dbt-core&subdirectory=core->-r python_modules/elementl-data/requirements.txt (line 3)) (2.23.0)
Requirement already satisfied: pytz>=2015.7 in /Users/max/.virtualenvs/internal/lib/python3.7/site-packages (from Babel>=2.0->agate<2,>=1.6->dbt-core@ git+https://github.com/fishtown-analytics/dbt.git#egg=dbt-core&subdirectory=core->-r python_modules/elementl-data/requirements.txt (line 3)) (2020.4)
Requirement already satisfied: googleapis-common-protos<1.53,>=1.6.0 in /Users/max/.virtualenvs/internal/lib/python3.7/site-packages (from dbt-bigquery@ git+https://github.com/fishtown-analytics/dbt.git#egg=dbt-bigquery&subdirectory=plugins/bigquery->-r python_modules/elementl-data/requirements.txt (line 4)) (1.6.0)
Requirement already satisfied: setuptools>=34.0.0 in /Users/max/.virtualenvs/internal/lib/python3.7/site-packages (from google-api-core<1.24,>=1.16.0->dbt-bigquery@ git+https://github.com/fishtown-analytics/dbt.git#egg=dbt-bigquery&subdirectory=plugins/bigquery->-r python_modules/elementl-data/requirements.txt (line 4)) (50.3.2)
Requirement already satisfied: six>=1.14.0 in /Users/max/.virtualenvs/internal/lib/python3.7/site-packages (from dbt-bigquery@ git+https://github.com/fishtown-analytics/dbt.git#egg=dbt-bigquery&subdirectory=plugins/bigquery->-r python_modules/elementl-data/requirements.txt (line 4)) (1.15.0)

Desconcertante. Anexei o requirements.txt e o constraints.txt. O setup.py é executado da seguinte forma:

from setuptools import setup

setup(
    install_requires=[
        "boto3",
        "dagster_aws",
        "dagster_dbt",
        "dagster_gcp",
        "dagster_pandas",
        "dagster_slack",
        "dagster",
        "dagstermill",
        "dbt",
        "google-cloud-bigquery",
        "idna",
        "nltk",
        "pandas",
        "pybuildkite",
        "requests",
        "slackclient",
        "snowflake-sqlalchemy",
        "tenacity",
    ],
)

requisitos.txt
restrições.txt

@mgasner Imagino que você se beneficiaria adotando pip-tools e executando a construção do gráfico de dependência e o gerenciamento de dependência como uma etapa separada da instalação. :)

Ah interessante! Tem certeza de que não está suprimindo a mensagem de erro? (há uma opção CLI, env var ou arquivo de configuração que pode fazer isso - a lista de configuração do pip ajudaria a identificar os dois últimos)

Isso está no CircleCI, então não é trivial executar o comando, mas eu já vi essas mensagens antes no CircleCI com esses contêineres e não estamos substituindo as coisas, então não tenho motivos para acreditar que estamos suprimindo alguma coisa.

Se não, você poderia postar instruções de reprodução em um Github Gist, talvez, e link para isso daqui?

Eu aprecio todos vocês olhando para isso, e definitivamente tentarei ajudar a replicá-lo - mas como envolve algumas bibliotecas privadas nossas (retiradas de repositórios do github), terei que me esforçar e não posso prometer que vai seja rápido.

@pradyunsg Sim, é claro que usar o resolvedor de dependências para resolver dependências não é inicial, mas esse não é o problema que encontrei aqui - esse é o ponto de partida.

Uma nota para todos relatando problemas aqui:

Oi. Lamento que você esteja tendo problemas agora. Obrigado por compartilhar seu relato conosco. Estamos trabalhando nos vários problemas entrelaçados que as pessoas estão relatando para nós.

(Se você não se importar, por favor, diga-nos também o que poderia ter acontecido de forma diferente para que você pudesse ter testado e detectado e relatado isso durante o período beta do resolvedor.)

FYI: PEP-643 (Metadados para Distribuições de Fontes de Pacotes) foi aprovado . :foguete:

Então, anteriormente, eu previ que isso forçaria as pessoas a parar de oferecer suporte a versões válidas de pacotes simplesmente por causa dos problemas de dependência, não por causa de qualquer problema programático real com eles. Isso já está acontecendo-

Screen Shot 2020-12-02 at 12 05 03 PM

Essa mudança vai levar as pessoas a serem muito, muito mais restritivas nas versões suportadas e isso terá ramificações que eu realmente espero que as pessoas tenham considerado.

Essa mudança vai levar as pessoas a serem muito, muito mais restritivas nas versões suportadas e isso terá ramificações que eu realmente espero que as pessoas tenham considerado.

A esperança é que as pessoas forneçam requisitos razoavelmente rígidos do que a comunidade coletiva tradicionalmente prefere. É muito raramente, quando os usuários pedem “solicitações” (por exemplo), que realmente qualquer versão de requisições serviria; mas as ferramentas de empacotamento do Python tradicionalmente “ajudam” o usuário ingenuamente escolhendo a versão possível do novo conjunto. Minha esperança é que usuários e mantenedores de pacotes Python possam fornecer mais contexto quando um requisito for especificado; isso ajudaria todos os usuários, mantenedores e desenvolvedores de ferramentas a fornecer um ambiente de cooperação melhor.

Uma coisa que pode valer a pena considerar é se os relatórios de longos tempos de resolução compartilham alguma característica comum - a coisa mais óbvia é um conjunto específico de pacotes "problemáticos". Eu vi o botocore aparecer muito em relatórios e me pergunto se ele tem um número excepcionalmente grande de lançamentos ou fez mais alterações incompatíveis do que outros pacotes?

Obviamente, não é prático para nós (os desenvolvedores do pip) investigar os pacotes caso a caso, mas precisamos de algo mais específico para entender o problema.

Talvez pudéssemos instrumentar o pip para despejar estatísticas ("tentou versões X do projeto A, versões Y do projeto B, ..., antes de falhar / ter sucesso"), para um arquivo local em algum lugar que pedimos às pessoas para enviar? Mas isso é principalmente o que está no log de qualquer maneira, e é menos útil, a menos que as pessoas deixem o comando ser executado até a conclusão, então talvez não seja muita ajuda adicional.

https://github.com/pypa/pip/issues/9187#issuecomment -736104404

Uma outra ideia para isso é parar depois de 100 retrocessos (ou algo assim) com uma mensagem dizendo "ei, o pip está retrocedendo muito devido a conflitos em pacotes $".

Vamos fazer isso -- e escolher um número para isso. E permitir que o usuário escolha um número maior na CLI?

Uma coisa a considerar é como contamos para esse número. Diga se X depende de Y. X==2.0 é fixado, Y foi retrocedido três vezes e, finalmente, todas as versões falharam, então X é retrocedido e fixado em X==1.0, onde Y é retrocedido mais duas vezes e finalmente encontrou uma versão funcional . Y agora tem uma contagem de retrocesso de 3 ou 5? Posso pensar em razões pelas quais um pode ser melhor que o outro.

Eu vi o botocore aparecer muito em relatórios e me pergunto se ele tem um número excepcionalmente grande de lançamentos ou fez mais alterações incompatíveis do que outros pacotes?

De fato, tem um número incomumente alto de lançamentos, está sendo lançado quase diariamente, veja https://pypi.org/project/botocore/#history

De fato, tem um número incomumente alto de lançamentos, está sendo lançado quase diariamente

E como exemplo, depende de python-dateutil>=2.1,<3.0.0 . Portanto, se você instalar o python-dateutil 3.0.0 e o botocore, o pip terá que retroceder em todas as versões do botocore antes de ter certeza de que não há um que funcione com o dateutil 3.0.0.

Fundamentalmente, esse é o cenário que está causando esses longos tempos de execução. Não podemos presumir que uma versão do botocore de anos atrás possa não ter permitido nenhuma versão de python-dateutil (mesmo que o 3.0.0 provavelmente nem existisse naquela época e na prática não funcione com ele) , então temos que verificar. E pior ainda, se uma versão antiga do botocore tiver uma dependência irrestrita em python-dateutil , podemos acabar instalando com dateutil 3.0.0 e ter um sistema que, embora tecnicamente consistente, não funciona .

A melhor correção é provavelmente o usuário adicionar uma restrição dizendo ao pip para não considerar versões do botocore anteriores a alguma versão que o usuário considere "recente". Mas pip não pode inventar tal restrição.

Eu vi o botocore aparecer muito em relatórios e me pergunto se ele tem um número excepcionalmente grande de lançamentos ou fez mais alterações incompatíveis do que outros pacotes?

Os pacotes da AWS são de fato lançados com frequência, e provavelmente o fato de serem fixados tão estritamente é a causa do extenso retrocesso. Portanto, parece não apenas muito solto, mas também especificações de requisitos muito rígidas podem causar problemas para o resolvedor. Existem alguns truques para forçar conflitos rapidamente (por exemplo, escolher o próximo pacote a ser resolvido com base na menor quantidade de versões ainda viáveis ​​e escolher pacotes que tenham a menor quantidade de dependências, ref https://github.com/sdispater/mixology/pull/5 ).

O exemplo clássico de explosão é combinar uma versão preferida do boto3 com qualquer versão do awscli, porque o boto3 é restritivo no botocore e o awscli também é restritivo no botocore.

Algumas bibliotecas tentam resolver este problema fornecendo extras_require, por exemplo aiobotocore[awscli,boto3]==1.1.2 irá aplicar pinos exatos (awscli==1.18.121 boto3==1.14.44) que são conhecidos por serem compatíveis entre si (o botocore está decidindo aqui ).

No entanto, se qualquer um for solicitado sem pin, o resolvedor terá que considerar uma grande quantidade de versões para encontrar uma que solicite versões de botocore sobrepostas.

$ pipgrip --tree boto3==1.14.44 awscli==1.18.121
boto3==1.14.44 (1.14.44)
├── botocore<1.18.0,>=1.17.44 (1.17.44)
│   ├── docutils<0.16,>=0.10 (0.15.2)
│   ├── jmespath<1.0.0,>=0.7.1 (0.10.0)
│   ├── python-dateutil<3.0.0,>=2.1 (2.8.1)
│   │   └── six>=1.5 (1.15.0)
│   └── urllib3<1.26,>=1.20 (1.25.11)
├── jmespath<1.0.0,>=0.7.1 (0.10.0)
└── s3transfer<0.4.0,>=0.3.0 (0.3.3)
    └── botocore<2.0a.0,>=1.12.36 (1.17.44)
        ├── docutils<0.16,>=0.10 (0.15.2)
        ├── jmespath<1.0.0,>=0.7.1 (0.10.0)
        ├── python-dateutil<3.0.0,>=2.1 (2.8.1)
        │   └── six>=1.5 (1.15.0)
        └── urllib3<1.26,>=1.20 (1.25.11)
awscli==1.18.121 (1.18.121)
├── botocore==1.17.44 (1.17.44)
│   ├── docutils<0.16,>=0.10 (0.15.2)
│   ├── jmespath<1.0.0,>=0.7.1 (0.10.0)
│   ├── python-dateutil<3.0.0,>=2.1 (2.8.1)
│   │   └── six>=1.5 (1.15.0)
│   └── urllib3<1.26,>=1.20 (1.25.11)
├── colorama<0.4.4,>=0.2.5 (0.4.3)
├── docutils<0.16,>=0.10 (0.15.2)
├── pyyaml<5.4,>=3.10 (5.3.1)
├── rsa<=4.5.0,>=3.1.2 (4.5)
│   └── pyasn1>=0.1.3 (0.4.8)
└── s3transfer<0.4.0,>=0.3.0 (0.3.3)
    └── botocore<2.0a.0,>=1.12.36 (1.17.44)
        ├── docutils<0.16,>=0.10 (0.15.2)
        ├── jmespath<1.0.0,>=0.7.1 (0.10.0)
        ├── python-dateutil<3.0.0,>=2.1 (2.8.1)
        │   └── six>=1.5 (1.15.0)
        └── urllib3<1.26,>=1.20 (1.25.11)

@ddelange obrigado pela análise!

Existem alguns truques para forçar conflitos rapidamente

Estamos explorando essa opção em #9211

O exemplo clássico de explosão é combinar uma versão preferida do boto3 com qualquer versão do awscli, porque o boto3 é restritivo no botocore e o awscli também é restritivo no botocore.

Infelizmente, não consigo pensar em nenhuma maneira de resolver isso sem que os mantenedores do pacote ajudem de alguma forma (ou usuários explicitamente e manualmente, restringindo quais versões eles estão dispostos a permitir que o pip considere).

Talvez precisemos de um mecanismo para marcar as versões como "muito antigas para valer a pena considerar por padrão". Mas isso precisaria de padrões de empacotamento para definir como essas informações são expostas e mantenedores de pacotes para gerenciar essas informações, então, na prática, duvido que seja prático.

FYI: PEP-643 (Metadados para Distribuições de Fontes de Pacotes) foi aprovado . 🚀

Ignorando os pacotes etc mais específicos da plataforma / legados, seria teoricamente possível que o pip buscasse todos os arquivos .whl.METADATA para cada versão de um pacote em uma grande chamada para o PyPI?

Com o cache adequado tanto no lado dos servidores pypa/warehouse quanto no lado do usuário do pip, pode ser uma grande aceleração. Como você mencionou anteriormente:

a maioria dos algoritmos de resolução que vi são baseados na suposição de que obter dados de dependência é barato

@ddelange Se o maior custo for baixar + pacotes de construção, sim. Consulte https://github.com/pypa/warehouse/issues/8254. :)

Edit: @dstufft discutiu sobre isso em https://github.com/pypa/pip/issues/9187#issuecomment -736792074.

2\. Start a binary search, cut the remaining candidates in half and try with that.
    2a. If it works, start taking the binary search towards the "newer" side (cut that in half, try again, etc).
    2b. If it fails, start taking the binary search towards the "older"side (cut that in half, try again, etc).

Este não é exatamente o uso correto de uma pesquisa binária, porque a lista de versões não é realmente "classificada" dessa maneira, mas funcionaria de maneira semelhante a git bisect? O maior problema com isso é que ele pulará as boas versões se todas as versões N mais recentes falharem e a metade mais antiga das versões falhar, mas a metade do meio estiver "OK".

Gostaria de saber se um algoritmo de busca binária/bisecção probabilística barulhento poderia tornar essa abordagem mais robusta. https://github.com/choderalab/thresholds/blob/master/thresholds/bisect.py

E como exemplo, depende de python-dateutil>=2.1,<3.0.0. Portanto, se você instalar o python-dateutil 3.0.0 e o botocore, o pip terá que retroceder em todas as versões do botocore antes de ter certeza de que não há um que funcione com o dateutil 3.0.0.

A melhor correção é provavelmente o usuário adicionar uma restrição dizendo ao pip para não considerar versões do botocore anteriores a alguma versão que o usuário considere "recente". Mas pip não pode inventar tal restrição.

Desculpas se isso já foi pensado e vetado

Eu me pergunto se o pip poderia fazer uma suposição aqui.

Dados os pacotes A e B onde A depende de B . Se A version 10 suporta versões B <= 5 , acho que pip poderia assumir que as versões de A < 10 não suportam versões de B > 5 . Na minha experiência, os pacotes raramente usam limites superiores em dependências. E quando o fazem, raramente diminuem em número (geralmente os mantenedores aumentam as versões, não as diminuem). Parece improvável que A==9 suporte B<=6 e diminua para B<=5 na próxima versão. E se isso acontecesse, o usuário ainda poderia obter o pip para resolver esse ambiente fornecendo uma restrição explícita.

Eu acho que isso ajudaria no caso em que o pip continua retrocedendo nas versões botocore - ele pode verificar a versão mais recente, ver que não suporta python-dateutil 3.0.0 e sair mais cedo (já que assume versões mais antigas de botocore não suportam uma versão mais recente de python-dateutil .

Talvez precisemos de um mecanismo para marcar as versões como "muito antigas para valer a pena considerar por padrão". Mas isso precisaria de padrões de empacotamento para definir como essas informações são expostas e mantenedores de pacotes para gerenciar essas informações, então, na prática, duvido que seja prático.

@pfmoore Eu tive várias falhas iniciais em que o resolvedor de dependência estava vagando por mais de uma dúzia de versões de pacotes e travando ao tentar obter metadados de pacotes tão antigos que seu setup.py não era compatível com o Python 3 ...

  1. Eu gostaria de esperar que o resolvedor de dependências esteja sendo inteligente o suficiente para não baixar uma versão que _sabe_ é apenas python2 ... não tenho certeza exatamente da melhor maneira, mas apenas declarando meu sentimento sobre o assunto.
  2. Desistir antes de baixar as n+1ª (por um valor arbitrário razoável de n) versões de um pacote com algum tipo de mensagem informativa sobre restrições de versão em caso de falha, provavelmente é mais inteligente do que baixar mais de 100 pacotes até que a compilação expire .

Não especificamente endereçado a você @pfmoore , apenas meu feedback geral depois de lidar com (e pesquisar) o novo resolvedor na última versão publicada do pip por várias (> 6) horas hoje.

  1. O problema por trás dos problemas da maioria das pessoas parece ser menos um caso de "não foi possível encontrar uma solução válida" e mais o caso de o novo resolvedor estar sendo _patologicamente persistente_ em seu esforço para resolver as coisas. Parece que o novo resolvedor não tem proteção contra loop infinito óbvio e tempos de desempenho patológicos de pior caso. Por que _alguém_ iria querer deixar o pip rodar por mais de uma hora _como comportamento padrão_. Existem várias sugestões ao longo da linha de uma opção "fail fast" ou "desistir cedo", que quando colocada ao lado do problema do resolvedor buscar informações de dependência para um único pacote, voltando 50 versões (veja meu exemplo lxml no ponto 4) à medida que fica cada vez mais lento ao longo de uma hora em uma compilação de CI ... não parece que isso estava pronto para ser o padrão.

  2. A única maneira pela qual consegui chegar perto do desempenho aceitável foi com --use-feature=fast-deps mas mesmo isso é uma mitigação parcial, pois o resolvedor de dependência voltará alegremente até o ponto em que não pode mais obter as informações dessa maneira e começa a fazer as coisas da maneira mais lenta novamente. Meu melhor exemplo é lxml que usou o caminho rápido das versões 4.6.1 até 3.7.2 Então o download mais lento do método tarball do pacote estava em uso de 3.7.1 até que a execução do CI expirou após 60 minutos, tendo andou todo o caminho de volta para 2.0.8.

  3. Os sistemas de CI para contêineres do docker frequentemente contêm _no pip cache_ devido às limitações de várias combinações de ferramentas de criação do docker disponíveis. Independentemente de outros problemas, medir o desempenho do resolvedor _deve_ incluir algum tipo de teste de desempenho para este caso, projetos de tamanho razoável de pelo menos 100 dependências totais com um ambiente de compilação 100% limpo, nada em cache.

    • Para pessoas interessadas em solucionar seus próprios problemas de desempenho com o novo resolvedor, encontrei 1 mecanismo parcial em torno deste https://stackoverflow.com/questions/58018300/using-a-pip-cache-directory-in-docker-builds , mas ele depende de novos recursos experimentais, então eu aconselho qualquer pessoa a confiar nele, e mesmo com isso para puxar o cache do ambiente docker para o host ... há a questão das limitações impostas pelos sistemas CI que residem fora da própria compilação do docker ... como no meu próprio caso, onde mesmo que eu puxe um pip cache do contêiner para o sistema de arquivos do host de compilação, o serviço de CI jogaria o conteúdo do sistema de arquivos fora em 60 minutos após a caixa de compilação ser ocioso.

Editar - erros de digitação corrigidos

Desculpas se isso já foi pensado e vetado

Certamente é algo implícito em nossas discussões, mas talvez nunca tenhamos abordado isso explicitamente. Então, obrigado por trazê-lo à tona.

Eu me pergunto se o pip poderia fazer uma suposição aqui.

O problema é que este é um terreno muito perigoso. Há um grande número de suposições que o pip poderia fazer o que simplificaria as coisas, mas a experiência mostrou que sempre que fazemos tais suposições, encontramos algum pacote que as quebra. E "fazer uma suposição inválida" é praticamente garantido para ser relatado a nós como "um bug" 🙂

Seu exemplo parece muito razoável, mas e se um pacote lançasse uma versão com suporte python-dateutil <= 3.0 , mas adicionasse um recurso que dependia do comportamento que foi removido no dateutil 3.0, então eles lançaram sua próxima versão dependendo de python-dateutil < 3 como uma abordagem de curto prazo enquanto eles tentavam implementar seu recurso de uma maneira que funcionasse com versões mais recentes do dateutil?

Acho que se começarmos a usar heurísticas como essa, precisaríamos adicionar maneiras de permitir que os usuários controlem se estão habilitados. E isso fica complexo muito rápido.

Eu gosto da ideia em princípio (gostaria que os projetos não fizessem coisas estranhas que dificultam meu trabalho como desenvolvedor de pip 🙂), mas os aspectos práticos provavelmente o tornarão inviável.

@pfmoore Ao longo das linhas do que eu estava chegando no ponto 3 do meu comentário https://github.com/pypa/pip/issues/9187#issuecomment -738103308 - Uma solução heurística "padrão simples" aqui onde "versão padrão do pacote backtrace depth = N " e um sinalizador de linha de comando para substituir isso por um valor definido pelo usuário de M resolveria alguns dos problemas com botocore e outros pacotes que retrocedem muitas versões. Para o novo resolvedor, acho que heurísticas simples como essa, com padrões bem documentados, mensagens de erro úteis e configuração fácil... podem ajudar bastante a mitigar os relatórios de bugs que podem vir de abordagens "inteligentes" mais complicadas.

Acho que se começarmos a usar heurísticas como essa, precisaríamos adicionar maneiras de permitir que os usuários controlem se estão habilitados. E isso fica complexo muito rápido.

Um ponto justo. Eu me identifico totalmente com os "botões demais" em um projeto OSS. Eu entendo que fazer suposições tem um custo aqui e confio no seu julgamento (e da equipe do pip). Dito isso, acho que a suposição acima é justa, dada uma escotilha de escape bem projetada. Talvez:

  • Os usuários que precisam de uma versão mais antiga podem definir uma restrição para essa versão? Isso altera o comportamento da solução alternativa para o novo resolvedor de forçar os usuários a definir explicitamente limites inferiores em requisitos para acelerar as coisas para definir limites superiores em dependências que exigem uma versão mais antiga devido a um downgrade de limite superior de dependência.

  • Pip pode fazer a suposição descrita acima, mas também retrocede um pouco conforme necessário (com um número configurável de retrocessos). Isso supõe que diminuições em uma versão máxima de dependência são raras e de curta duração (por exemplo, lançar uma versão de patch que reduza o dep, corrija na próxima versão ou duas e remova o limite novamente) e resolveria:

    e se um pacote lançasse uma versão com suporte a python-dateutil <= 3.0, mas adicionasse um recurso que dependia do comportamento que foi removido no dateutil 3.0, então eles lançaram sua próxima versão dependendo de python-dateutil < 3 como uma abordagem de curto prazo enquanto eles tentaram implementar seu recurso de uma maneira que funcionasse com versões mais recentes do dateutil?

Dito isto, eu não pensei sobre isso tanto quanto vocês. Apenas meus 2 centavos.

A proposta de @jcrist faz a mesma suposição que a proposta de @dstufft para busca binária faz: que as dependências mínimas exigidas de um pacote geralmente são ordenadas através das versões do pacote

Suponho que a questão seja se os casos em que isso não é verdade ocorrem com frequência suficiente para que não valha a pena acelerar o resolvedor.

Por mais absurdo que possa parecer... Gostaria de saber se alguém já utilizou todos os metadados de pacotes disponíveis no PyPI para realizar uma análise real da situação. Deve ser possível descobrir um limite inferior razoável para a frequência com que tais diminuições em uma versão máxima de dependência realmente acontecem.

Não consigo responder tão rápido quanto as sugestões estão chegando agora, então vou deixar essa discussão por enquanto e analisar mais tarde. Mas posso apenas agradecer a todos pelas sugestões construtivas e úteis. Este é um problema realmente complicado de acertar, e novas ideias e perspectivas são realmente úteis!

Por mais absurdo que possa parecer... Gostaria de saber se alguém já utilizou todos os metadados de pacotes disponíveis no PyPI para realizar uma análise real da situação. Deve ser possível descobrir um limite inferior razoável para a frequência com que tais diminuições em uma versão máxima de dependência realmente acontecem.

Nada absurdo. Eu tenho um trabalho em andamento tentando coletar esses dados para análise. O problema é que eu tenho o material disponível na API PyPI JSON, e isso tem sido um trabalho muito grande para coletar. Mas obter dados de dependência significa baixar todas as rodas no PyPI, além de construir todos os sdists (e mesmo assim, para sdists, recebo apenas metadados que se aplicam ao meu sistema). Eu ainda tenho que descobrir por onde começar com essa tarefa. Mesmo "apenas escolher pacotes representativos" não é prático, pois eu não teria olhado para "botocore" e assumido que era um pacote importante, se não o tivesse visto aparecer aqui com tanta frequência.

(Se alguém tiver um espelho PyPI e puder extrair os arquivos medatada de todas as rodas, e publicar apenas esses dados em algum lugar, isso seria muito útil).

Então, sim, é uma sugestão razoável, mas a logística de baixar todo o PyPI para obter os dados torna muito mais difícil do que você imagina. (Os desenvolvedores de pip não têm nenhum acesso privilegiado ao PyPI que possa facilitar isso, infelizmente).

Vou deixar as coisas acumularem da noite para o dia e ver como a conversa evoluiu. Mas por enquanto... @pfmoore (e qualquer outra pessoa curiosa... é um pouco tarde para eu começar a fazer esse tipo de coisa a esta hora da noite) se você estiver curioso, existem alguns pontos de partida que encontrei enquanto investigava se foi tentado que são de outras pessoas que fizeram a análise PyPI "da maneira mais difícil" no passado.

... edit: último pensamento, para quem está analisando essa ideia, não se esqueça de verificar como o pip faz a pesquisa de dependência para --use-feature=fast-deps porque provavelmente fará uma grande diferença na facilidade com que você obtém os metadados para tantas versões.

@techdragon Essas postagens são principalmente de alguns anos atrás, e o PyPI tem significativamente mais pacotes agora do que antes (aqui está uma postagem do início de 2019, mostrando a curva de crescimento: https://blog.adafruit.com/2019/03 /13/growth-in-the-python-ecosystem-python/). É um crescimento quase exponencial.

Muitas dessas técnicas agora exigem _significativamente_ mais recursos do que costumavam. Eu tive alguma visibilidade em um esforço não público para fazer isso, e mesmo com a maior máquina EC2 da AWS, não era trivial e cancelado antes que algo útil saísse disso.

Sobre a questão de analisar (e possivelmente validar) metadados de arquivamento de pacotes no PyPI, as pessoas podem estar interessadas em https://github.com/pypa/warehouse/issues/474#issuecomment -370986838 e https://github.com/ pypa/packaging-problems/issues/264 , conforme listado no item "Auditoria e atualização de metadados do pacote" na lista de projetos de empacotamento financiáveis ​​.

(Se alguém tiver um espelho PyPI e puder extrair os arquivos medatada de todas as rodas, e publicar apenas esses dados em algum lugar, isso seria muito útil).

fwiw: Raspei o PyPI em setembro de 2019 e agosto de 2020, usando distlib para obter as dependências de cada pacote e despejei os resultados em um arquivo. Os resultados são confusos e incompletos (não me lembro como o distlib resolve as dependências, então não sei exatamente _como_ incompleto), e não tenho tempo agora para limpá-los, mas agora está disponível aqui se é útil para qualquer pessoa: https://github.com/sersorrel/pypi-stats

A esperança é que as pessoas forneçam requisitos razoavelmente rígidos do que a comunidade coletiva tradicionalmente prefere. É muito raramente, quando os usuários pedem “solicitações” (por exemplo), que realmente qualquer versão de requisições serviria; mas as ferramentas de empacotamento do Python tradicionalmente “ajudam” o usuário ingenuamente escolhendo a versão possível do novo conjunto. Minha esperança é que usuários e mantenedores de pacotes Python possam fornecer mais contexto quando um requisito for especificado; isso ajudaria todos os usuários, mantenedores e desenvolvedores de ferramentas a fornecer um ambiente de cooperação melhor.

Isso é razoável no topo - a versão faz sentido, já que você não sabe se novas versões vão funcionar (e isso é obviamente mais fácil para pacotes que suportam versionamento semântico).

O problema está na outra direção - limites mais restritivos (só suportamos as três últimas versões da biblioteca X, por exemplo) reduzem a janela de compatibilidade entre os pacotes. Se você tem um pacote que funciona com ~v1, mas está configurado para apenas >v1.46 e outro pacote que está limitado a v1.40, você não poderá encontrar uma correspondência.

Tudo bem se for por razões legítimas, mas se estiver acontecendo apenas para evitar lidar com problemas excessivos do gerenciador de pacotes, parece um problema. Este é um exemplo simplificado - no mundo real, onde as pessoas dependem de mais de duas bibliotecas, e os mantenedores (que geralmente trabalham de graça) estão apenas suportando um conjunto mínimo de versões, será muito pior e resultará em muitos casos em que o pip não poderá encontrar correspondências.

Mesmo "apenas escolher pacotes representativos" não é prático, pois eu não teria olhado para "botocore" e assumido que era um pacote importante

https://pypistats.org/top

O que o @tedivm disse é verdade -- ao encorajar os desenvolvedores de módulos a fixar seus requisitos com mais firmeza, você está pedindo o Version Hell, onde dois módulos são simplesmente impossíveis de instalar juntos, mesmo que eles realmente funcionem.

Sério, mantenedores de PIP - por favor, voltem para o resolvedor anterior e voltem com um melhor, quando todos esses problemas forem resolvidos. POR FAVOR. Não há como você resolver todos esses problemas. Não há como fazer isso.

Apenas tratar é como uma oportunidade de aprendizado. Não há problema em tentar, mas quando você vê que não funciona, não há vergonha em desistir.

Olá, @potiuk! Obrigado por compartilhar seu conselho e opinião. Levaremos em consideração.

Executamos um período beta para o novo resolvedor de pip para solicitar relatórios de bugs, começando com o lançamento do pip 20.2 em julho . Pessoas que estão relatando novos problemas para nós: realmente nos ajudaria se você também pudesse nos dizer o que poderia ter acontecido de forma diferente para que você pudesse testar e detectar e relatar os problemas que você está vendo durante o período beta do resolvedor de pip. Não importa se removemos o resolvedor legado conforme planejado, o mantemos por mais tempo ou fazemos outra coisa, sua resposta nesse ponto nos ajudará em lançamentos futuros.

Na verdade, eu já compartilhei o Airflow Story aqui com uma explicação completa de por que realmente tentamos, mas não pudemos testar o novo resolvedor antes https://github.com/pypa/pip/issues/9203#issuecomment -737878689

@potiuk Obrigado por fazer isso! Outros participantes aqui - por favor, siga o exemplo de @potiuk .

E para ser muito franco - isso não foi uma reclamação de qualquer tipo. Foi apenas um conselho. Eu entendo o trabalho duro que você tem mantendo metade da internet funcionando! Foi realmente uma sugestão e observação como usuário de que não há como corrigir isso agora, olhando para o tipo e a quantidade de problemas que vemos. É simplesmente uma avaliação realista (eu acho) da situação e sugerindo o curso de ação que você pode tomar.

Eu aprecio todo o trabalho que você colocou nele! Só não vai funcionar desta vez e acho bom você encarar a realidade.

Um segundo para os dois pontos de @potiuk - há muito tempo lamentei a falta de um solucionador "adequado" no pip, então mal posso esperar para que os problemas sejam resolvidos.

E o equivalente desenvolvedor do HugOps para todos da equipe do pip!

Talvez o backtracker deva parar em 2 backtracks por padrão?

Faria sentido aumentar por padrão (e imprimir o caso de teste) se o tempo total decorrido também exceder um limite padrão?

(Observe que o pior caso "gerar uma cadeia infinita de pacotes e slowloris" aqui ainda é ilimitado quando há apenas um limite de retrocesso)

@westurner não, porque nem todo mundo tem conexões rápidas de internet e/ou computadores, então os métodos baseados em tempo tornariam o pip inutilizável por padrão para esses usuários.

O número de retrocessos seria uma heurística melhor.

Este é um longo tópico, com muitas opiniões e sugestões diferentes. Vou jogar minha própria experiência aqui também.

No trabalho, estou usando o novo resolvedor e funciona muito bem. Temos ~70 dependências diretas complexas, incluindo grandes estruturas de ML como pytorch , tensorflow , etc e também boto3 e os suspeitos usuais como requests , Click , Flask . Ele resolve para um gráfico de dependência completo de ~ 267 dependências com sucesso.

Para sistemas de produção, sou da opinião de que as dependências devem ser bloqueadas com antecedência. Um desenvolvedor trabalhando em um projeto precisa entender qual é o impacto quando atualiza uma dependência ou adiciona uma nova dependência. Percebo que esse fluxo de trabalho ou opinião não é o único fluxo de trabalho ou opinião. Também não estou muito interessado em discutir os prós ou contras. Estou apenas compartilhando o que funciona bem na minha experiência e o que acredito que leva a resultados determinísticos e bons.

Para lock nossas dependências, temos resultados muito positivos usando pip-tools que foi mencionado anteriormente.

Nosso fluxo de trabalho é o seguinte:

  • Desenvolvedor modifica requirements.in
  • O desenvolvedor executa um script que finalmente invoca python3.7 -m piptools compile
  • O desenvolvedor observa os resultados da resolução e confirma o arquivo requirements.txt bloqueado no repositório
  • CI só precisa usar o arquivo requirements.txt bloqueado que já está garantido para resolver em um tempo razoável

Reconheço que isso só funciona para "usuários finais" e não para outros projetos, como o Airflow, que precisam ter dependências "mais soltas". No entanto, acho que projetos como awscli, boto, airflow têm alguma responsabilidade por definir restrições razoáveis ​​em suas dependências. Outros ecossistemas de linguagem, como Java e JavaScript, são capazes de fazer isso com sucesso, por que o Python não deveria?

Também gostaria de chamar a atenção para alguns dados que podem ajudar em algumas análises estáticas do PyPI.
https://github.com/DavHau/pypi-deps-db

O link acima é do mach-nix, que se esforça muito para tornar o Python agradável de usar no Nix. Não estou sugerindo que o ecossistema Python precise se mover em direção a mecanismos tão extremos, mas talvez esse banco de dados ajude se alguém estiver procurando analisar as coisas.

Limites padrão razoáveis ​​para uma ferramenta que não deve baixar uma sequência infinita de código para executar com permissões no nível de instalação:

  • número de retrocessos

    • 100?

  • tempo de execução total

    • 1 hora?

  • número de pacotes

    • 1000?

Eles podem ser substituídos por variáveis ​​de ambiente pip.conf e/ou PIP_ .

Devemos tentar limitar o consumo de recursos (largura de banda, tempo de CPU) dos piores casos.

Fixar dependências é uma solução alternativa que pode introduzir atraso no tempo para corrigir problemas críticos de gravidade: com (por exemplo, SemVer) restrições, os usuários não precisam ficar presos a versões antigas.

(Edit) Embora, para ser justo, o pip não pretenda gerenciar o ciclo de vida completo da distribuição de software: pip install -U pode quebrar as coisas, não funcionar: não devemos esperar que os usuários executem pip install regularmente ou mesmo novamente (até que um contêiner seja reconstruído com --no-cache TBH )

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