##// END OF EJS Templates
Apply JSON config updates recursively
Thomas Kluyver -
Show More
@@ -9,6 +9,25 b' from tornado import web'
9
9
10 from ...base.handlers import IPythonHandler, json_errors
10 from ...base.handlers import IPythonHandler, json_errors
11
11
12 def recursive_update(target, new):
13 """Recursively update one dictionary using another.
14
15 None values will delete their keys.
16 """
17 for k, v in new.items():
18 if isinstance(v, dict):
19 if k not in target:
20 target[k] = {}
21 recursive_update(target[k], v)
22 if not target[k]:
23 # Prune empty subdicts
24 del target[k]
25
26 elif v is None:
27 target.pop(k, None)
28
29 else:
30 target[k] = v
12
31
13 class ConfigHandler(IPythonHandler):
32 class ConfigHandler(IPythonHandler):
14 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH')
33 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH')
@@ -46,11 +65,8 b' class ConfigHandler(IPythonHandler):'
46 else:
65 else:
47 section = {}
66 section = {}
48
67
49 for k, v in self.get_json_body().items():
68 update = self.get_json_body()
50 if v is None:
69 recursive_update(section, update)
51 section.pop(k, None)
52 else:
53 section[k] = v
54
70
55 with io.open(filename, 'w', encoding='utf-8') as f:
71 with io.open(filename, 'w', encoding='utf-8') as f:
56 json.dump(section, f)
72 json.dump(section, f)
@@ -1,5 +1,5 b''
1 # coding: utf-8
1 # coding: utf-8
2 """Test the kernel specs webservice API."""
2 """Test the config webservice API."""
3
3
4 import json
4 import json
5
5
@@ -32,7 +32,7 b' class ConfigAPI(object):'
32 return self._req('PATCH', section, json.dumps(values))
32 return self._req('PATCH', section, json.dumps(values))
33
33
34 class APITest(NotebookTestBase):
34 class APITest(NotebookTestBase):
35 """Test the kernelspec web service API"""
35 """Test the config web service API"""
36 def setUp(self):
36 def setUp(self):
37 self.config_api = ConfigAPI(self.base_url())
37 self.config_api = ConfigAPI(self.base_url())
38
38
@@ -46,18 +46,22 b' class APITest(NotebookTestBase):'
46 self.assertEqual(r.json(), sample)
46 self.assertEqual(r.json(), sample)
47
47
48 def test_modify(self):
48 def test_modify(self):
49 sample = {'foo': 'bar', 'baz': 73}
49 sample = {'foo': 'bar', 'baz': 73,
50 'sub': {'a': 6, 'b': 7}, 'sub2': {'c': 8}}
50 self.config_api.set('example', sample)
51 self.config_api.set('example', sample)
51
52
52 r = self.config_api.modify('example', {'foo': None, # should delete foo
53 r = self.config_api.modify('example', {'foo': None, # should delete foo
53 'baz': 75,
54 'baz': 75,
54 'wib': [1,2,3],
55 'wib': [1,2,3],
56 'sub': {'a': 8, 'b': None, 'd': 9},
57 'sub2': {'c': None} # should delete sub2
55 })
58 })
56 self.assertEqual(r.status_code, 204)
59 self.assertEqual(r.status_code, 204)
57
60
58 r = self.config_api.get('example')
61 r = self.config_api.get('example')
59 self.assertEqual(r.status_code, 200)
62 self.assertEqual(r.status_code, 200)
60 self.assertEqual(r.json(), {'baz': 75, 'wib': [1,2,3]})
63 self.assertEqual(r.json(), {'baz': 75, 'wib': [1,2,3],
64 'sub': {'a': 8, 'd': 9}})
61
65
62 def test_get_unknown(self):
66 def test_get_unknown(self):
63 # We should get an empty config dictionary instead of a 404
67 # We should get an empty config dictionary instead of a 404
General Comments 0
You need to be logged in to leave comments. Login now