En attendant le dossier sur la programmation non bloquante, voici une petite lib qui résout un cas d’école : faire une requête HTTP sans bloquer avec une jolie API, en pur Python.
Pour ça, on dégaine pip et installe requests-futures, un plugin pour la célèbre lib requests qui fonctionne avec Python 2 et 3 :
pip install requests-futures |
requests-futures va créer pour vous une pool de workers (2 par défaut) et quand vous faites une requête, la lib vous retourne un objet future qui vous permet d’attacher un callback.
Fiou, le nombre de liens référant à d’autres articles du blog est en train d’exploser.
Exemple :
import time from requests_futures.sessions import FuturesSession # Cette session est notre point d'entrée, c'est elle # qui gère nos workers. Faites help(FuturesSession) # pour voir ses paramètres. session = FuturesSession() # Les URLs sur lesquelles on va faire # nos requêtes URLs = [ "http://sametmax.com", "http://sebsauvage.net", "http://indexerror.net", "http://afpy.org", "http://0bin.net" ] # Notre callback qui sera appelé quand une # des requêtes sera terminée. Il reçoit # l'objet future pour seul paramètre def faire_un_truc_avec_le_resultat(future): # On est juste intéressé par le résutlat, qui # est un objet response typique de la lib # request response = future.result() print(response.url, response.status_code) # On traite chaque URL. Comme on a 2 workers, # on pourra traiter au mieux 2 URLs en parallèle, # mais toujours sans bloquer le programme # principal for url in URLs: # On fait notre requête GET future = session.get(url) # On rajoute le callback à appeler quand # le résultat de la requête arrive. # La flemme de faire la gestion des erreurs. future.add_done_callback(faire_un_truc_avec_le_resultat) # Juste pour montrer que c'est bien non bloquant for x in range(10): print(x) time.sleep(1) |
Output :
0 1 (u'http://sebsauvage.net/', 200) (u'http://sametmax.com/', 200) 2 (u'http://indexerror.net/', 200) (u'http://0bin.net/', 200) (u'http://www.afpy.org/', 200) 3 4 5 6 7 8 9
On remerciera turgon37 pour sa question sur IndexError qui m’a amené à écrire cet article.
Il manque un apostrophe dans “d’école”.
Dans le commentaire avant faire_un_truc_avec_le_resultat(future): c’est le seuL paramètre
Chaque URL (sans s), à l’inverse en ligne suivante on pourra traiter 2 URLs avec un s (ou pas, si on considère que c’est un nom propre ?)
D’où sortent ces 2 workers ? Est-ce une valeur par défaut ?
Aussi, il me semble qu’avoir des liens hypertextes identifiables rapidement (variations de couleur ou soulignage par exemple) est une bonne pratique. Je me suis rendu compte aujourd’hui qu’il y avait autant de liens dans tes articles.
@toto : Les liens sont identifiables par rapport au texte (certes un rouge assez sombre), et ils se soulignent au passage de la souris. L’article précise que 2 workers est le nombre par défaut et la doc que 10 est le nombre max de workers.
Au passage quelle est la traduction “officielle” de worker en français ?
@zed : je dirais “processus dédié” mais à vrai dire je connais rien d’officiel.
Pourquoi la réponse avec un score de -1 est en premier pour la question corespondante sur IndexError ? Y’a comme un souci.
Bonjour,
est-ce que par hasard il y aurait un moyen de passer des arguments au callback ?
Merci
Oui avec functools.partial.
Ok, merci.
Juste pour savoir, est-ce qu’il y a un avantage à utiliser la solution que vous donnez ici http://sametmax.com/en-attendant-asyncio/, ou la solution de cet article ?
La solution de cet article est plus pratique si on peut se permettre de rajouter une dépendance.
Ok, merci beaucoup, ça m’a aidé. Cependant je rencontre un problème plutôt inattendu. J’ai l’impression que les futures ne ferment pas les connections. Quand on lance beaucoup de futures, et qu’on fait un “lsof -i | grep “python” | wc -l”, on se rend compte que le nombre de fichiers ne fait qu’augmenter. Dans certains cas, ça dépasse ce que l’OS peut supporter, et le programme crashe. Est-ce que c’est normal ?
Je ne sais pas, faut regarder le code et checker si les futures ferment les connections ou si il faut le faire manuellement à l’obtention du résultat.
Ok, j’ai trouvé:
“`
headers = {‘User-agent’: ‘Mozilla/5.0’,
‘Connection’: ‘close’}
future = self.session.get(url, headers=headers)