Repository: mne-tools/mne-python

Issue:
pyvista _Brain init too stringent about size parameter
I'm hitting the error `'"size" parameter must be int or tuple.'` I'm generating lots of similar figures, so I store several project-level parameters (including brain plotting kwargs like figure size) in an external YAML file, and pyYAML's "safe loader" doesn't support tuples, only lists.  Instead of checking `isinstance(size, tuple)` maybe we could check if it is length-2, and then convert it to tuple?  Or do it however the mayavi code did it; it works to pass `size=[1200, 1200]` with the mayavi backend.

@GuillaumeFavelier since you're the PyVista master I wanted to run this by you before just making the change... but I'm happy to do it myself if you're OK with it and you're too busy.


Discussion:
Good catch @drammock, even just for compatibility with `mayavi` I think `list` should be supported. Feel free to take care of it if you want 👍 

Resolving patch:
diff --git a/mne/viz/_brain/_brain.py b/mne/viz/_brain/_brain.py
--- a/mne/viz/_brain/_brain.py
+++ b/mne/viz/_brain/_brain.py
@@ -20,7 +20,7 @@
 
 
 class _Brain(object):
-    u"""Class for visualizing a brain.
+    """Class for visualizing a brain.
 
     It is used for creating meshes of the given subject's
     cortex. The activation data can be shown on a mesh using add_data
@@ -48,9 +48,9 @@ class _Brain(object):
         Setting this to ``None`` is equivalent to ``(0.5, 0.5, 0.5)``.
     alpha : float in [0, 1]
         Alpha level to control opacity of the cortical surface.
-    size : float | tuple(float, float)
+    size : int | array-like, shape (2,)
         The size of the window, in pixels. can be one number to specify
-        a square window, or the (width, height) of a rectangular window.
+        a square window, or a length-2 sequence to specify (width, height).
     background : tuple(int, int, int)
         The color definition of the background: (red, green, blue).
     foreground : matplotlib color
@@ -168,12 +168,11 @@ def __init__(self, subject_id, hemi, surf, title=None,
         col_dict = dict(lh=1, rh=1, both=1, split=2)
         n_col = col_dict[hemi]
 
-        if isinstance(size, int):
-            fig_size = (size, size)
-        elif isinstance(size, tuple):
-            fig_size = size
-        else:
-            raise ValueError('"size" parameter must be int or tuple.')
+        size = tuple(np.atleast_1d(size).round(0).astype(int).flat)
+        if len(size) not in (1, 2):
+            raise ValueError('"size" parameter must be an int or length-2 '
+                             'sequence of ints.')
+        fig_size = size if len(size) == 2 else size * 2  # 1-tuple to 2-tuple
 
         self._foreground = foreground
         self._hemi = hemi
@@ -250,7 +249,7 @@ def add_data(self, array, fmin=None, fmid=None, fmax=None,
                  hemi=None, remove_existing=None, time_label_size=None,
                  initial_time=None, scale_factor=None, vector_alpha=None,
                  clim=None, verbose=None):
-        u"""Display data from a numpy array on the surface.
+        """Display data from a numpy array on the surface.
 
         This provides a similar interface to
         :meth:`surfer.Brain.add_overlay`, but it displays
@@ -811,7 +810,7 @@ def screenshot(self, mode='rgb'):
         return self._renderer.screenshot(mode)
 
     def update_lut(self, fmin=None, fmid=None, fmax=None):
-        u"""Update color map.
+        """Update color map.
 
         Parameters
         ----------
@@ -1032,7 +1031,7 @@ def update_auto_scaling(self, restore=False):
 
     @property
     def data(self):
-        u"""Data used by time viewer and color bar widgets."""
+        """Data used by time viewer and color bar widgets."""
         return self._data
 
     @property
@@ -1051,7 +1050,7 @@ def _show(self):
             logger.info("No active/running renderer available.")
 
     def _check_hemi(self, hemi):
-        u"""Check for safe single-hemi input, returns str."""
+        """Check for safe single-hemi input, returns str."""
         if hemi is None:
             if self._hemi not in ['lh', 'rh']:
                 raise ValueError('hemi must not be None when both '


Test patch:
diff --git a/mne/viz/_brain/tests/test_brain.py b/mne/viz/_brain/tests/test_brain.py
--- a/mne/viz/_brain/tests/test_brain.py
+++ b/mne/viz/_brain/tests/test_brain.py
@@ -60,8 +60,8 @@ def test_brain_init(renderer):
     """Test initialization of the _Brain instance."""
     hemi = 'both'
 
-    with pytest.raises(ValueError, match='size'):
-        _Brain(subject_id=subject_id, hemi=hemi, surf=surf, size=0.5)
+    with pytest.raises(ValueError, match='"size" parameter must be'):
+        _Brain(subject_id=subject_id, hemi=hemi, surf=surf, size=[1, 2, 3])
     with pytest.raises(TypeError, match='figure'):
         _Brain(subject_id=subject_id, hemi=hemi, surf=surf, figure='foo')
     with pytest.raises(ValueError, match='interaction'):