Skip to content

Commit c43081d

Browse files
authored
Merge pull request #21 from eriknw/fix_ipywidgets
Fix displaying of outputs
2 parents d34e022 + 976da66 commit c43081d

File tree

4 files changed

+54
-33
lines changed

4 files changed

+54
-33
lines changed

afar/_magic.py renamed to afar/_abra.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def __setstate__(self, state):
8181
self._scoped = scoped_function(func, outer_scope)
8282

8383

84-
def abracadabra(runner):
84+
def cadabra(runner):
8585
# Create a new function from the code block of the context.
8686
# For now, we require that the source code is available.
8787
source = "def _afar_magic_():\n" + "".join(runner.context_body)

afar/_core.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
from dask import distributed
77

8-
from ._magic import abracadabra
9-
from ._printing import RecordPrint, print_outputs, print_outputs_async
8+
from ._abra import cadabra
9+
from ._printing import PrintRecorder, print_outputs, print_outputs_async
1010
from ._reprs import repr_afar
1111
from ._utils import is_kernel, supports_async_output
1212
from ._where import find_where
@@ -185,7 +185,7 @@ def _exit(self, exc_type, exc_value, exc_traceback):
185185
endline = maxline + 5 # give us some wiggle room
186186

187187
self.context_body = get_body(self._lines[self._body_start : endline])
188-
self._magic_func, names, futures = abracadabra(self)
188+
self._magic_func, names, futures = cadabra(self)
189189
display_expr = self._magic_func._display_expr
190190

191191
if self._where == "remotely":
@@ -267,9 +267,7 @@ def _exit(self, exc_type, exc_value, exc_traceback):
267267

268268
out = Output()
269269
display(out)
270-
with out:
271-
print("\N{SPARKLES} Running afar... \N{SPARKLES}")
272-
# Can we show `distributed.progress` right here?
270+
out.append_stdout("\N{SPARKLES} Running afar... \N{SPARKLES}")
273271
stdout_future.add_done_callback(
274272
partial(print_outputs_async, out, stderr_future, repr_future)
275273
)
@@ -316,7 +314,7 @@ class Get(Run):
316314

317315
def run_afar(magic_func, names, futures, capture_print):
318316
if capture_print:
319-
rec = RecordPrint()
317+
rec = PrintRecorder()
320318
if "print" in magic_func._scoped.builtin_names and "print" not in futures:
321319
sfunc = magic_func._scoped.bind(futures, print=rec)
322320
else:

afar/_printing.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import sys
33
from io import StringIO
44
from threading import Lock, local
5+
from time import sleep
56

67
from ._reprs import display_repr
78

@@ -16,7 +17,7 @@ def __call__(self, *args, **kwargs):
1617
return self.printer(*args, **kwargs)
1718

1819

19-
class RecordPrint:
20+
class PrintRecorder:
2021
n = 0
2122
local_print = LocalPrint()
2223
print_lock = Lock()
@@ -27,17 +28,17 @@ def __init__(self):
2728

2829
def __enter__(self):
2930
with self.print_lock:
30-
if RecordPrint.n == 0:
31+
if PrintRecorder.n == 0:
3132
LocalPrint.printer = builtins.print
3233
builtins.print = self.local_print
33-
RecordPrint.n += 1
34+
PrintRecorder.n += 1
3435
self.local_print.printer = self
3536
return self
3637

3738
def __exit__(self, exc_type, exc_value, exc_traceback):
3839
with self.print_lock:
39-
RecordPrint.n -= 1
40-
if RecordPrint.n == 0:
40+
PrintRecorder.n -= 1
41+
if PrintRecorder.n == 0:
4142
builtins.print = LocalPrint.printer
4243
self.local_print.printer = LocalPrint.printer
4344
return False
@@ -72,18 +73,30 @@ def print_outputs_async(out, stderr_future, repr_future, stdout_future):
7273
7374
This is used as a callback to `stdout_future`.
7475
"""
75-
stdout_val = stdout_future.result()
76-
stderr_val = stderr_future.result()
77-
if repr_future is not None:
78-
repr_val = repr_future.result()
79-
else:
80-
repr_val = None
81-
out.clear_output()
82-
if stdout_val or stderr_val or repr_val is not None:
83-
with out:
84-
if stdout_val:
85-
print(stdout_val, end="")
86-
if stderr_val:
87-
print(stderr_val, end="", file=sys.stderr)
76+
try:
77+
stdout_val = stdout_future.result()
78+
out.clear_output()
79+
count = 0
80+
while out.outputs:
81+
# See: https://github.com/jupyter-widgets/ipywidgets/issues/3260
82+
count += 1
83+
if count == 100: # 0.5 seconds
84+
# This doesn't appear to always clear correctly in JupyterLab.
85+
# I don't know why. I'm still investigating.
86+
out.outputs = type(out.outputs)() # is this safe?
87+
break
88+
sleep(0.005)
89+
if stdout_val:
90+
out.append_stdout(stdout_val)
91+
92+
stderr_val = stderr_future.result()
93+
if stderr_val:
94+
out.append_stderr(stderr_val)
95+
96+
if repr_future is not None:
97+
repr_val = repr_future.result()
8898
if repr_val is not None:
89-
display_repr(repr_val)
99+
display_repr(repr_val, out=out)
100+
except Exception as exc:
101+
print(exc, file=sys.stderr)
102+
raise

afar/_reprs.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,18 +84,28 @@ def __repr__(self):
8484
return self.val
8585

8686

87-
def display_repr(results):
87+
def display_repr(results, out=None):
8888
"""Display results from `repr_afar` locally in IPython/Jupyter"""
8989
val, method_name, is_exception = results
9090
if is_exception:
91-
print(val, file=sys.stderr)
91+
if out is None:
92+
print(val, file=sys.stderr)
93+
else:
94+
out.append_stderr(val)
9295
return
9396
if val is None and method_name is None:
9497
return
98+
99+
from IPython.display import display
100+
95101
if method_name == "_ipython_display_":
96-
val._ipython_display_()
102+
if out is None:
103+
display(val)
104+
else:
105+
out.append_display_data(val)
97106
else:
98-
from IPython.display import display
99-
100107
mimic = MimicRepr(val, method_name)
101-
display(mimic)
108+
if out is None:
109+
display(mimic)
110+
else:
111+
out.append_display_data(mimic)

0 commit comments

Comments
 (0)