Skip to content

Commit

Permalink
FIX: Fix problematic bound methods [circle full]
Browse files Browse the repository at this point in the history
  • Loading branch information
larsoner committed Oct 20, 2020
1 parent 37da9e1 commit 8abd6ff
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 24 deletions.
21 changes: 21 additions & 0 deletions mne/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,27 @@ def download_is_error(monkeypatch):
monkeypatch.setattr(mne.utils.fetching, '_get_http', _fail)


@pytest.fixture()
def brain_gc():
"""Ensure that brain can be properly garbage collected."""
from mne.viz import Brain
gc.collect()
n = sum(isinstance(obj, Brain) for obj in gc.get_objects())
assert n == 0, f'{n} before'
yield
gc.collect()
n = 0
ref = list()
new = '\n'
for obj in gc.get_objects():
if isinstance(obj, Brain):
n += 1
ref.extend([
f'{r.__class__.__name__}: {repr(r)[:100].replace(new, " ")}'
for r in gc.get_referrers(obj)])
assert n == 0, f'{n} after:\n{new.join(ref)}'


def pytest_sessionfinish(session, exitstatus):
"""Handle the end of the session."""
n = session.config.option.durations
Expand Down
23 changes: 12 additions & 11 deletions mne/viz/_brain/_brain.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ def setup_time_viewer(self, time_viewer=True, show_traces=True):

# Direct access parameters:
self.plotter = self._renderer.plotter
self._iren = self._renderer.plotter.iren
self.main_menu = self.plotter.main_menu
self.window = self.plotter.app_window
self.tool_bar = self.window.addToolBar("toolbar")
Expand Down Expand Up @@ -428,20 +429,14 @@ def _clean(self):
self._clear_callbacks()
self.actions.clear()
self.sliders.clear()
self.reps = None
self.plotter = None
self.main_menu = None
self.window = None
self.tool_bar = None
self.status_bar = None
self.interactor = None
if self.mpl_canvas is not None:
self.mpl_canvas.clear()
self.mpl_canvas = None
self.time_actor = None
self.picked_renderer = None
for key in list(self.act_data_smooth.keys()):
self.act_data_smooth[key] = None
for key in ('reps', 'plotter', 'main_menu', 'window', 'tool_bar',
'status_bar', 'interactor', 'mpl_canvas', 'time_actor',
'picked_renderer', 'act_data_smooth', '_iren'):
setattr(self, key, None)

@contextlib.contextmanager
def ensure_minimum_sizes(self):
Expand Down Expand Up @@ -922,6 +917,9 @@ def _load_icons(self):
self.icons["visibility_on"] = QIcon(":/visibility_on.svg")
self.icons["visibility_off"] = QIcon(":/visibility_off.svg")

def _save_movie_noname(self):
return self.save_movie(None)

