Skip to content

Commit e92d4e8

Browse files
committed
Merge remote-tracking branch 'upstream/maint/0.14.x'
2 parents c6f0597 + e3e7b7a commit e92d4e8

File tree

3 files changed

+68
-22
lines changed

3 files changed

+68
-22
lines changed

CHANGELOG.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ be separately managed and follow a different development pace.
3232
* MNT: Test on Python 3.10, various CI updates (#824)
3333
* MNT: Avoid jinja2 v3 until nbconvert handles breakages (#823)
3434

35+
Version 0.14.1 (March 29, 2022)
36+
-------------------------------
37+
Bug-fix release in the 0.14.x series.
38+
39+
* RF/FIX: Decompose filter construction for special queries and lists (#826)
40+
41+
Includes the following back-ports from 0.15.0:
42+
43+
* FIX: Clarify exception message (#806)
44+
* FIX: Catch UnicodeDecodeErrors along with JSONDecodeErrors for better reporting (#796)
45+
* FIX: Accept paths/strings for layout configuration files (#799)
46+
* ENH: Add __main__ to allow ``python -m bids`` to run CLI (#794)
47+
3548
Version 0.14.0 (November 09, 2021)
3649
----------------------------------
3750

bids/layout/layout.py

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -752,32 +752,52 @@ def _build_file_query(self, **kwargs):
752752
for name, val in filters.items():
753753
tag_alias = aliased(Tag)
754754

755-
if isinstance(val, (list, tuple)) and len(val) == 1:
756-
val = val[0]
755+
val_list = list(listify(val)) if val is not None else [None]
756+
if not val_list:
757+
continue
758+
759+
if Query.OPTIONAL in val_list:
760+
continue
757761

762+
none = required = False
763+
if None in val_list:
764+
none = True
765+
val_list.remove(None)
766+
if Query.NONE in val_list:
767+
none = True
768+
val_list.remove(Query.NONE)
769+
if Query.REQUIRED in val_list:
770+
required = True
771+
val_list.remove(Query.REQUIRED)
772+
773+
if none and required:
774+
# Always true, apply no filter
775+
continue
776+
777+
# Baseline, use join, start accumulating clauses
758778
join_method = query.join
779+
val_clauses = []
759780

760-
if val is None or val == Query.NONE:
781+
# NONE and REQUIRED get special treatment
782+
if none:
761783
join_method = query.outerjoin
762-
val_clause = tag_alias._value.is_(None)
763-
elif val == Query.OPTIONAL:
764-
continue
765-
elif val == Query.ANY:
766-
val_clause = tag_alias._value.isnot(None)
767-
elif regex:
768-
if isinstance(val, (list, tuple)):
769-
val_clause = sa.or_(*[
770-
tag_alias._value.op('REGEXP')(str(v))
771-
for v in val
772-
])
773-
else:
774-
val_clause = tag_alias._value.op('REGEXP')(str(val))
775-
else:
776-
vals = listify(val)
777-
if isinstance(vals[0], int):
778-
val_clause = cast(tag_alias._value, sa.Integer).in_(vals)
779-
else:
780-
val_clause = tag_alias._value.in_(vals)
784+
val_clauses.append(tag_alias._value.is_(None))
785+
if required:
786+
val_clauses.append(tag_alias._value.isnot(None))
787+
788+
# Any remaining values
789+
if regex:
790+
val_clauses.extend([tag_alias._value.op('REGEXP')(str(v))
791+
for v in val_list])
792+
elif val_list:
793+
_value = tag_alias._value
794+
if isinstance(val_list[0], int):
795+
_value = cast(tag_alias._value, sa.Integer)
796+
797+
val_clauses.append(_value.in_(val_list))
798+
799+
# Looking for intersection with list of vals, so use OR
800+
val_clause = sa.or_(*val_clauses) if len(val_clauses) > 1 else val_clauses[0]
781801

782802
query = join_method(
783803
tag_alias,

bids/layout/tests/test_layout.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,19 @@ def test_get_with_invalid_filters(layout_ds005):
772772
l.get(**filters)
773773

774774

775+
def test_get_with_query_constants_in_match_list(layout_ds005):
776+
l = layout_ds005
777+
get1 = l.get(subject='12', run=1, suffix='bold')
778+
get_none = l.get(subject='12', run=None, suffix='bold')
779+
get_any = l.get(subject='12', run=Query.ANY, suffix='bold')
780+
get1_and_none = l.get(subject='12', run=[None, 1], suffix='bold')
781+
get1_and_any = l.get(subject='12', run=[Query.ANY, 1], suffix='bold')
782+
get_none_and_any = l.get(subject='12', run=[Query.ANY, Query.NONE], suffix='bold')
783+
assert set(get1_and_none) == set(get1) | set(get_none)
784+
assert set(get1_and_any) == set(get1) | set(get_any)
785+
assert set(get_none_and_any) == set(get_none) | set(get_any)
786+
787+
775788
def test_load_layout(layout_synthetic_nodb, db_dir):
776789
db_path = str(db_dir / 'tmp_db')
777790
layout_synthetic_nodb.save(db_path)

0 commit comments

Comments
 (0)