mne-python icon indicating copy to clipboard operation
mne-python copied to clipboard

Unify treatment of pathlike objects

Open dafrose opened this issue 2 years ago • 7 comments

Describe the bug

Across mne pathlike objects are currently treated inconsistently. In many place, paths are tested for and accepted being str or pathlib.Path. In other places, only str is accepted and the functions fails for Path objects. The problem occured in my pipelines in multiple places and seemingly random, where different function from the same context behaved differently.

Steps to reproduce

One example where a Path object fails is mne.make_forward_solution(info, ...) where info can either be an instance of Info or a string representing a path, but not a pathlib.path. In particular, the in mne.forward._make_forward.py:

564     if not isinstance(info, (Info, str)):
565         raise TypeError('info should be an instance of Info or string')

Expected results

Judging from the mne code the intention appears to be that both str and Path representations of a path are accepted everywhere.

Additional information

I would like to get your feedback, whether there are reasons why this is not treated consistently, but I gather that this is oversight due to the shear amount of places to check. I also offer to try and hunt down places that need changing and submit a pull request. However, there seem to be variations to how, when and where paths are checked in the code. It might make sense to unify that as well. For example, I this reference in mne.io.write.start_file:

308         fname = _fn35(fname)

That points to this code in mne.fixes:

def _fn35(fname):
    try:
        from py._path.common import PathBase
    except ImportError:
        pass
    else:
        if isinstance(fname, PathBase):
            fname = str(fname)
    if isinstance(fname, Path):
        fname = str(fname)
    return fname

To be honest, I do not understand the purpose of this particular function, because all it does is call str(fname) and even if it doesn't it most certainly would not hurt to do it (a str of a str is still a str). So the function could probably just be removed altogether and replaced with str(fname).

Your thoughts?

dafrose avatar Apr 06 '22 12:04 dafrose

Across mne pathlike objects are currently treated inconsistently. In many place, paths are tested for and accepted being str or pathlib.Path. In other places, only str is accepted and the functions fails for Path objects. The problem occured in my pipelines in multiple places and seemingly random, where different function from the same context behaved differently.

We have slowly been converting all of these to:

_validate_type(info, (Info, 'path-like'), 'info')
if not isinstance(info, Info):  # path-like
    info = read_info(info)

and under the hood, read_info should do at some point something like:

    info = _check_fname(info, must_exist=True, overwrite='read', name='info')

But we haven't made this transition everywhere, as you've seen with our forward code.

For example, I this reference in mne.io.write.start_file [about _fn35]

This looks like old Python3.5 cruft to me that should make use of 'path-like' / _check_fname

Would you be up for trying a PR for these fixes?

larsoner avatar Apr 06 '22 12:04 larsoner

I can try, I am unsure, however, how they should exactly be fixed. Is this code snippet that you mentioned the go-to solution for all pathlike checks?

_validate_type(info, (Info, 'path-like'), 'info')
if not isinstance(info, Info):  # path-like
    info = read_info(info)

About the _fn35 function: so that should essentially be replaced with _check_fname, did I understand that correctly?

dafrose avatar Apr 06 '22 15:04 dafrose

Is this code snippet that you mentioned the go-to solution for all pathlike checks?

Most reading checks, yes. If you look in our code (e.g., git grep _check_fname) you can find many existing good examples of how to handle paths

About the _fn35 function: so that should essentially be replaced with _check_fname, did I understand that correctly?

Yeah, I think that should work!

larsoner avatar Apr 06 '22 15:04 larsoner

I don't understand the Info type here – what does that have to do with str and pathlib.Path types?

cbrnr avatar Apr 06 '22 16:04 cbrnr

Presumably in this part of the code you can either pass an Info obj directly, or a path to a file with Info that can be read

larsoner avatar Apr 06 '22 17:04 larsoner

Ah OK! This makes sense!

cbrnr avatar Apr 06 '22 17:04 cbrnr

@dafrose are you still hoping to work on this? If not, we might assign someone else to do it at a sprint at the end of June.

drammock avatar Jun 15 '22 15:06 drammock

I'm happy to work on this one:

  • mne.fixes._fn35 was removed in this PR: https://github.com/mne-tools/mne-python/pull/7638.
  • I'll look into hunting down where _check_fname should be added.

cgohil8 avatar Aug 23 '22 09:08 cgohil8

Note that it was already a bit improved in https://github.com/mne-tools/mne-python/pull/11473

mscheltienne avatar Nov 04 '23 10:11 mscheltienne