def _configure_tool_bar(self):
self.actions["screenshot"] = self.tool_bar.addAction(
self.icons["screenshot"],
Expand All @@ -931,7 +929,7 @@ def _configure_tool_bar(self):
self.actions["movie"] = self.tool_bar.addAction(
self.icons["movie"],
"Save movie...",
partial(self.save_movie, filename=None)
self._save_movie_noname,
)
self.actions["visibility"] = self.tool_bar.addAction(
self.icons["visibility_on"],
Expand Down Expand Up @@ -1308,6 +1306,7 @@ def help(self):
)

def _clear_callbacks(self):
from ..backends._pyvista import _remove_picking_callback
for callback in self.callbacks.values():
if callback is not None:
if hasattr(callback, "plotter"):
Expand All @@ -1317,6 +1316,8 @@ def _clear_callbacks(self):
if hasattr(callback, "slider_rep"):
callback.slider_rep = None
self.callbacks.clear()
if self.show_traces:
_remove_picking_callback(self._iren, self.plotter.picker)

@property
def interaction(self):
Expand Down
20 changes: 14 additions & 6 deletions mne/viz/_brain/tests/test_brain.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def GetPosition(self):


@testing.requires_testing_data
def test_brain_init(renderer, tmpdir, pixel_ratio):
def test_brain_init(renderer, tmpdir, pixel_ratio, brain_gc):
"""Test initialization of the Brain instance."""
from mne.label import read_label
hemi = 'lh'
Expand Down Expand Up @@ -229,7 +229,7 @@ def test_brain_init(renderer, tmpdir, pixel_ratio):

@testing.requires_testing_data
@pytest.mark.slowtest
def test_brain_save_movie(tmpdir, renderer):
def test_brain_save_movie(tmpdir, renderer, brain_gc):
"""Test saving a movie of a Brain instance."""
if renderer._get_3d_backend() == "mayavi":
pytest.skip('Save movie only supported on PyVista')
Expand All @@ -243,7 +243,7 @@ def test_brain_save_movie(tmpdir, renderer):

@testing.requires_testing_data
@pytest.mark.slowtest
def test_brain_time_viewer(renderer_interactive, pixel_ratio):
def test_brain_time_viewer(renderer_interactive, pixel_ratio, brain_gc):
"""Test time viewer primitives."""
if renderer_interactive._get_3d_backend() != 'pyvista':
pytest.skip('TimeViewer tests only supported on PyVista')
Expand Down Expand Up @@ -285,6 +285,7 @@ def test_brain_time_viewer(renderer_interactive, pixel_ratio):
img = brain.screenshot(mode='rgb')
want_shape = np.array([300 * pixel_ratio, 300 * pixel_ratio, 3])
assert_allclose(img.shape, want_shape)
brain.close()


@testing.requires_testing_data
Expand All @@ -300,7 +301,8 @@ def test_brain_time_viewer(renderer_interactive, pixel_ratio):
pytest.param('mixed', marks=pytest.mark.slowtest),
])
@pytest.mark.slowtest
def test_brain_traces(renderer_interactive, hemi, src, tmpdir):
def test_brain_traces(renderer_interactive, hemi, src, tmpdir,
brain_gc):
"""Test brain traces."""
if renderer_interactive._get_3d_backend() != 'pyvista':
pytest.skip('Only PyVista supports traces')
Expand Down Expand Up @@ -393,6 +395,7 @@ def test_brain_traces(renderer_interactive, hemi, src, tmpdir):
# only test one condition to save time
if not (hemi == 'rh' and src == 'surface' and
check_version('sphinx_gallery')):
brain.close()
return
fnames = [str(tmpdir.join(f'temp_{ii}.png')) for ii in range(2)]
block_vars = dict(image_path_iterator=iter(fnames),
Expand All @@ -406,6 +409,7 @@ def test_brain_traces(renderer_interactive, hemi, src, tmpdir):
gallery_conf = dict(src_dir=str(tmpdir), compress_images=[])
scraper = _BrainScraper()
rst = scraper(block, block_vars, gallery_conf)
assert brain.plotter is None # closed
gif_0 = fnames[0][:-3] + 'gif'
for fname in (gif_0, fnames[1]):
assert path.basename(fname) in rst
Expand All @@ -418,7 +422,7 @@ def test_brain_traces(renderer_interactive, hemi, src, tmpdir):

@testing.requires_testing_data
@pytest.mark.slowtest
def test_brain_linkviewer(renderer_interactive):
def test_brain_linkviewer(renderer_interactive, brain_gc):
"""Test _LinkViewer primitives."""
if renderer_interactive._get_3d_backend() != 'pyvista':
pytest.skip('Linkviewer only supported on PyVista')
Expand Down Expand Up @@ -449,9 +453,13 @@ def test_brain_linkviewer(renderer_interactive):
link_viewer.set_fmax(1)
link_viewer.set_playback_speed(value=0.1)
link_viewer.toggle_playback()
del link_viewer
brain1.close()
brain2.close()
brain_data.close()


def test_brain_colormap():
def test_brain_colormap(brain_gc):
"""Test brain's colormap functions."""
colormap = "coolwarm"
alpha = 1.0
Expand Down
2 changes: 1 addition & 1 deletion mne/viz/_brain/tests/test_notebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@requires_version('nbformat')
@requires_version('nbclient')
@requires_version('ipympl')
def test_notebook_3d_backend(renderer_notebook):
def test_notebook_3d_backend(renderer_notebook, brain_gc):
"""Test executing a notebook that should not fail."""
import nbformat
from nbclient import NotebookClient
Expand Down
7 changes: 7 additions & 0 deletions mne/viz/backends/_pyvista.py
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,13 @@ def _update_picking_callback(plotter,
plotter.picker = picker


def _remove_picking_callback(interactor, picker):
interactor.RemoveObservers(vtk.vtkCommand.RenderEvent)
interactor.RemoveObservers(vtk.vtkCommand.LeftButtonPressEvent)
interactor.RemoveObservers(vtk.vtkCommand.EndInteractionEvent)
picker.RemoveObservers(vtk.vtkCommand.EndPickEvent)


def _arrow_glyph(grid, factor):
glyph = vtk.vtkGlyphSource2D()
glyph.SetGlyphTypeToArrow()
Expand Down
12 changes: 6 additions & 6 deletions mne/viz/tests/test_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def test_plot_head_positions():
@requires_pysurfer
@traits_test
@pytest.mark.slowtest
def test_plot_sparse_source_estimates(renderer_interactive):
def test_plot_sparse_source_estimates(renderer_interactive, brain_gc):
"""Test plotting of (sparse) source estimates."""
sample_src = read_source_spaces(src_fname)

Expand All @@ -121,10 +121,10 @@ def test_plot_sparse_source_estimates(renderer_interactive):
stc = SourceEstimate(stc_data, vertices, 1, 1)

colormap = 'mne_analyze'
plot_source_estimates(stc, 'sample', colormap=colormap,
background=(1, 1, 0),
subjects_dir=subjects_dir, colorbar=True,
clim='auto')
brain = plot_source_estimates(
stc, 'sample', colormap=colormap, background=(1, 1, 0),
subjects_dir=subjects_dir, colorbar=True, clim='auto')
brain.close()
pytest.raises(TypeError, plot_source_estimates, stc, 'sample',
figure='foo', hemi='both', clim='auto',
subjects_dir=subjects_dir)
Expand Down Expand Up @@ -574,7 +574,7 @@ def test_snapshot_brain_montage(renderer):
@pytest.mark.parametrize('pick_ori', ('vector', None))
@pytest.mark.parametrize('kind', ('surface', 'volume', 'mixed'))
def test_plot_source_estimates(renderer_interactive, all_src_types_inv_evoked,
pick_ori, kind):
pick_ori, kind, brain_gc):
"""Test plotting of scalar and vector source estimates."""
invs, evoked = all_src_types_inv_evoked
inv = invs[kind]
Expand Down

0 comments on commit 8abd6ff

Please sign in to comment.