Skip to content

Mock tornado for jupyterlite - #381

Merged
martinRenou merged 1 commit into
matplotlib:masterfrom
martinRenou:support_jupyterlite
Oct 19, 2021
Merged

martinRenou merged 1 commit into
matplotlib:masterfrom
martinRenou:support_jupyterlite

Conversation

@martinRenou

Copy link
Copy Markdown
Member

See #334

@github-actions

Copy link
Copy Markdown
Contributor

Binder 👈 Launch a binder notebook on branch martinRenou/ipympl/support_jupyterlite

@lgtm-com

lgtm-com Bot commented Oct 19, 2021

Copy link
Copy Markdown

This pull request introduces 1 alert when merging e5b2cf9 into 76111ce - view on LGTM.com

new alerts:

  • 1 for Unused import

@lgtm-com

lgtm-com Bot commented Oct 19, 2021

Copy link
Copy Markdown

This pull request introduces 1 alert when merging 011ee0f into 558cc47 - view on LGTM.com

new alerts:

  • 1 for Unused import

@martinRenou
martinRenou merged commit fb80bd5 into matplotlib:master Oct 19, 2021
@martinRenou
martinRenou deleted the support_jupyterlite branch October 19, 2021 11:21
Comment thread ipympl/backend_nbagg.py
Comment on lines +11 to +14
try:
import micropip # noqa
except ImportError:
sys.modules['tornado'] = types.ModuleType('tornadofake')

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@martinRenou I think that the mocking got put into the wrong place here? the idea is that micropip is only available in jupyterlite correct? so it should be:

try:
    import micropip  # noqa
    sys.modules['tornado'] = types.ModuleType('tornadofake')
except ImportError:
   pass

?

@ianhi

ianhi commented Oct 24, 2021

Copy link
Copy Markdown
Collaborator

This broke the save_animation functionality of mpl-interactions as that does some tricky things with animations. As in the review comment I think that the check for if we're in a pyodide context is not set up correctly.

%matplotlib ipympl
import matplotlib.pyplot as plt
import numpy as np

import mpl_interactions.ipyplot as iplt

x = np.linspace(0, 2 * np.pi, 200)


def f(x, amp, freq):
    return amp * np.sin(x * freq)


# Create the plot as normal
fig, ax = plt.subplots()
controls = iplt.plot(x, f, freq=(0.05, 10, 250), amp=(1, 10))
_ = iplt.title("the Frequency is: {freq:.2f}", controls=controls["freq"])

# save as a gif
anim = controls.save_animation("freq-plot-1.gif", fig, "freq", interval=35)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-2-6978a96bf4a5> in <module>
     18 
     19 # save as a gif
---> 20 anim = controls.save_animation("freq-plot-1.gif", fig, "freq", interval=35)

~/mambaforge/envs/micro/lib/python3.8/site-packages/mpl_interactions/controller.py in save_animation(self, filename, fig, param, interval, func_anim_kwargs, N_frames, **kwargs)
    318         # draw then stop necessary to prevent an extra loop after finished saving
    319         # see https://discourse.matplotlib.org/t/how-to-prevent-funcanimation-looping-a-single-time-after-save/21680/2
--> 320         fig.canvas.draw()
    321         anim.event_source.stop()
    322         anim.save(filename, **kwargs)

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/backends/backend_webagg_core.py in draw(self)
    151         self._png_is_old = True
    152         try:
--> 153             super().draw()
    154         finally:
    155             self.manager.refresh_all()  # Swap the frames.

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py in draw(self)
    404              (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
    405               else nullcontext()):
--> 406             self.figure.draw(self.renderer)
    407             # A GUI class may be need to update a window using this draw, so
    408             # don't forget to call the superclass.

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     72     @wraps(draw)
     73     def draw_wrapper(artist, renderer, *args, **kwargs):
---> 74         result = draw(artist, renderer, *args, **kwargs)
     75         if renderer._rasterizing:
     76             renderer.stop_rasterizing()

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     49                 renderer.start_filter()
     50 
---> 51             return draw(artist, renderer, *args, **kwargs)
     52         finally:
     53             if artist.get_agg_filter() is not None:

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/figure.py in draw(self, renderer)
   2788             self.stale = False
   2789 
-> 2790         self.canvas.draw_event(renderer)
   2791 
   2792     def draw_artist(self, a):

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/backend_bases.py in draw_event(self, renderer)
   1790         s = 'draw_event'
   1791         event = DrawEvent(s, self, renderer)
-> 1792         self.callbacks.process(s, event)
   1793 
   1794     def resize_event(self):

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/cbook/__init__.py in process(self, s, *args, **kwargs)
    273                 except Exception as exc:
    274                     if self.exception_handler is not None:
--> 275                         self.exception_handler(exc)
    276                     else:
    277                         raise

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/cbook/__init__.py in _exception_printer(exc)
     87 def _exception_printer(exc):
     88     if _get_running_interactive_framework() in ["headless", None]:
---> 89         raise exc
     90     else:
     91         traceback.print_exc()

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/cbook/__init__.py in process(self, s, *args, **kwargs)
    268             if func is not None:
    269                 try:
--> 270                     func(*args, **kwargs)
    271                 # this does not capture KeyboardInterrupt, SystemExit,
    272                 # and GeneratorExit

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/animation.py in _start(self, *args)
    994         # actually start the event_source.
    995         self.event_source.add_callback(self._step)
--> 996         self.event_source.start()
    997 
    998     def _stop(self, *args):

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/backend_bases.py in start(self, interval)
   1132         if interval is not None:
   1133             self.interval = interval
-> 1134         self._timer_start()
   1135 
   1136     def stop(self):

~/mambaforge/envs/micro/lib/python3.8/site-packages/matplotlib/backends/backend_webagg_core.py in _timer_start(self)
     93                 self._on_timer)
     94         else:
---> 95             self._timer = tornado.ioloop.PeriodicCallback(
     96                 self._on_timer,
     97                 max(self.interval, 1e-6))

AttributeError: module 'tornadofake' has no attribute 'ioloop'

@ianhi

ianhi commented Oct 24, 2021

Copy link
Copy Markdown
Collaborator

simpler matplotlib only reproduction:

%matplotlib ipympl

from matplotlib import pyplot as plt
from matplotlib import animation

fig = plt.figure()

def animate(i):
    pass

anim = animation.FuncAnimation(fig, animate)
fig.canvas.draw()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants