Open Karduin opened 2 years ago
bonsoir @Karduin
le fichier que tu as modifié est bien le source de ce qui se trouve ici
donc effectivement c'est là qu'on pourrait envisager de corriger le code que je tape dans la vidéo pour montrer celui qu'il aurait fallu taper avec une 3.10
(pour info, ça ne se met pas à jour tout seul depuis git, il faudra un opération manuelle pour ça)
je me souviens assez mal de ce contenu, mais il me semble qu'en effet le principal du code est dans les vidéos, et que les notebooks sont bcp plus minces que dans les autres semaines.
donc oui je pense que c'est la bonne voie pour mettre à jour le contenu de cette semaine-là, qui en a bien besoin par ailleurs :)
merci de ton aide !
Bonjour Thierry, En ce qui concerne le commit 'Boucle d'événements' j'ai essayé de coller le plus possible à la vidéo parce que je pense que l'objectif c'est d'expliquer le fonctionnement de la boucle. Les tasks ne sont pas encore abordées et du coup pour la version 3.10 c'est du low level api. Je n'ai pas su utiliser loop.stop() parce-que pour que cela fonctionne j'ai ajouter un objet 'future'. Est-ce que c'est bon comme ça, je ne suis pas sur. Heureusement que tu vérifiera tout ça ;-)
Bonjour @Karduin
Je te remercie pour cette contribution; je viens de jeter un coup d'oeil et j'ai conclu qu'il allait me falloir de toutes façons passer pas mal de temps sur ce sujet ne serait-ce que pour me remettre tout ça en mémoire et bien tout vérifier avec les nouvelles versions
aussi je te suggère de ne pas passer trop de temps là-dessus; je ne sais pas trop quand j'aurai le temps de faire ce travail, et ne suis même pas sûr de ne pas complètement enlever la semaine 8 à terme, car de toute évidence elle suscite assez peu d'intérêt, et d'autre part en l'état elle est en effet assez décalée par rapport à l'état de l'art - en plus du fait que j'ai en réalité assez peu l'occasion de pratiquer...
un truc qui m'a interpelé toutefois: moi aussi à un moment j'ai été tenté de remplacer un appel à genre run_until_complete()
par un appel à asyncio.run()
, mais je me suis rendu compte à l'usage que cela ne correspond pas exactement, car si je me souviens bien asyncio.run()
va faire le ménage à la fin, et fermer la boucle, alors que ce n'était pas le cas de run_until_complete()
; mais il faut se méfier car dans le cadre d'un petit programme on ne se rend pas compte de la différence
bref, cette histoire est bien compliquée, à nouveau je ne voudrais pas que tu perdes trop de temps là-dessus :)
Bon voila les derniers. ça fonctionne correctement dans un notebook, dans ipython et dans une console. Il faut bien sur dans une console remplacer await par asyncio.run.
Effectivement asyncio.run ferme la boucle mais en créant un objet future la boucle ne se termine pas tant que l'objet n'est pas done. Après est-ce la bonne façon de faire , Je ne suis pas assez qualifié pour en juger ;-) En tous cas ça fonctionne sans erreur dans un notebook. ça ne fonctionne pas sur windows certaines fonctions ne sont pas implémentées. J'ai testé sur linux et notebook du mooc. Je présume que sur mac ça fonctionnera. Du coup j'ai fini, c’était formateur donc ne t’inquiètes pas pour le temps.
d'autre part en l'état elle est en effet assez décalée par rapport à l'état de l'art - en plus du fait que j'ai en réalité assez peu l'occasion de pratiquer..
Oui difficile d'être raccord avec les vidéos.
Si je me fis à la doc et aux erreurs pendant mes essais, asyncio.run()
utilise run_until_complete()
. Toujours d’après la doc c'est maintenant une fonction low level api comme d'autre utilisées dans game.
Toujours par rapport à la doc :
Il est préférable d'utiliser les fonctions asyncio de haut niveau, telles qu'asyncio.run(). Le référencement de l'objet boucle ou l'appel de ses méthodes s'adresse principalement aux auteurs de code, de bibliothèques et de frameworks de niveau inférieur, qui ont besoin d'un contrôle plus fin du comportement de la boucle d'événement.
game rentre dans quelle catégorie ?
C'est pour cela que j'ai fait comme ça.
async def mainloop(self):
loop = asyncio.get_running_loop() #remplace get_event_loop
fut = loop.create_future() # l'objet future s'il n'est pas done la boucle tourne indéfiniment
# on met ensemble une clock et un scheduler
clock = Clock(fut) # je passe l'objet à clock quand fut et done on arrête clock
scheduler = Scheduler(self.script, fut) #je pass fut au scheduler pour le set à done quand tout est fini
# et on fait tourner le tout
asyncio.ensure_future(clock.run())
asyncio.ensure_future(scheduler.run())
await fut
je viens de passer un peu de temps à revoir un peu tout ça
je me demandais si tu avais utilisé la 3.10 pour tes essais ?
en effet avec la 3.10 j'ai l'impression qu'il y a encore un autre petit souci, avecget_event_loop()
qui ne se comporte pas exactement comme avant apparemment..
ce qui ne me plait pas du tout dans toute cette affaire, c'est qu'il ne semble pas y avoir moyen d'écrire du code qui fonctionne à l'identique dans un notebook et dans un .py 'normal'
je vais tacher de mieux documenter tout ceci avant de décider de la meilleure approche...
Oui, j'ai travaillé avec la 3.10. Un souci de quel nature ?
ce qui ne me plait pas du tout dans toute cette affaire, c'est qu'il ne semble pas y avoir moyen d'écrire du code qui fonctionne à l'identique dans un notebook et dans un .py 'normal'
Je crois effectivement que l'on a pas le choix. La boucle existant déjà dans ipython et du coup dans les noteboooks.
Cela dit, je n'ai ni ton expérience ni ta maitrise du sujet.
J'ai pensé à quelque chose concernant un code commun, notebook /terminal.
import asyncio
async def morceaux(message):
print(message, "début")
# avec await on rend la main
await asyncio.sleep(0.5)
print(message, "milieu")
await asyncio.sleep(1)
print(message, "fin")
return f'{message} par morceaux'
async def main():
L = await asyncio.gather(
morceaux("run 1"),
morceaux("run 2"),
)
print(L)
try:
get_ipython()
asyncio.create_task(main()) #run from ipython and notebook
except:
asyncio.run(main()) #run from console
ça fonctionne bien dans dans un terminal et dans un notebook. Dans ipython, ça run correctement, mais il passe aussi par l'except
.
Je crois que l'on peux différencier ipython / notebook, en récupérant l'objet avec get_ipython
mais je bloque sur l'utilisation.
ip_or_nb = get_ipython()
dans ipython terminal j'obtiens : <IPython.terminal.interactiveshell.TerminalInteractiveShell object at 0x000002468BD6F7C0>
dans un notebook sur funmooc : <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7f02ad1e1660>
On devrait pouvoir faire quelque chose TerminalInteractiveShell vs ZMQInteractiveShell, mais je vois pas comment.
c'est une approche intéressante, mais ça me semble vraiment très compliqué je veux dire, sachant qu'on parle de la toute première vidéo où il y a du code qui tourne, je crains que ça ne déroute/dégoûte les gens qui débarquent
par ailleurs cette semaine ne fait guère recette; les statistiques indiquent que de l'ordre de 1000 personnes seulement ont ouvert les notebooks sur la semaine 8, c'est-à-dire 25x moins que le premier notebook du coup je me demande si je ne vais pas laisser tomber, c'est-à-dire enlever complètement la semaine 8, ou alors laisser le contenu tel quel, et mettre un gros avertissement sur le fait que ce n'est plus à jour...
Effectivement, ça ne vaut vraisemblablement pas le coup d'investir du temps pour cette section.
pour résumer la liste des soucis que je vois à ce stade; je parle uniquement de la toute première vidéo, celle où on fait tourner deux instances de morceaux
avec le code initial
loop = asyncio.get_event_loop()
loop.run_until_complete(morceaux("run"))
loop.run_until_complete(
asyncio.gather(morceaux("run1"),
morceaux("run2")))
fonctionne, mais affiche
/Users/tparment/git/flotpython-course/w8/w8-s2-code-in-video.py:23: DeprecationWarning: There is no current event loop
loop = asyncio.get_event_loop()
run début
run milieu
run fin
/Users/tparment/git/flotpython-course/w8/w8-s2-code-in-video.py:28: DeprecationWarning: There is no current event loop
asyncio.gather(morceaux("run1"),
run1 début
run2 début
run1 milieu
run2 milieu
run1 fin
run2 fin
idem avec new_event_loop()
plutôt que get_event_loop()
loop = asyncio.new_event_loop()
loop.run_until_complete(morceaux("run"))
loop.run_until_complete(
asyncio.gather(morceaux("run1"),
morceaux("run2")))
cette fois ça plante avec
python w8-s2-code-in-video.py
run début
run milieu
run fin
/Users/tparment/git/flotpython-course/w8/w8-s2-code-in-video.py:28: DeprecationWarning: There is no current event loop
asyncio.gather(morceaux("run1"),
Traceback (most recent call last):
File "/Users/tparment/git/flotpython-course/w8/w8-s2-code-in-video.py", line 27, in <module>
loop.run_until_complete(
File "/Users/tparment/miniconda3/envs/flotpython-course/lib/python3.10/asyncio/base_events.py", line 625, in run_until_complete
future = tasks.ensure_future(future, loop=self)
File "/Users/tparment/miniconda3/envs/flotpython-course/lib/python3.10/asyncio/tasks.py", line 615, in ensure_future
return _ensure_future(coro_or_future, loop=loop)
File "/Users/tparment/miniconda3/envs/flotpython-course/lib/python3.10/asyncio/tasks.py", line 621, in _ensure_future
raise ValueError('The future belongs to a different loop than '
ValueError: The future belongs to a different loop than the one specified as the loop argument
pareil mais on passe par une coroutine main()
qui appelle gather()
async def main():
L = await asyncio.gather(
morceaux("run 1"),
morceaux("run 2"),
)
print(L)
loop = asyncio.new_event_loop()
loop.run_until_complete(morceaux("run"))
loop.run_until_complete(main())
cette fois ça fonctionne pas mal
run début
run milieu
run fin
run 1 début
run 2 début
run 1 milieu
run 2 milieu
run 1 fin
run 2 fin
['run 1 par morceaux', 'run 2 par morceaux']
reste à documenter un peu la même démarche avec les notebooks
Mais le fait que run_until_complete()
soit du low level api ne va pas poser de problème ? Il ne faudrait pas théoriquement passer par les fonctions high level pour des petits codes comme ça ? ( enfin d’après la doc ).
Je ne sais pas qu'en penser.
honnêtement moi non plus à vrai dire à l'époque je n'avais pas trop le choix, et ça m'allait bien parce que je voulais introduire la boucle; même si c'est du low-level, c'est important de comprendre qu'il y a là derriere que boucle qui fait avancer le schmilblick
donc quelque part j'avais envie de rester le plus possible fidèle au code original
en plus parmi les essais que j'ai faits, il y avait asyncio.run()
mais si je me souviens bien je pouvais l'utiliser une seule fois, ou quelque chose dans ce goût-là, donc j'ai écarté ça, parce que dans les notebooks on a lance souvent plusieurs trucs d'affilée dans la même boucle, et ça n'est pas compatible avec l'usage de asyncio.run()
si je comprends bien
enfin j'ai eu par ailleurs les chiffres de fréquentation de cette partie sur les vidéos, et ça confirme le peu d'intérêt que tout ceci a suscité donc je pense qu'il est urgent d'attendre et de réfléchir :-)
Juste pour voir si je suis dans la bonne direction et si je continue comme ça. Est-ce que quelque chose comme ça convient ?