diff --git a/Makefile b/Makefile index a68e628828c..47395575c99 100644 --- a/Makefile +++ b/Makefile @@ -97,7 +97,7 @@ upload-pipy: flake: @if command -v flake8 > /dev/null; then \ echo "Running flake8"; \ - flake8 --count mne examples tutorials setup.py; \ + flake8 --count; \ else \ echo "flake8 not found, please install it!"; \ exit 1; \ diff --git a/doc/conf.py b/doc/conf.py index 8ce82d05bec..aaf23c34c17 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -71,12 +71,12 @@ ] linkcheck_ignore = [ - 'https://doi.org/10.1088/0031-9155/57/7/1937', # 403 Client Error: Forbidden for url: http://iopscience.iop.org/article/10.1088/0031-9155/57/7/1937/meta - 'https://doi.org/10.1088/0031-9155/51/7/008', # 403 Client Error: Forbidden for url: https://iopscience.iop.org/article/10.1088/0031-9155/51/7/008 - 'https://sccn.ucsd.edu/wiki/.*', # HTTPSConnectionPool(host='sccn.ucsd.edu', port=443): Max retries exceeded with url: /wiki/Firfilt_FAQ (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:847)'),)) - 'https://docs.python.org/dev/howto/logging.html', # ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer')) - 'https://docs.python.org/3/library/.*', # ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer')) - 'https://hal.archives-ouvertes.fr/hal-01848442/', # Sometimes: 503 Server Error: Service Unavailable for url: https://hal.archives-ouvertes.fr/hal-01848442/ + 'https://doi.org/10.1088/0031-9155/57/7/1937', # noqa 403 Client Error: Forbidden for url: http://iopscience.iop.org/article/10.1088/0031-9155/57/7/1937/meta + 'https://doi.org/10.1088/0031-9155/51/7/008', # noqa 403 Client Error: Forbidden for url: https://iopscience.iop.org/article/10.1088/0031-9155/51/7/008 + 'https://sccn.ucsd.edu/wiki/.*', # noqa HTTPSConnectionPool(host='sccn.ucsd.edu', port=443): Max retries exceeded with url: /wiki/Firfilt_FAQ (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:847)'),)) + 'https://docs.python.org/dev/howto/logging.html', # noqa ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer')) + 'https://docs.python.org/3/library/.*', # noqa ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer')) + 'https://hal.archives-ouvertes.fr/hal-01848442/', # noqa Sometimes: 503 Server Error: Service Unavailable for url: https://hal.archives-ouvertes.fr/hal-01848442/ ] linkcheck_anchors = False # saves a bit of time @@ -95,7 +95,7 @@ source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' @@ -120,13 +120,13 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. unused_docs = [] @@ -140,15 +140,15 @@ default_role = "py:obj" # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'default' @@ -157,7 +157,7 @@ modindex_common_prefix = ['mne.'] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # -- Options for HTML output ---------------------------------------------- @@ -189,10 +189,10 @@ # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. @@ -220,27 +220,27 @@ # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False @@ -250,12 +250,12 @@ html_show_sphinx = False # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # variables to pass to HTML templating engine build_dev_html = bool(int(os.environ.get('BUILD_DEV_HTML', False))) @@ -264,7 +264,7 @@ 'use_media_buttons': True, 'build_dev_html': build_dev_html} # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'mne-doc' @@ -282,8 +282,8 @@ # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ -# ('index', 'MNE.tex', u'MNE Manual', -# u'MNE Contributors', 'manual'), + # ('index', 'MNE.tex', u'MNE Manual', + # u'MNE Contributors', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -463,7 +463,7 @@ def reset_warnings(gallery_conf, fname): 'scipy.* is deprecated and will be removed in', # dipy r'Converting `np\.character` to a dtype is deprecated', # vtk r'sphinx\.util\.smartypants is deprecated', - 'is a deprecated alias for the builtin', # NumPy + 'is a deprecated alias for the builtin', # NumPy ): warnings.filterwarnings( # deal with other modules having bad imports 'ignore', message=".*%s.*" % key, category=DeprecationWarning) @@ -622,7 +622,6 @@ def reset_warnings(gallery_conf, fname): 'n_elp', 'n_pts', 'n_tris', 'n_nodes', 'n_nonzero', 'n_events_out', 'n_segments', 'n_orient_inv', 'n_orient_fwd', 'n_orient', 'n_dipoles_lcmv', 'n_dipoles_fwd', - # Undocumented (on purpose) 'RawKIT', 'RawEximia', 'RawEGI', 'RawEEGLAB', 'RawEDF', 'RawCTF', 'RawBTi', 'RawBrainVision', 'RawCurry', 'RawNIRX', 'RawGDF', diff --git a/doc/conftest.py b/doc/conftest.py index 3c366864308..102c338598e 100644 --- a/doc/conftest.py +++ b/doc/conftest.py @@ -1 +1 @@ -from mne.conftest import * +from mne.conftest import * # noqa diff --git a/doc/sphinxext/flow_diagram.py b/doc/sphinxext/flow_diagram.py index abae1376afc..55bbf4aa8d0 100644 --- a/doc/sphinxext/flow_diagram.py +++ b/doc/sphinxext/flow_diagram.py @@ -149,8 +149,8 @@ def generate_flow_diagram(app): del node g.get_node('legend').attr.update(shape='plaintext', margin=0, rank='sink') # put legend in same rank/level as inverse - l = g.add_subgraph(['legend', 'inv'], name='legendy') - l.graph_attr['rank'] = 'same' + leg = g.add_subgraph(['legend', 'inv'], name='legendy') + leg.graph_attr['rank'] = 'same' g.layout('dot') g.draw(out_fname, format='svg') diff --git a/environment.yml b/environment.yml index 0bee8a1848f..88d43dceb00 100644 --- a/environment.yml +++ b/environment.yml @@ -4,7 +4,6 @@ channels: dependencies: - python>=3.5 - pip -- mkl - numpy - scipy - matplotlib @@ -33,8 +32,7 @@ dependencies: - mne - https://github.com/numpy/numpydoc/archive/master.zip - imageio-ffmpeg>=0.4.1 - - vtk<8.2; platform_system == "Darwin" - - vtk; platform_system != "Darwin" + - vtk>=9.0.1 - pyvista>=0.24 - pyvistaqt - https://github.com/enthought/mayavi/archive/master.zip diff --git a/mne/beamformer/_compute_beamformer.py b/mne/beamformer/_compute_beamformer.py index 81f194a5de3..7ad11dda471 100644 --- a/mne/beamformer/_compute_beamformer.py +++ b/mne/beamformer/_compute_beamformer.py @@ -12,6 +12,7 @@ from scipy import linalg from ..cov import Covariance, make_ad_hoc_cov +from ..fixes import dict_ from ..forward.forward import is_fixed_orient, _restrict_forward_to_src_sel from ..io.proj import make_projector, Projection from ..minimum_norm.inverse import _get_vertno, _prepare_forward @@ -406,7 +407,7 @@ def _compute_power(Cm, W, n_orient): return source_power -class Beamformer(dict): +class Beamformer(dict_): """A computed beamformer. Notes diff --git a/mne/bem.py b/mne/bem.py index 5af9e18e15a..df15fea9625 100644 --- a/mne/bem.py +++ b/mne/bem.py @@ -33,7 +33,7 @@ from .utils import (verbose, logger, run_subprocess, get_subjects_dir, warn, _pl, _validate_type, _TempDir, _check_freesurfer_home, _check_fname, has_nibabel) -from .fixes import einsum +from .fixes import einsum, dict_ # ############################################################################ @@ -47,7 +47,7 @@ # -class ConductorModel(dict): +class ConductorModel(dict_): """BEM or sphere model.""" def __repr__(self): # noqa: D105 diff --git a/mne/conftest.py b/mne/conftest.py index 3bcf30f97e7..d6200bc8099 100644 --- a/mne/conftest.py +++ b/mne/conftest.py @@ -297,14 +297,8 @@ def _check_skip_backend(name): def renderer_notebook(): """Verify that pytest_notebook is installed.""" from mne.viz.backends.renderer import _use_test_3d_backend - try: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - from pytest_notebook import execution - except ImportError: - pytest.skip("Test skipped, requires pytest-notebook") with _use_test_3d_backend('notebook'): - yield execution + yield @pytest.fixture(scope='function', params=[testing._pytest_param()]) diff --git a/mne/cov.py b/mne/cov.py index 4e5589bde7d..ddc62bfe63f 100644 --- a/mne/cov.py +++ b/mne/cov.py @@ -40,7 +40,7 @@ from . import viz from .fixes import (BaseEstimator, EmpiricalCovariance, _logdet, - empirical_covariance, log_likelihood) + empirical_covariance, log_likelihood, dict_) def _check_covs_algebra(cov1, cov2): @@ -63,7 +63,7 @@ def _get_tslice(epochs, tmin, tmax): return tslice -class Covariance(dict): +class Covariance(dict_): """Noise covariance matrix. .. warning:: This class should not be instantiated directly, but diff --git a/mne/fixes.py b/mne/fixes.py index 3b0b5bfc2b2..3bcc70f31e3 100644 --- a/mne/fixes.py +++ b/mne/fixes.py @@ -1091,3 +1091,61 @@ def mean(array, axis): @contextmanager def nullcontext(enter_result=None): yield enter_result + + +############################################################################### +# Work around autosummary making bad :class: links + +class dict_(dict): + + def clear(self): + """D.clear() -> None + + Remove all items from D. + """ + return super().clear() + + def copy(self): + """D.copy() -> dict + + a shallow copy of D.""" + return super().copy() + + def items(self): + """D.items() -> object + + A set-like object providing a view on D's items. + """ + return super().items() + + def keys(self): + """D.keys() -> object + + A set-like object providing a view on D's keys. + """ + return super().keys() + + def pop(self, k, *args, **kwargs): + """D.pop(k[,d]) -> object + + Remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + """ + return super().pop(k, *args, **kwargs) + + def update(self, *E, **F): + """D.update([E, ]**F) -> None + + Update D from dict/iterable E and F. + If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] + If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v + In either case, this is followed by: for k in F: D[k] = F[k] + """ + return super().update(*E, **F) + + def values(self): + """D.values() -> object + + An object providing a view on D's values. + """ + return super().values() diff --git a/mne/forward/forward.py b/mne/forward/forward.py index ac80fd7f774..67aa4ce18b7 100644 --- a/mne/forward/forward.py +++ b/mne/forward/forward.py @@ -47,10 +47,10 @@ _validate_type, _check_compensation_grade, _check_option, _check_stc_units, _stamp_to_dt) from ..label import Label -from ..fixes import einsum +from ..fixes import einsum, dict_ -class Forward(dict): +class Forward(dict_): """Forward class to represent info from forward solution. Attributes diff --git a/mne/io/meas_info.py b/mne/io/meas_info.py index 33fc6b7960b..ec02f0acaf5 100644 --- a/mne/io/meas_info.py +++ b/mne/io/meas_info.py @@ -17,6 +17,7 @@ import numpy as np from scipy import linalg +from ..fixes import dict_ from .pick import channel_type, pick_channels, pick_info from .constants import FIFF from .open import fiff_open @@ -192,7 +193,7 @@ def _format_trans(obj, key): # XXX Eventually this should be de-duplicated with the MNE-MATLAB stuff... -class Info(dict, MontageMixin): +class Info(dict_, MontageMixin): """Measurement information. This data structure behaves like a dictionary. It contains all metadata diff --git a/mne/io/proj.py b/mne/io/proj.py index 0822d16af84..733a160517a 100644 --- a/mne/io/proj.py +++ b/mne/io/proj.py @@ -19,10 +19,11 @@ from .write import (write_int, write_float, write_string, write_name_list, write_float_matrix, end_block, start_block) from ..defaults import _BORDER_DEFAULT, _EXTRAPOLATE_DEFAULT +from ..fixes import dict_ from ..utils import logger, verbose, warn, fill_doc -class Projection(dict): +class Projection(dict_): """Projection vector. A basic class to proj a meaningful print for projection vectors. diff --git a/mne/minimum_norm/inverse.py b/mne/minimum_norm/inverse.py index 41c7a442bff..bdfecc81520 100644 --- a/mne/minimum_norm/inverse.py +++ b/mne/minimum_norm/inverse.py @@ -11,7 +11,7 @@ from scipy import linalg from ._eloreta import _compute_eloreta -from ..fixes import _safe_svd +from ..fixes import _safe_svd, dict_ from ..io.constants import FIFF from ..io.open import fiff_open from ..io.tag import find_tag @@ -46,7 +46,7 @@ INVERSE_METHODS = ('MNE', 'dSPM', 'sLORETA', 'eLORETA') -class InverseOperator(dict): +class InverseOperator(dict_): """InverseOperator class to represent info from inverse operator.""" def copy(self): diff --git a/mne/tests/test_docstring_parameters.py b/mne/tests/test_docstring_parameters.py index 4aee7b43610..2c545e67569 100644 --- a/mne/tests/test_docstring_parameters.py +++ b/mne/tests/test_docstring_parameters.py @@ -9,6 +9,7 @@ import pytest import mne +from mne.fixes import dict_ from mne.utils import run_tests_if_main, requires_numpydoc, _pl public_modules = [ @@ -86,8 +87,8 @@ def _func_name(func, cls=None): # XXX should maybe also restore the parameter-desc-length < 800 char check } subclass_name_ignores = ( - (dict, {'values', 'setdefault', 'popitems', 'keys', 'pop', 'update', - 'copy', 'popitem', 'get', 'items', 'fromkeys'}), + (dict_, {'values', 'setdefault', 'popitems', 'keys', 'pop', 'update', + 'copy', 'popitem', 'get', 'items', 'fromkeys', 'clear'}), (list, {'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'sort'}), (mne.fixes.BaseEstimator, {'get_params', 'set_params', 'fit_transform'}), diff --git a/mne/transforms.py b/mne/transforms.py index a884ff1885d..05228fc96b9 100644 --- a/mne/transforms.py +++ b/mne/transforms.py @@ -14,7 +14,7 @@ from copy import deepcopy from scipy import linalg -from .fixes import einsum, jit, mean +from .fixes import einsum, jit, mean, dict_ from .io.constants import FIFF from .io.open import fiff_open from .io.tag import read_tag @@ -70,7 +70,7 @@ def _to_const(cf): return int(cf) -class Transform(dict): +class Transform(dict_): """A transform. Parameters diff --git a/mne/viz/_3d.py b/mne/viz/_3d.py index 299ef72dcf9..cdc9ef54a95 100644 --- a/mne/viz/_3d.py +++ b/mne/viz/_3d.py @@ -14,6 +14,7 @@ from itertools import cycle import os import os.path as op +import sys import warnings from collections.abc import Iterable from functools import partial @@ -2441,10 +2442,10 @@ def plot_vector_source_estimates(stc, subject=None, hemi='lh', colormap='hot', if brain_alpha < 1.0: for ff in brain._figures: for f in ff: - if f.scene is not None: + if f.scene is not None and sys.platform != 'darwin': f.scene.renderer.use_depth_peeling = True else: - if brain_alpha < 1.0: + if brain_alpha < 1.0 and sys.platform != 'darwin': brain.enable_depth_peeling() _check_time_viewer_compatibility(brain, time_viewer, show_traces) diff --git a/mne/viz/_brain/tests/test_notebook.py b/mne/viz/_brain/tests/test_notebook.py index d8fa65e6f9b..22888b337ae 100644 --- a/mne/viz/_brain/tests/test_notebook.py +++ b/mne/viz/_brain/tests/test_notebook.py @@ -10,13 +10,14 @@ @pytest.mark.slowtest @testing.requires_testing_data @requires_version('nbformat') +@requires_version('nbclient') def test_notebook_3d_backend(renderer_notebook): """Test executing a notebook that should not fail.""" import nbformat + from nbclient import NotebookClient - notebook = nbformat.read( - os.path.join(PATH, "test.ipynb"), as_version=4, - ) - exec_results = renderer_notebook.execute_notebook(notebook) - if exec_results.exec_error: - raise exec_results.exec_error + notebook_filename = os.path.join(PATH, "test.ipynb") + with open(notebook_filename) as f: + nb = nbformat.read(f, as_version=4) + client = NotebookClient(nb) + client.execute() diff --git a/mne/viz/backends/_pyvista.py b/mne/viz/backends/_pyvista.py index e45a3310746..11d1fb8a01b 100644 --- a/mne/viz/backends/_pyvista.py +++ b/mne/viz/backends/_pyvista.py @@ -13,6 +13,7 @@ from contextlib import contextmanager import os +import sys import warnings import numpy as np @@ -122,7 +123,8 @@ def _enable_aa(figure, plotter): if os.getenv('AZURE_CI_WINDOWS', 'false').lower() == 'true': return if figure.is_active(): - plotter.enable_anti_aliasing() + if sys.platform != 'darwin': + plotter.enable_anti_aliasing() plotter.ren_win.LineSmoothingOn() diff --git a/server_environment.yml b/server_environment.yml index 9c27f9e6266..83bb365e215 100644 --- a/server_environment.yml +++ b/server_environment.yml @@ -20,4 +20,5 @@ dependencies: - pyvista==0.24 - ipywidgets - nbformat - - pytest-notebook + - nbclient + - jupyter_client<6.1.5 diff --git a/setup.cfg b/setup.cfg index 2d105aa61f5..4c29bc36214 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ doc-files = doc [flake8] exclude = __init__.py,*externals*,constants.py,fixes.py,resources.py -ignore = W504,I100,I101,I201,N806 +ignore = W503,W504,I100,I101,I201,N806 [tool:pytest] addopts =