Parfois, il faut beaucoup de temps pour exécuter une seule cellule, alors qu'il est en cours d'exécution, je voudrais écrire et exécuter d'autres cellules dans le même bloc-notes, en accédant aux variables dans le même contexte.
Existe-t-il une magie ipython pouvant être utilisée de telle sorte que lorsqu'elle est ajoutée à une cellule, l'exécution de la cellule crée automatiquement un nouveau thread et s'exécute avec des données globales partagées dans le bloc-notes?
Ce n'est peut-être pas une réponse, mais plutôt la direction à suivre. Je n'ai rien vu de tel, ça m'intéresse toujours aussi.
Mes découvertes actuelles suggèrent qu'il faut définir sa propre magie cellulaire personnalisée . De bonnes références seraient la section magique de cellule personnalisée dans la documentation et deux exemples que je considérerais:
Ces deux liens enveloppant le code dans un thread. Cela pourrait être un point de départ.
MISE À JOUR: ngcm-tutorial at github a la description de la classe des jobs d'arrière-plan
##github.com/jupyter/ngcm-tutorial/blob/master/Day-1/IPython%20Kernel/Background%20Jobs.ipynb
from IPython.lib import backgroundjobs as bg
jobs = bg.BackgroundJobManager()
def printfunc(interval=1, reps=5):
for n in range(reps):
time.sleep(interval)
print('In the background... %i' % n)
sys.stdout.flush()
print('All done!')
sys.stdout.flush()
jobs.new('printfunc(1,3)')
jobs.status()
MISE À JOUR 2: Une autre option:
from IPython.display import display
from ipywidgets import IntProgress
import threading
class App(object):
def __init__(self, nloops=2000):
self.nloops = nloops
self.pb = IntProgress(description='Thread loops', min=0, max=self.nloops)
def start(self):
display(self.pb)
while self.pb.value < self.nloops:
self.pb.value += 1
self.pb.color = 'red'
app = App(nloops=20000)
t = threading.Thread(target=app.start)
t.start()
#t.join()
Voici un petit extrait que j'ai trouvé
def jobs_manager():
from IPython.lib.backgroundjobs import BackgroundJobManager
from IPython.core.magic import register_line_magic
from IPython import get_ipython
jobs = BackgroundJobManager()
@register_line_magic
def job(line):
ip = get_ipython()
jobs.new(line, ip.user_global_ns)
return jobs
Il utilise le module intégré IPython IPython.lib.backgroundjobs
. Le code est donc petit et simple et aucune nouvelle dépendance n'est introduite.
Je l'utilise comme ceci:
jobs = jobs_manager()
%job [fetch_url(_) for _ in urls] # saves html file to disk
Starting job # 0 in a separate thread.
Ensuite, vous pouvez surveiller l'état avec:
jobs.status()
Running jobs:
1 : [fetch_url(_) for _ in urls]
Dead jobs:
0 : [fetch_url(_) for _ in urls]
Si le travail échoue, vous pouvez inspecter la trace de la pile avec
jobs.traceback(0)
Il n'y a aucun moyen de tuer un emploi. J'utilise donc soigneusement ce hack sale:
def kill_thread(thread):
import ctypes
id = thread.ident
code = ctypes.pythonapi.PyThreadState_SetAsyncExc(
ctypes.c_long(id),
ctypes.py_object(SystemError)
)
if code == 0:
raise ValueError('invalid thread id')
Elif code != 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(
ctypes.c_long(id),
ctypes.c_long(0)
)
raise SystemError('PyThreadState_SetAsyncExc failed')
Il lève SystemError
dans un thread donné. Donc, pour tuer un travail que je fais
kill_thread(jobs.all[1])
Pour tuer tous les travaux en cours que je fais
for thread in jobs.running:
kill_thread(thread)
J'aime utiliser %job
avec une barre de progression basée sur un widget https://github.com/alexanderkuk/log-progress comme ceci:
%job [fetch_url(_) for _ in log_progress(urls, every=1)]
http://g.recordit.co/iZJsJm8BOL.gif
On peut même utiliser %job
au lieu de multiprocessing.TreadPool
:
for chunk in get_chunks(urls, 3):
%job [fetch_url(_) for _ in log_progress(chunk, every=1)]
http://g.recordit.co/oTVCwugZYk.gif
Quelques problèmes évidents avec ce code:
Vous ne pouvez pas utiliser de code arbitraire dans %job
. Il ne peut pas y avoir d'affectations et pas d'impressions par exemple. Je l'utilise donc avec des routines qui stockent les résultats sur le disque dur
Parfois hack sale dans kill_thread
ne marche pas. Je pense que c'est pourquoi IPython.lib.backgroundjobs
n'a pas cette fonctionnalité de par sa conception. Si le thread effectue un appel système comme sleep
ou read
, l'exception est ignorée.
Il utilise des threads. Python a GIL, donc %job
ne peut pas être utilisé pour certains calculs lourds qui prennent en python octet code