Repository: mne-tools/mne-python

Issue:
mne.set_config doesn't accept Path objects
#### Describe the bug
Passing a `pathlib.Path` value to `mne.set_config()` raises an error.

#### Steps to reproduce
```python
import pathlib
import mne

path = pathlib.Path('/tmp')
mne.set_config(key='SUBJECTS_DIR', value=path)
```
#### Expected results
Configuration would be updated.

#### Actual results
```python
TypeError                                 Traceback (most recent call last)
Untitled-1 in 
      4 
      5 path = pathlib.Path('/tmp')
----> 6 mne.set_config(key='SUBJECTS_DIR', value=path)

~/Development/mne-python/mne/utils/config.py in set_config(key, value, home_dir, set_env)
    264     # While JSON allow non-string types, we allow users to override config
    265     # settings using env, which are strings, so we enforce that here
--> 266     _validate_type(value, (str, type(None)), "value",
    267                    "None or string")
    268 

~/Development/mne-python/mne/utils/check.py in _validate_type(item, types, item_name, type_name)
    377                 type_name[-1] = 'or ' + type_name[-1]
    378                 type_name = ', '.join(type_name)
--> 379         raise TypeError('%s must be an instance of %s, got %s instead'
    380                         % (item_name, type_name, type(item),))
    381 

TypeError: value must be an instance of None or string, got  instead
```

#### Additional information
As we support `Path` objects in other places in MNE, `set_config()` should not be an exception.


Discussion:
👍 - I'd also ask why we need to restrict the types here. Somehow the data must be serialized as a string to end up in the JSON file, so I wouldn't check anything. Otherwise, we might have to add more and more types that have a suitable string representation to this check.
> Otherwise, we might have to add more and more types that have a suitable string representation to this check.

We should probably simply cast to a string if `not None`, but I suppose that's what you meant, right?
> We should probably simply cast to a string if not None, but I suppose that's what you meant, right?

Basically yes.
They all have to be string because we also support overriding with environment variables, so yes just cast

Resolving patch:
diff --git a/mne/utils/config.py b/mne/utils/config.py
--- a/mne/utils/config.py
+++ b/mne/utils/config.py
@@ -263,8 +263,9 @@ def set_config(key, value, home_dir=None, set_env=True):
     _validate_type(key, 'str', "key")
     # While JSON allow non-string types, we allow users to override config
     # settings using env, which are strings, so we enforce that here
-    _validate_type(value, (str, type(None)), "value",
-                   "None or string")
+    _validate_type(value, (str, 'path-like', type(None)), 'value')
+    if value is not None:
+        value = str(value)
 
     if key not in known_config_types and not \
             any(k in key for k in known_config_wildcards):


Test patch:
diff --git a/mne/utils/tests/test_config.py b/mne/utils/tests/test_config.py
--- a/mne/utils/tests/test_config.py
+++ b/mne/utils/tests/test_config.py
@@ -1,6 +1,7 @@
 from io import StringIO
 import os
 import pytest
+from pathlib import Path
 
 from mne.utils import (set_config, get_config, get_config_path,
                        set_memmap_min_size, _get_stim_channel, sys_info)
@@ -12,6 +13,8 @@ def test_config(tmpdir):
     key = '_MNE_PYTHON_CONFIG_TESTING'
     value = '123456'
     value2 = '123'
+    value3 = Path('/foo/bar')
+
     old_val = os.getenv(key, None)
     os.environ[key] = value
     assert (get_config(key) == value)
@@ -35,6 +38,11 @@ def test_config(tmpdir):
     assert (key not in os.environ)
     if old_val is not None:
         os.environ[key] = old_val
+
+    # Check serialization from Path to string
+    with pytest.warns(RuntimeWarning, match='non-standard'):
+        set_config(key, value3, home_dir=tempdir)
+
     # Check if get_config with key=None returns all config
     key = 'MNE_PYTHON_TESTING_KEY'
     assert key not in get_config(home_dir=tempdir)