Repository: bids-standard/pybids

Issue:
layout.get(..., extension=bids.layout.Query.ANY) fails
`layout.get` assumes that the `extension` filter is a string or list of strings. We need to handle the `Query` options, as well as `None`.

```Python
~/Projects/bids-standard/pybids/bids/layout/layout.py in get(self, return_type, target, scope, regex_search, absolute_paths, drop_invalid_filters, **filters)
    918         if 'extension' in filters:
    919             exts = listify(filters['extension'])
--> 920             filters['extension'] = [x.lstrip('.') for x in exts]
    921 
    922         if drop_invalid_filters:

~/Projects/bids-standard/pybids/bids/layout/layout.py in <listcomp>(.0)
    918         if 'extension' in filters:
    919             exts = listify(filters['extension'])
--> 920             filters['extension'] = [x.lstrip('.') for x in exts]
    921 
    922         if drop_invalid_filters:

AttributeError: 'Query' object has no attribute 'lstrip'
```


Discussion:
What's the use case for saying you want no extension or any extension? Don't all BIDS-valid files have extensions?
The use case was determining which entities were used by a dataset.

```Python
[ent for ent in list(layout.get_entities(metadata=False))
 if layout.get(**{ent: bids.layout.Query.ANY})]
```

This failure required:

```Python
[ent for ent in list(layout.get_entities(metadata=False))
 if ent == 'extension' or layout.get(**{ent: bids.layout.Query.ANY})]
```

There might be smarter ways to go about this, but it seems silly for this to be the only entity that can't take a `Query`.
Not saying this is high priority, btw.
Ah, I see. FWIW, I think the DB only contains entities that are actually seen during indexing, and not all entities defined in the JSON files. So I think anything in `layout.get_entities(metadata=False)` is guaranteed to exist at least once in a project filename.

EDIT: I take it back—`Entity` records are indeed created at `Config` load time.
This is only failing because of a check intended to ensure backwards compatibility with people still using `extensions` instead of `extension`. We're already issuing a warning that `extensions` goes away in 0.11, so I vote we just wait till 0.11 and this problem will go away by itself as soon as we remove the vestigial code.
Sounds good. I think we merged some things that justified bumping the minor version already, so should we just go ahead and remove it?
Sure, I'll go ahead and do that.

Resolving patch:
diff --git a/bids/layout/layout.py b/bids/layout/layout.py
--- a/bids/layout/layout.py
+++ b/bids/layout/layout.py
@@ -897,27 +897,17 @@ def get(self, return_type='object', target=None, scope='all',
         -------
         list of :obj:`bids.layout.BIDSFile` or str
             A list of BIDSFiles (default) or strings (see return_type).
-
-        Notes
-        -----
-        * In pybids 0.9.0, the 'extensions' argument has been removed in
-          favor of the 'extension' entity.
         """
-        if 'extensions' in filters:
-            filters['extension'] = filters.pop('extensions')
-            warnings.warn("In pybids 0.9.0, the 'extensions' filter was "
-                          "deprecated in favor of 'extension'. The former will"
-                          " stop working in 0.11.0.", DeprecationWarning)
 
         layouts = self._get_layouts_in_scope(scope)
 
         entities = self.get_entities()
 
-        # For consistency with past versions where "extensions" was a
-        # hard-coded argument, allow leading periods
+        # Strip leading periods if extensions were passed
         if 'extension' in filters:
             exts = listify(filters['extension'])
-            filters['extension'] = [x.lstrip('.') for x in exts]
+            filters['extension'] = [x.lstrip('.') if isinstance(x, str) else x
+                                    for x in exts]
 
         if drop_invalid_filters:
             invalid_filters = set(filters.keys()) - set(entities.keys())


Test patch:
diff --git a/bids/layout/tests/test_layout.py b/bids/layout/tests/test_layout.py
--- a/bids/layout/tests/test_layout.py
+++ b/bids/layout/tests/test_layout.py
@@ -280,7 +280,8 @@ def test_get_val_none(layout_7t_trt, acq):
 
 def test_get_val_enum_any(layout_7t_trt):
     t1w_files = layout_7t_trt.get(
-        subject='01', ses='1', suffix='T1w', acquisition=Query.ANY)
+        subject='01', ses='1', suffix='T1w', acquisition=Query.ANY,
+        extension=Query.ANY)
     assert not t1w_files
     bold_files = layout_7t_trt.get(subject='01', ses='1', run=1, suffix='bold',
                                    acquisition=Query.ANY)