Repository: bids-standard/pybids

Issue:
BIDSLayout arguments: Argument is not JSON serializable
In PR #647 there were changes to how the BIDSLayout is saved.

If one passes an argument to `BIDSLayout` that is not serializable, it will throw an errorr:

```
/opt/miniconda-latest/envs/neuro/lib/python3.6/json/encoder.py in default(self, o)
    178         """
    179         raise TypeError("Object of type '%s' is not JSON serializable" %
--> 180                         o.__class__.__name__)
    181 
    182     def encode(self, o):

TypeError: Object of type 'PosixPath' is not JSON serializable
```

For example, if you pass a `Path` object to `derivatives`. 

Probably all arguments should be cast to string prior to this just in case. 

Discussion:
adelavega: cool, thanks! so now a Pathlib object should right?
codecov[bot]: # [Codecov](https://codecov.io/gh/bids-standard/pybids/pull/665?src=pr&el=h1) Report
> Merging [#665](https://codecov.io/gh/bids-standard/pybids/pull/665?src=pr&el=desc) into [master](https://codecov.io/gh/bids-standard/pybids/commit/9904dd54dce1a6f33799f78b80fc9f4da92d8da6?el=desc) will **decrease** coverage by `0.77%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/bids-standard/pybids/pull/665/graphs/tree.svg?width=650&height=150&src=pr&token=0d39OR1fhx)](https://codecov.io/gh/bids-standard/pybids/pull/665?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #665      +/-   ##
==========================================
- Coverage   84.62%   83.85%   -0.78%     
==========================================
  Files          29       29              
  Lines        3435     3406      -29     
  Branches      847      847              
==========================================
- Hits         2907     2856      -51     
- Misses        340      358      +18     
- Partials      188      192       +4     
```

| Flag | Coverage Δ | |
|---|---|---|
| #unittests | `83.85% <100.00%> (-0.78%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/bids-standard/pybids/pull/665?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [bids/layout/models.py](https://codecov.io/gh/bids-standard/pybids/pull/665/diff?src=pr&el=tree#diff-Ymlkcy9sYXlvdXQvbW9kZWxzLnB5) | `84.89% <100.00%> (-4.21%)` | :arrow_down: |
| [bids/layout/db.py](https://codecov.io/gh/bids-standard/pybids/pull/665/diff?src=pr&el=tree#diff-Ymlkcy9sYXlvdXQvZGIucHk=) | `95.89% <0.00%> (-2.82%)` | :arrow_down: |
| [bids/variables/io.py](https://codecov.io/gh/bids-standard/pybids/pull/665/diff?src=pr&el=tree#diff-Ymlkcy92YXJpYWJsZXMvaW8ucHk=) | `72.93% <0.00%> (-2.76%)` | :arrow_down: |
| [bids/analysis/model\_spec.py](https://codecov.io/gh/bids-standard/pybids/pull/665/diff?src=pr&el=tree#diff-Ymlkcy9hbmFseXNpcy9tb2RlbF9zcGVjLnB5) | `80.64% <0.00%> (-1.04%)` | :arrow_down: |
| [bids/layout/layout.py](https://codecov.io/gh/bids-standard/pybids/pull/665/diff?src=pr&el=tree#diff-Ymlkcy9sYXlvdXQvbGF5b3V0LnB5) | `86.09% <0.00%> (-0.59%)` | :arrow_down: |
| [bids/variables/variables.py](https://codecov.io/gh/bids-standard/pybids/pull/665/diff?src=pr&el=tree#diff-Ymlkcy92YXJpYWJsZXMvdmFyaWFibGVzLnB5) | `89.30% <0.00%> (-0.14%)` | :arrow_down: |
| [bids/analysis/transformations/base.py](https://codecov.io/gh/bids-standard/pybids/pull/665/diff?src=pr&el=tree#diff-Ymlkcy9hbmFseXNpcy90cmFuc2Zvcm1hdGlvbnMvYmFzZS5weQ==) | `87.86% <0.00%> (-0.06%)` | :arrow_down: |
| [bids/variables/collections.py](https://codecov.io/gh/bids-standard/pybids/pull/665/diff?src=pr&el=tree#diff-Ymlkcy92YXJpYWJsZXMvY29sbGVjdGlvbnMucHk=) | `89.44% <0.00%> (-0.06%)` | :arrow_down: |
| [bids/layout/index.py](https://codecov.io/gh/bids-standard/pybids/pull/665/diff?src=pr&el=tree#diff-Ymlkcy9sYXlvdXQvaW5kZXgucHk=) | `87.75% <0.00%> (-0.05%)` | :arrow_down: |
| ... and [1 more](https://codecov.io/gh/bids-standard/pybids/pull/665/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/bids-standard/pybids/pull/665?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/bids-standard/pybids/pull/665?src=pr&el=footer). Last update [9904dd5...9d7d128](https://codecov.io/gh/bids-standard/pybids/pull/665?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

effigies: Yup, explicitly test that case.

Patch:
diff --git a/bids/layout/models.py b/bids/layout/models.py
index 745885e3..fd6857cb 100644
--- a/bids/layout/models.py
+++ b/bids/layout/models.py
@@ -60,7 +60,7 @@ class LayoutInfo(Base):
             kwargs['root'] = str(Path(kwargs['root']).absolute())
 
         # Get abspaths
-        if 'derivatives' in kwargs and isinstance(kwargs['derivatives'], list):
+        if kwargs.get('derivatives') not in (None, True, False):
             kwargs['derivatives'] = [
                 str(Path(der).absolute())
                 for der in listify(kwargs['derivatives'])
diff --git a/bids/layout/validation.py b/bids/layout/validation.py
index 7dbbd1fa..463058ed 100644
--- a/bids/layout/validation.py
+++ b/bids/layout/validation.py
@@ -103,7 +103,7 @@ def validate_derivative_paths(paths, layout=None, **kwargs):
         return os.path.exists(dd)
 
     for p in paths:
-        p = os.path.abspath(p)
+        p = os.path.abspath(str(p))
         if os.path.exists(p):
             if check_for_description(p):
                 deriv_dirs.append(p)


Test patch:
diff --git a/bids/layout/tests/test_layout.py b/bids/layout/tests/test_layout.py
index e2ec2bc2..02650dae 100644
--- a/bids/layout/tests/test_layout.py
+++ b/bids/layout/tests/test_layout.py
@@ -3,7 +3,8 @@ BIDSLayout class."""
 
 import os
 import re
-from os.path import (join, abspath, basename)
+from os.path import join, abspath, basename
+from pathlib import Path
 
 import numpy as np
 import pytest
@@ -185,12 +186,12 @@ def test_get_metadata_error(layout_7t_trt):
     result = layout_7t_trt.files[path].get_metadata()
     with pytest.raises(KeyError) as err:
         result['Missing']
-    assert "Metadata term 'Missing' unavailable for file {}".format(path) in str(err)
+    assert "Metadata term 'Missing' unavailable for file {}".format(path) in str(err.value)
 
     result = layout_7t_trt.get_metadata(path)
     with pytest.raises(KeyError) as err:
         result['Missing']
-    assert "Metadata term 'Missing' unavailable for file {}".format(path) in str(err)
+    assert "Metadata term 'Missing' unavailable for file {}".format(path) in str(err.value)
 
 
 def test_get_with_bad_target(layout_7t_trt):
@@ -514,6 +515,21 @@ def test_deriv_indexing():
     assert not layout.get(scope='nonexistent')
 
 
+def test_path_arguments():
+    data_dir = join(get_test_data_path(), 'ds005')
+    deriv_dir = join(data_dir, 'derivatives', 'events')
+
+    layout = BIDSLayout(Path(data_dir), derivatives=Path(deriv_dir))
+    assert layout.get(scope='derivatives')
+    assert layout.get(scope='events')
+    assert not layout.get(scope='nonexistent')
+
+    layout = BIDSLayout(Path(data_dir), derivatives=[Path(deriv_dir)])
+    assert layout.get(scope='derivatives')
+    assert layout.get(scope='events')
+    assert not layout.get(scope='nonexistent')
+
+
 def test_layout_in_scope(layout_ds005, layout_ds005_derivs):
     assert layout_ds005._in_scope(['all'])
     assert layout_ds005._in_scope('raw')