<p>aiohttp ne doit pas invoquer le gestionnaire d'exceptions pour les situations non exceptionnelles</p>

Créé le 10 nov. 2020  ·  4Commentaires  ·  Source: aio-libs/aiohttp

🐞 Décrivez le bogue
Lorsque certains objets d'aiohttp sont divulgués, ils invoquent le gestionnaire d'exceptions asyncio, ce qui dans certains environnements (par exemple lors de l'utilisation d' aiorun , avec stop_on_unhandled_errors=True ) signifie que tout le programme est arrêté.

Par exemple, voici le code pertinent dans ClientResponse :

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

💡 Reproduire
Fuitez un objet aiohttp qui fait cela.

💡 Comportement attendu
Des objets qui fuient ne devraient pas provoquer d'exception, pour la même raison que open() ing un fichier et non .close() ing il ne lève pas d'exception.

Tous les objets qui font cela semblent déjà déclencher un avertissement concernant la fuite de ressources - je ne vois pas de raison pour qu'ils invoquent également le gestionnaire d'exceptions.

📋 Votre version du Python

$ python --version
Python 3.8.6

📋 Votre version des distributions aiohttp / yarl / multidict

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

console
$ python -m pip show multidict
Nom: multidict
La dernière version: 4.7.6

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

📋 Contexte supplémentaire
cjrh / aiorun # 56

bug wontfix

Tous les 4 commentaires

Je ne suis pas d'accord.
La ressource non fermée est une erreur de programmation suffisamment grave qui peut être corrigée facilement en écrivant le code correct.
Par exemple, asyncio lui-même utilise call_exception_handler() pour signaler les appels asynchrones non attendus.
Si vous ne voulez pas écrire de code pédant, vous pouvez utiliser stop_on_unhandled_errors=False .

Alors, pourquoi ne pas soulever une véritable exception alors?

Je suppose que le fait de lever une exception dans __del__ sera ignoré. Mais c'est comme ça que ça devrait être, non? Il est très difficile de déboguer "quelque chose alloue un objet et ne le ferme pas correctement", surtout quand le bogue n'est pas dans votre propre code mais dans une bibliothèque; la clé de contexte source_traceback n'est pas documentée, et il semble peu convivial de s'appuyer sur le comportement non documenté du gestionnaire d'exceptions par défaut d'asyncio pour permettre de déboguer les fuites de mémoire.

Je ne vois pas non plus où asyncio utilise call_exception_handler() pour avertir des coroutines inattendues. Lorsqu'une coroutine non attendue est récupérée, elle invoque _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);
    }

qui invoque warnings._warn_unawaited_coroutine() :

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

qui émet un RuntimeWarning. Je ne vois pas call_exception_handler() utilisé nulle part là-bas - en fait, je ne vois aucune utilisation de call_exception_handler() dans stdlib qui n'inclut pas exception dans le contexte.

Et encore: si oublier de fermer un fichier ne soulève pas d'exception, pourquoi oublier de nettoyer n'importe quel objet aléatoire aiohttp?

Vous avez raison, lever une exception à partir __del__ méthode
C'est pourquoi nous avons besoin d'une autre solution de signalisation.
Asyncio fait appel call_exception_handler() de __del__ , voir Task.__del__ par exemple: https://github.com/python/cpython/blob/master/Lib/asyncio/tasks.py # L139 -L148

Vous avez raison, source_traceback n'est pas documenté dans https://docs.python.org/3/library/asyncio-eventloop.html?highlight=call_exception_handler#asyncio.loop.call_exception_handler
Pourriez-vous créer une pull request sur https://github.com/python/cpython/ pour corriger la documentation? Je suis heureux de l'examiner / de le fusionner.

Et encore: si oublier de fermer un fichier ne soulève pas d'exception, pourquoi oublier de nettoyer n'importe quel objet aléatoire aiohttp?

aiohttp ne lève pas non plus d'exception à partir de __del__ , c'est tout simplement impossible en Python car l'appel du finaliseur d'objet est indéterministe.

Un fichier non fermé génère un ResourceWarning . Cet avertissement est ignoré par défaut: https://docs.python.org/3/library/warnings.html#default -warning-filter
C'est pourquoi ce type d'avertissement n'est pas visible pour de très nombreux développeurs s'ils ne les activent pas explicitement. Ceci est fait en partie pour des raisons historiques: pendant longtemps, ce type d'avertissement n'existait pas, de nombreuses bibliothèques n'appellent pas file.close() parce que leurs auteurs ne sont pas conscients de la nécessité d'un nettoyage des ressources gracieux, etc., etc. .

Heureusement, file.close() pourrait être appelé à partir de file.__del__ toute sécurité, c'est un appel régulier.
Mais dans le monde asyncio close() est très souvent différent: il doit s'agir d'une fonction asynchrone qui ne peut tout simplement pas être appelée à partir de __del__ .
Par exemple, transport.close() sans attendre protocol.connection_lost() conduit à des avertissements même pour les sockets simples; pour SSL, la situation est encore pire.
aiohttp a un petit problème avec les finaliseurs sync / async mais je travaille dessus; aiohttp 4.0 devrait être nettoyé de manière significative.

C'est pourquoi le nettoyage est très important, un avertissement doit être levé et call_exception_handler() sera utilisé pour rendre la mauvaise utilisation plus visible.

Asyncio _does_ appelle call_exception_handler() partir de __del__ , voir Task.__del__ par exemple

Assez juste, j'ai raté cet appel.

Pourriez-vous créer une pull request sur python / cpython pour corriger la documentation?

Je ne sais vraiment pas comment le documenter au mieux. J'ai créé bpo-42347 car j'ai remarqué d'autres divergences entre la documentation, la docstring et l'implémentation.

C'est pourquoi le nettoyage est très important, un avertissement doit être levé et call_exception_handler () sera utilisé pour rendre la mauvaise utilisation plus visible.

Je suppose que c'est juste frustrant lorsque vous obtenez des erreurs parce qu'une bibliothèque que vous utilisez ne se nettoie pas correctement. Je n'avais pas réalisé que c'était si important que cela devrait arrêter l'application, et je ne me suis même pas rendu compte au départ que la fuite était la raison pour laquelle aiorun arrêtait la boucle.

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

Questions connexes

JulienPalard picture JulienPalard  ·  3Commentaires

rubenvdham picture rubenvdham  ·  5Commentaires

Smosker picture Smosker  ·  3Commentaires

alxpy picture alxpy  ·  5Commentaires

AtomsForPeace picture AtomsForPeace  ·  5Commentaires