<p>aiohttp não deve invocar o manipulador de exceção para situações comuns</p>

Criado em 10 nov. 2020  ·  4Comentários  ·  Fonte: aio-libs/aiohttp

🐞 Descreva o bug
Quando certos objetos de aiohttp vazam, eles invocam o manipulador de exceção asyncio, que em alguns ambientes (por exemplo, ao usar aiorun , com stop_on_unhandled_errors=True ) significa que o programa inteiro está parado.

Por exemplo, aqui está o código relevante em ClientResponse :

https://github.com/aio-libs/aiohttp/blob/a8d9ec3f1667463e80545b1cacc7833d1ff305e9/aiohttp/client_reqrep.py#L748 -L751

💡 Para reproduzir
Vazamento de um objeto aiohttp que faz isso.

💡 Comportamento esperado
O vazamento de objetos não deve causar uma exceção, pela mesma razão que open() ing um arquivo e não .close() ing não levanta uma exceção.

Todos os objetos que fazem isso parecem já gerar um aviso sobre o vazamento de recursos - não vejo um motivo para eles chamarem o manipulador de exceções também.

📋 Sua versão do Python

$ python --version
Python 3.8.6

📋 Sua versão das distribuições aiohttp / yarl / multidict

$ python -m pip show aiohttp
Name: aiohttp
Version: 3.6.2

`` `console
$ python -m pip show multidict
Nome: multidict
Versão: 4.7.6

```console
$ python -m pip show yarl
Name: yarl
Version: 1.5.1

📋 Contexto adicional
cjrh / aiorun # 56

bug wontfix

Todos 4 comentários

Discordo.
O recurso não fechado é um erro de programação suficientemente sério que pode ser corrigido facilmente escrevendo o código correto.
Por exemplo, o próprio asyncio usa call_exception_handler() para relatar sobre chamadas assíncronas não esperadas.
Se você não quiser escrever um código pedante - você pode usar stop_on_unhandled_errors=False .

Então, por que não levantar uma exceção real então?

Acho que porque o lançamento de uma exceção em __del__ será ignorado. Mas é assim que deve ser, não? É muito difícil depurar "algo aloca um objeto e não o fecha adequadamente", especialmente quando o bug não está em seu próprio código, mas em alguma biblioteca; a chave de contexto source_traceback está documentada e parece hostil confiar no comportamento não documentado do manipulador de exceção padrão de asyncio para tornar possível depurar vazamentos de memória.

Eu também não vejo onde asyncio usa call_exception_handler() para avisar sobre corrotinas não esperadas. Quando uma co-rotina não esperada é coletada como lixo, ela invoca _PyErr_WarnUnawaitedCoroutine :

    /* If `gen` is a coroutine, and if it was never awaited on,
       issue a RuntimeWarning. */
    if (gen->gi_code != NULL &&
        ((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE &&
        gen->gi_frame->f_lasti == -1)
    {
        _PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
    }

que invoca warnings._warn_unawaited_coroutine() :

void
_PyErr_WarnUnawaitedCoroutine(PyObject *coro)
{
    /* First, we attempt to funnel the warning through
       warnings._warn_unawaited_coroutine.

que emite um RuntimeWarning. Não vejo call_exception_handler() usado em nenhum lugar lá - na verdade, não vejo nenhum uso de call_exception_handler() em stdlib que não inclua exception no contexto.

E novamente: se esquecer de fechar um arquivo não levanta uma exceção, por que esquecer de limpar qualquer objeto aiohttp aleatório?

Você está certo, levantar uma exceção do método __del__ é uma prática desencorajada.
É por isso que precisamos de outra solução de sinalização.
Asyncio faz chamada call_exception_handler() de __del__ , consulte Task.__del__ por exemplo: https://github.com/python/cpython/blob/master/Lib/asyncio/tasks.py # L139 -L148

Você está certo, source_traceback não está documentado em https://docs.python.org/3/library/asyncio-eventloop.html?highlight=call_exception_handler#asyncio.loop.call_exception_handler
Você se importaria de criar uma solicitação pull em https://github.com/python/cpython/ para corrigir a documentação? Fico feliz em revisar / mesclar.

E novamente: se esquecer de fechar um arquivo não levanta uma exceção, por que esquecer de limpar qualquer objeto aiohttp aleatório?

aiohttp não levanta uma exceção de __del__ também, é simplesmente impossível em Python porque a chamada do finalizador de objeto é indeterminística.

Arquivo não fechado gera ResourceWarning . Este aviso é ignorado por padrão: https://docs.python.org/3/library/warnings.html#default -warning-filter
É por isso que esse tipo de aviso não é visível para muitos desenvolvedores se eles não os habilitarem explicitamente. Isso é feito parcialmente por razões históricas: por muito tempo esse tipo de aviso não existia, muitas bibliotecas não chamam file.close() porque seus autores não estão cientes da necessidade de uma limpeza harmoniosa de recursos, etc., etc. .

Felizmente, file.close() pode ser chamada de file.__del__ com segurança, é uma chamada normal.
Mas no mundo assíncrono close() é muito diferente: tem que ser uma função assíncrona que não pode ser chamada de __del__ .
Por exemplo, transport.close() sem esperar por protocol.connection_lost() leva a avisos mesmo para soquetes simples; para SSL, a situação é ainda pior.
o aiohttp tem um pouco de confusão com os finalizadores de sincronização / async, mas estou trabalhando nisso; aiohttp 4.0 deve ser limpo significativamente.

É por isso que a limpeza é muito importante, um aviso deve ser gerado e call_exception_handler() será usado para tornar o mau uso mais visível.

Asyncio _does_ chama call_exception_handler() de __del__ , consulte Task.__del__ por exemplo

Muito bem, perdi a chamada.

Você se importaria de criar uma solicitação de pull em python / cpython para corrigir a documentação?

Sinceramente, não tenho certeza da melhor forma de documentar isso. Eu criei o bpo-42347 porque notei algumas outras discrepâncias entre os docs, o docstring e a implementação.

É por isso que a limpeza é muito importante, um aviso deve ser gerado e call_exception_handler () será usado para tornar o mau uso mais visível.

Eu acho que é frustrante quando você recebe erros porque uma biblioteca que você está usando não se limpa de maneira adequada. Não percebi que era tão importante para interromper o aplicativo, e nem percebi inicialmente que o vazamento era o motivo pelo qual o aiorun estava interrompendo o loop.

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

Questões relacionadas

AtomsForPeace picture AtomsForPeace  ·  5Comentários

zhmiao picture zhmiao  ·  3Comentários

rubenvdham picture rubenvdham  ·  5Comentários

yuval-lb picture yuval-lb  ·  5Comentários

jonringer picture jonringer  ·  4Comentários