diff --git a/doc/changes/latest.inc b/doc/changes/latest.inc index ae3a1a93839..18f5c7e29e7 100644 --- a/doc/changes/latest.inc +++ b/doc/changes/latest.inc @@ -56,4 +56,4 @@ Bugs API changes ~~~~~~~~~~~ -- None yet +- Deprecate arguments ``kind`` and ``path`` from :func:`mne.channels.read_layout` in favor of a common argument ``fname`` (:gh:`11500` by `Mathieu Scheltienne`_) diff --git a/mne/channels/layout.py b/mne/channels/layout.py index 0cd3f6f0211..87149f458a4 100644 --- a/mne/channels/layout.py +++ b/mne/channels/layout.py @@ -163,21 +163,33 @@ def _read_lay(fname): return box, pos, names, ids -def read_layout(kind, path=None, scale=True): +def read_layout(fname=None, path="", scale=True, *, kind=None): """Read layout from a file. Parameters ---------- - kind : str - The name of the ``.lout`` file (e.g. ``kind='Vectorview-all'`` for - ``'Vectorview-all.lout'``). + fname : path-like | str + Either the path to a ``.lout`` or ``.lay`` file or the name of a + built-in layout. c.f. Notes for a list of the available built-in + layouts. path : path-like | None The path of the folder containing the Layout file. Defaults to the ``mne/channels/data/layouts`` folder inside your mne-python installation. + + .. deprecated:: v1.4 + The ``kind`` and ``path`` parameters will be removed in version + 1.5. Please use the ``fname`` parameter instead. scale : bool Apply useful scaling for out the box plotting using ``layout.pos``. Defaults to True. + kind : str | None + The name of the ``.lout`` file (e.g. ``kind='Vectorview-all'`` for + ``'Vectorview-all.lout'``). + + .. deprecated:: v1.4 + The ``kind`` and ``path`` parameters will be removed in version + 1.5. Please use the ``fname`` parameter instead. Returns ------- @@ -190,7 +202,7 @@ def read_layout(kind, path=None, scale=True): Notes ----- - Valid ``kind`` arguments are: + Valid ``fname`` arguments are: .. table:: :widths: auto @@ -241,22 +253,55 @@ def read_layout(kind, path=None, scale=True): """ readers = {".lout": _read_lout, ".lay": _read_lay} - if path is None: - path = Path(__file__).parent / "data" / "layouts" - # kind should be the name as a string, but let's consider the case where - # the path to the file is provided instead. - kind = Path(kind) - if len(kind.suffix) == 0 and (path / kind.with_suffix(".lout")).exists(): - kind = kind.with_suffix(".lout") - elif len(kind.suffix) == 0 and (path / kind.with_suffix(".lay")).exists(): - kind = kind.with_suffix(".lay") - - fname = kind if kind.exists() else path / kind.name - if fname.suffix not in (".lout", ".lay"): - raise ValueError( - "Unknown layout type. Should be of type .lout or .lay." + if fname is None: # deprecated in 1.4 + warn( + "Argument 'kind' and 'path' are deprecated in favor of 'fname'.", + DeprecationWarning, ) - kind = fname.stem + if path == "" or path is None: + path = Path(__file__).parent / "data" / "layouts" + # kind should be the name as a string, but let's consider the case + # where the path to the file is provided instead. + kind = Path(kind) + if ( + len(kind.suffix) == 0 + and (path / kind.with_suffix(".lout")).exists() + ): + kind = kind.with_suffix(".lout") + elif ( + len(kind.suffix) == 0 + and (path / kind.with_suffix(".lay")).exists() + ): + kind = kind.with_suffix(".lay") + + fname = kind if kind.exists() else path / kind.name + if fname.suffix not in (".lout", ".lay"): + raise ValueError( + "Unknown layout type. Should be of type .lout or .lay." + ) + kind = fname.stem + else: + # to be removed along the deprecated argument + if kind is not None or path != "": + warn( + "Argument 'kind' and 'path' are deprecated in favor of " + "'fname' and should not be provided alongside 'fname'.", + DeprecationWarning, + ) + if isinstance(fname, str): + # is it a built-in layout? + directory = Path(__file__).parent / "data" / "layouts" + if (directory / fname).exists(): + fname = directory / fname + elif (directory / fname).with_suffix(".lout").exists(): + fname = (directory / fname).with_suffix(".lout") + elif (directory / fname).with_suffix(".lay").exists(): + fname = (directory / fname).with_suffix(".lay") + # if not, it must be a valid path provided as str or Path + fname = _check_fname(fname, "read", must_exist=True, name="layout") + # and it must have a valid extension + _check_option("fname extension", fname.suffix, readers) + kind = fname.stem box, pos, names, ids = readers[fname.suffix](fname) if scale: @@ -495,7 +540,7 @@ def find_layout(info, ch_type=None, exclude='bads'): return generate_2d_layout(xy, ch_names=ch_names, name='custom', normalize=True) - layout = read_layout(layout_name) + layout = read_layout(fname=layout_name) if not is_old_vv: layout.names = _clean_names(layout.names, remove_whitespace=True) if has_CTF_grad: diff --git a/mne/channels/tests/test_layout.py b/mne/channels/tests/test_layout.py index 7956c3b1da2..e17f90cafaf 100644 --- a/mne/channels/tests/test_layout.py +++ b/mne/channels/tests/test_layout.py @@ -54,22 +54,36 @@ def _get_test_info(): def test_io_layout_lout(tmp_path): """Test IO with .lout files.""" - layout = read_layout('Vectorview-all', scale=False) - layout.save(tmp_path / "foobar.lout") + layout = read_layout(fname="Vectorview-all", scale=False) + layout.save(tmp_path / "foobar.lout", overwrite=True) layout_read = read_layout( - tmp_path / "foobar.lout", path="./", scale=False + fname=tmp_path / "foobar.lout", scale=False, ) assert_array_almost_equal(layout.pos, layout_read.pos, decimal=2) assert layout.names == layout_read.names assert "