Skip to content

Commit

Permalink
FIX: Make lists and dicts hashable
Browse files Browse the repository at this point in the history
Resolves: #683.
  • Loading branch information
oesteban committed Nov 25, 2020
1 parent 0d14325 commit 9fb388b
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 2 deletions.
4 changes: 2 additions & 2 deletions bids/layout/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import sqlalchemy as sa
from bids_validator import BIDSValidator

from ..utils import listify, natural_sort
from ..utils import listify, natural_sort, hashablefy
from ..external import inflect
from ..exceptions import (
BIDSDerivativesValidationError,
Expand Down Expand Up @@ -677,7 +677,7 @@ def get(self, return_type='object', target=None, scope='all',
results = [x for x in results if target in x.entities]

if return_type == 'id':
results = list(set([x.entities[target] for x in results]))
results = list(set([hashablefy(x.entities[target]) for x in results]))
results = natural_sort(results)

elif return_type == 'dir':
Expand Down
21 changes: 21 additions & 0 deletions bids/layout/tests/test_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,27 @@ def test_get_tr(layout_7t_trt):
assert tr == 4.0


def test_get_nonhashable_metadata(layout_ds117):
"""Test nonhashable metadata values (#683)."""
assert layout_ds117.get_IntendedFor(subject=['01'])[0] == (
"ses-mri/func/sub-01_ses-mri_task-facerecognition_run-01_bold.nii.gz",
"ses-mri/func/sub-01_ses-mri_task-facerecognition_run-02_bold.nii.gz",
"ses-mri/func/sub-01_ses-mri_task-facerecognition_run-03_bold.nii.gz",
"ses-mri/func/sub-01_ses-mri_task-facerecognition_run-04_bold.nii.gz",
"ses-mri/func/sub-01_ses-mri_task-facerecognition_run-05_bold.nii.gz",
"ses-mri/func/sub-01_ses-mri_task-facerecognition_run-06_bold.nii.gz",
"ses-mri/func/sub-01_ses-mri_task-facerecognition_run-07_bold.nii.gz",
"ses-mri/func/sub-01_ses-mri_task-facerecognition_run-08_bold.nii.gz",
"ses-mri/func/sub-01_ses-mri_task-facerecognition_run-09_bold.nii.gz",
)

landmarks = layout_ds117.get_AnatomicalLandmarkCoordinates(subject=['01'])[0]
assert landmarks["Nasion"] == (43, 111, 95)
assert landmarks["LPA"] == (140, 74, 16)
assert landmarks["RPA"] == (143, 74, 173)



def test_to_df(layout_ds117):
# Only filename entities
df = layout_ds117.to_df()
Expand Down
15 changes: 15 additions & 0 deletions bids/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

import re
import os
from frozendict import frozendict


# Monkeypatch to print out frozendicts *as if* they were dictionaries.
frozendict.__repr__ = lambda s: repr({k: v for k, v in s.items()})


def listify(obj):
Expand All @@ -10,6 +15,16 @@ def listify(obj):
return obj if isinstance(obj, (list, tuple, type(None))) else [obj]


def hashablefy(obj):
''' Make dictionaries and lists hashable or raise. '''
if isinstance(obj, list):
return tuple([hashablefy(o) for o in obj])

if isinstance(obj, dict):
return frozendict({k: hashablefy(v) for k, v in obj.items()})
return obj


def matches_entities(obj, entities, strict=False):
''' Checks whether an object's entities match the input. '''
if strict and set(obj.entities.keys()) != set(entities.keys()):
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ install_requires =
bids-validator
num2words
click
frozendict
tests_require =
pytest >=3.3
mock
Expand Down

0 comments on commit 9fb388b

Please sign in to comment.