Skip to content

Commit 02a8de6

Browse files
committed
add dot nesting for inputs in JSONSink
1 parent d8978c3 commit 02a8de6

File tree

2 files changed

+87
-16
lines changed

2 files changed

+87
-16
lines changed

nipype/interfaces/io.py

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1860,7 +1860,19 @@ def _list_outputs(self):
18601860

18611861
class JSONFileSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec):
18621862
out_file = File(desc='JSON sink file')
1863-
in_dict = traits.Dict(desc='input JSON dictionary')
1863+
in_dict = traits.Dict(value={}, usedefault=True,
1864+
desc='input JSON dictionary')
1865+
_outputs = traits.Dict(value={}, usedefault=True)
1866+
1867+
def __setattr__(self, key, value):
1868+
if key not in self.copyable_trait_names():
1869+
if not isdefined(value):
1870+
super(JSONFileSinkInputSpec, self).__setattr__(key, value)
1871+
self._outputs[key] = value
1872+
else:
1873+
if key in self._outputs:
1874+
self._outputs[key] = value
1875+
super(JSONFileSinkInputSpec, self).__setattr__(key, value)
18641876

18651877

18661878
class JSONFileSinkOutputSpec(TraitedSpec):
@@ -1869,7 +1881,10 @@ class JSONFileSinkOutputSpec(TraitedSpec):
18691881

18701882
class JSONFileSink(IOBase):
18711883

1872-
""" Very simple frontend for storing values into a JSON file.
1884+
"""
1885+
Very simple frontend for storing values into a JSON file.
1886+
Entries already existing in in_dict will be overridden by matching
1887+
entries dynamically added as inputs.
18731888
18741889
.. warning::
18751890
@@ -1896,34 +1911,52 @@ class JSONFileSink(IOBase):
18961911
input_spec = JSONFileSinkInputSpec
18971912
output_spec = JSONFileSinkOutputSpec
18981913

1899-
def __init__(self, input_names=[], **inputs):
1914+
def __init__(self, infields=[], force_run=True, **inputs):
19001915
super(JSONFileSink, self).__init__(**inputs)
1901-
self._input_names = filename_to_list(input_names)
1902-
add_traits(self.inputs, [name for name in self._input_names])
1916+
self._input_names = infields
1917+
1918+
undefined_traits = {}
1919+
for key in infields:
1920+
self.inputs.add_trait(key, traits.Any)
1921+
self.inputs._outputs[key] = Undefined
1922+
undefined_traits[key] = Undefined
1923+
self.inputs.trait_set(trait_change_notify=False, **undefined_traits)
1924+
1925+
if force_run:
1926+
self._always_run = True
1927+
1928+
def _process_name(self, name, val):
1929+
if '.' in name:
1930+
newkeys = name.split('.')
1931+
name = newkeys.pop(0)
1932+
nested_dict = {newkeys.pop(): val}
1933+
1934+
for nk in reversed(newkeys):
1935+
nested_dict = {nk: nested_dict}
1936+
val = nested_dict
1937+
1938+
return name, val
19031939

19041940
def _list_outputs(self):
19051941
import json
19061942
import os.path as op
1943+
19071944
if not isdefined(self.inputs.out_file):
19081945
out_file = op.abspath('datasink.json')
19091946
else:
19101947
out_file = self.inputs.out_file
19111948

1912-
out_dict = dict()
1949+
out_dict = self.inputs.in_dict
19131950

1914-
if isdefined(self.inputs.in_dict):
1915-
if isinstance(self.inputs.in_dict, dict):
1916-
out_dict = self.inputs.in_dict
1917-
else:
1918-
for name in self._input_names:
1919-
val = getattr(self.inputs, name)
1920-
val = val if isdefined(val) else 'undefined'
1921-
out_dict[name] = val
1951+
# Overwrite in_dict entries automatically
1952+
for key, val in self.inputs._outputs.items():
1953+
if not isdefined(val) or key == 'trait_added':
1954+
continue
1955+
key, val = self._process_name(key, val)
1956+
out_dict[key] = val
19221957

19231958
with open(out_file, 'w') as f:
19241959
json.dump(out_dict, f)
19251960
outputs = self.output_spec().get()
19261961
outputs['out_file'] = out_file
19271962
return outputs
1928-
1929-

nipype/interfaces/tests/test_io.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,41 @@ def test_freesurfersource():
238238
yield assert_equal, fss.inputs.hemi, 'both'
239239
yield assert_equal, fss.inputs.subject_id, Undefined
240240
yield assert_equal, fss.inputs.subjects_dir, Undefined
241+
242+
243+
def test_jsonsink():
244+
import json
245+
import os
246+
247+
ds = nio.JSONFileSink()
248+
yield assert_equal, ds.inputs._outputs, {}
249+
ds = nio.JSONFileSink(in_dict={'foo': 'var'})
250+
yield assert_equal, ds.inputs.in_dict, {'foo': 'var'}
251+
ds = nio.JSONFileSink(infields=['test'])
252+
yield assert_true, 'test' in ds.inputs.copyable_trait_names()
253+
254+
curdir = os.getcwd()
255+
outdir = mkdtemp()
256+
os.chdir(outdir)
257+
js = nio.JSONFileSink(infields=['test'], in_dict={'foo': 'var'})
258+
js.inputs.new_entry = 'someValue'
259+
setattr(js.inputs, 'contrasts.alt', 'someNestedValue')
260+
res = js.run()
261+
262+
with open(res.outputs['out_file'], 'r') as f:
263+
data = json.load(f)
264+
yield assert_true, data == {"contrasts": {"alt": "someNestedValue"}, "foo": "var", "new_entry": "someValue"}
265+
266+
js = nio.JSONFileSink(infields=['test'], in_dict={'foo': 'var'})
267+
js.inputs.new_entry = 'someValue'
268+
js.inputs.test = 'testInfields'
269+
setattr(js.inputs, 'contrasts.alt', 'someNestedValue')
270+
res = js.run()
271+
272+
with open(res.outputs['out_file'], 'r') as f:
273+
data = json.load(f)
274+
yield assert_true, data == {"test": "testInfields", "contrasts": {"alt": "someNestedValue"}, "foo": "var", "new_entry": "someValue"}
275+
276+
os.chdir(curdir)
277+
shutil.rmtree(outdir)
278+

0 commit comments

Comments
 (0)