Para um pequeno diagnóstico de proxy com urllib3, use ProxyManager com novas tentativas desativadas. Assim você vê a primeira tentativa, sem gastar mais tempo repetindo uma entrada que parou de funcionar. Em um projeto gerenciado pelo uv:

uv add urllib3

Substitua o endpoint de documentação por um proxy HTTP do seu serviço ou da lista de proxies HTTP e faça a requisição por esse gerenciador:

import urllib3

proxy = urllib3.ProxyManager(
    "http://203.0.113.10:8080",
    timeout=urllib3.Timeout(connect=5, read=10),
    retries=False,
)

try:
    response = proxy.request("GET", "https://example.com/")
    print(response.status)
    print(response.data.decode("utf-8", errors="replace")[:200])
finally:
    proxy.clear()

Leia response.status primeiro e depois confira response.data. Esses são os campos do urllib3; status_code e text do estilo Requests não os substituem. Uma rejeição do site pode chegar por um proxy funcional. Já uma exceção antes da resposta indica que não foi possível concluir a requisição.

retries=False é uma escolha de diagnóstico, não uma política de novas tentativas de produção. Os ajustes de conexão de 5 segundos e leitura de 10 segundos limitam suas próprias fases; eles não somam um prazo total garantido de 15 segundos. DNS e progresso da resposta podem afetar a espera total.

O exemplo pré-carrega o corpo e limpa os pools do gerenciador ao final. Para requisições repetidas pelo mesmo endpoint, mantenha o gerenciador até o fim do trabalho para reutilizar suas conexões. Mudar para preload_content=False altera esse ciclo de vida: quem chama precisa ler e liberar a resposta em fluxo.

Escolha o gerenciador conforme o serviço que escuta. Um PoolManager comum não passa a usar proxy porque uma URL está guardada em outro lugar; SOCKS usa urllib3.contrib.socks.SOCKSProxyManager e suas dependências opcionais. O código acima segue o caminho HTTP de ProxyManager.

Artigos relacionados