diff --git a/fsl/utils/filetree/filetree.py b/fsl/utils/filetree/filetree.py index 6f268b27d572408aebcf51c3ebb0df277cb9c37a..c7d7bcf4d65e136e3f88504eff7ee23208722799 100644 --- a/fsl/utils/filetree/filetree.py +++ b/fsl/utils/filetree/filetree.py @@ -20,7 +20,7 @@ class FileTree(object): Properties - ``templates``: dictionary mapping short names to filename templates - - ``variables``: dictionary mapping variables in the templates to specific values + - ``variables``: dictionary mapping variables in the templates to specific values (variables set to None are explicitly unset) - ``sub_trees``: filename trees describing specific sub-directories - ``parent``: parent FileTree, of which this sub-tree is a sub-directory """ @@ -182,6 +182,7 @@ class FileTree(object): Creates a new filetree with updated variables :arg variables: new values for the variables + Setting variables to None will explicitly unset them """ new_tree = deepcopy(self) new_tree.variables.update(variables) @@ -194,6 +195,7 @@ class FileTree(object): :param short_name: short name of the path template :param filename: filename matching the template :return: variables needed to get to the given filename + Variables with None value are optional variables in the template that were not used """ text, _ = self.get_template(short_name) return utils.extract_variables(text, filename, self.variables) diff --git a/fsl/utils/filetree/utils.py b/fsl/utils/filetree/utils.py index 54cdc3c5d9324883a0aa66455f9a22fe8a78cce6..f78046fff8f6878309225b55ae22d6c8dab519ff 100644 --- a/fsl/utils/filetree/utils.py +++ b/fsl/utils/filetree/utils.py @@ -65,14 +65,18 @@ def fill_known(template, variables): Fills in the known variables filling the other variables with {<variable_name>} :param template: template - :param variables: mapping of variable names to values + :param variables: mapping of variable names to values (ignoring any None) :return: cleaned string """ prev = '' while prev != template: prev = template - settings = {name: variables[name] if name in variables else '{' + name + '}' - for name in set(find_variables(template))} + settings = {} + for name in set(find_variables(template)): + if name in variables and variables[name] is not None: + settings[name] = variables[name] + else: + settings[name] = '{' + name + '}' template = template.format(**settings) return template @@ -135,7 +139,7 @@ def extract_variables(template, filename, known_vars=None): :param template: template matching the given filename :param filename: filename :param known_vars: already known variables - :return: dictionary from variable names to string representations + :return: dictionary from variable names to string representations (unused variables set to None) """ if known_vars is None: known_vars = {} @@ -166,6 +170,9 @@ def extract_variables(template, filename, known_vars=None): raise ValueError('Multiple values found for {}'.format(var)) else: extracted_value[var] = value + for name in find_variables(template): + if name not in extracted_value: + extracted_value[name] = None extracted_value.update(known_vars) return extracted_value raise ValueError("{} did not match {}".format(filename, template)) diff --git a/tests/test_filetree/test_read.py b/tests/test_filetree/test_read.py index 17cbe01145c477ea05340fd60b6801d4acc244dd..b27984477218cd69f757199948865c2a06e6f8d1 100644 --- a/tests/test_filetree/test_read.py +++ b/tests/test_filetree/test_read.py @@ -76,6 +76,9 @@ def test_custom_tree(): assert len(tree.get_all('sub_file', glob_vars=['opt'])) == 2 assert len(tree.get_all('sub_file', glob_vars='all')) == 2 assert len(tree.get_all('sub_file')) == 1 + assert len(tree.update(opt=None).get_all('sub_file')) == 1 + assert len(tree.update(opt=None).get_all('sub_file', glob_vars=['opt'])) == 1 + assert len(tree.update(opt=None).get_all('sub_file', glob_vars='all')) == 1 for fn, settings in zip(tree.get_all('sub_file', glob_vars='all'), tree.get_all_vars('sub_file', glob_vars='all')): @@ -87,9 +90,10 @@ def test_custom_tree(): tree.get_all('opt_file') assert len(tree.get_all('opt_file', glob_vars=['opt'])) == 1 - for vars in ({}, {'opt': 'test'}): + for vars in ({'opt': None}, {'opt': 'test'}): filename = tree.update(**vars).get('sub_file') assert vars == tree.extract_variables('sub_file', filename) + assert {'opt': None} == tree.extract_variables('sub_file', tree.get('sub_file')) assert tree.exists(('sub_file', 'opt_file'), error=True, on_disk=True, glob_vars=['opt']) assert tree.exists(('sub_file', 'opt_file'), on_disk=True, glob_vars=['opt']) diff --git a/tests/test_filetree/test_template.py b/tests/test_filetree/test_template.py index ad572a48e0c80772ac78338f84cf01878074af65..96ecc18f00adb1add2b191c8d3744460f1c02bc6 100644 --- a/tests/test_filetree/test_template.py +++ b/tests/test_filetree/test_template.py @@ -11,10 +11,10 @@ def test_variables(): def test_get_variables(): assert {'var': 'test'} == utils.extract_variables('{var}', 'test') assert {'var': 'test'} == utils.extract_variables('2{var}', '2test') - assert {'var': 'test'} == utils.extract_variables('{var}[_{other_var}]', 'test') + assert {'var': 'test', 'other_var': None} == utils.extract_variables('{var}[_{other_var}]', 'test') assert {'var': 'test', 'other_var': 'foo'} == utils.extract_variables('{var}[_{other_var}]', 'test_foo') assert {'var': 'test', 'other_var': 'foo'} == utils.extract_variables('{var}[_{other_var}]_{var}', 'test_foo_test') - assert {'var': 'test'} == utils.extract_variables('{var}[_{other_var}]_{var}', 'test_test') + assert {'var': 'test', 'other_var': None} == utils.extract_variables('{var}[_{other_var}]_{var}', 'test_test') with pytest.raises(ValueError): utils.extract_variables('{var}[_{other_var}]_{var}', 'test_foo') with pytest.raises(ValueError):