##// END OF EJS Templates
Put frontend config files in profile_foo/nbconfig/ subdir
Thomas Kluyver -
Show More
@@ -1,88 +1,102 b''
1 """Tornado handlers for frontend config storage."""
1 """Tornado handlers for frontend config storage."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5 import json
5 import json
6 import os
6 import os
7 import io
7 import io
8 import errno
8 from tornado import web
9 from tornado import web
9
10
10 from IPython.utils.py3compat import PY3
11 from IPython.utils.py3compat import PY3
11 from ...base.handlers import IPythonHandler, json_errors
12 from ...base.handlers import IPythonHandler, json_errors
12
13
13 def recursive_update(target, new):
14 def recursive_update(target, new):
14 """Recursively update one dictionary using another.
15 """Recursively update one dictionary using another.
15
16
16 None values will delete their keys.
17 None values will delete their keys.
17 """
18 """
18 for k, v in new.items():
19 for k, v in new.items():
19 if isinstance(v, dict):
20 if isinstance(v, dict):
20 if k not in target:
21 if k not in target:
21 target[k] = {}
22 target[k] = {}
22 recursive_update(target[k], v)
23 recursive_update(target[k], v)
23 if not target[k]:
24 if not target[k]:
24 # Prune empty subdicts
25 # Prune empty subdicts
25 del target[k]
26 del target[k]
26
27
27 elif v is None:
28 elif v is None:
28 target.pop(k, None)
29 target.pop(k, None)
29
30
30 else:
31 else:
31 target[k] = v
32 target[k] = v
32
33
33 class ConfigHandler(IPythonHandler):
34 class ConfigHandler(IPythonHandler):
34 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH')
35 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH')
35
36
37 @property
38 def config_dir(self):
39 return os.path.join(self.profile_dir, 'nbconfig')
40
41 def ensure_config_dir_exists(self):
42 try:
43 os.mkdir(self.config_dir, 0o755)
44 except OSError as e:
45 if e.errno != errno.EEXIST:
46 raise
47
36 def file_name(self, section_name):
48 def file_name(self, section_name):
37 return os.path.join(self.profile_dir, 'nb_%s_config.json' % section_name)
49 return os.path.join(self.config_dir, section_name+'.json')
38
50
39 @web.authenticated
51 @web.authenticated
40 @json_errors
52 @json_errors
41 def get(self, section_name):
53 def get(self, section_name):
42 self.set_header("Content-Type", 'application/json')
54 self.set_header("Content-Type", 'application/json')
43 filename = self.file_name(section_name)
55 filename = self.file_name(section_name)
44 if os.path.isfile(filename):
56 if os.path.isfile(filename):
45 with io.open(filename, encoding='utf-8') as f:
57 with io.open(filename, encoding='utf-8') as f:
46 self.finish(f.read())
58 self.finish(f.read())
47 else:
59 else:
48 self.finish("{}")
60 self.finish("{}")
49
61
50 @web.authenticated
62 @web.authenticated
51 @json_errors
63 @json_errors
52 def put(self, section_name):
64 def put(self, section_name):
53 self.get_json_body() # Will raise 400 if content is not valid JSON
65 self.get_json_body() # Will raise 400 if content is not valid JSON
54 filename = self.file_name(section_name)
66 filename = self.file_name(section_name)
67 self.ensure_config_dir_exists()
55 with open(filename, 'wb') as f:
68 with open(filename, 'wb') as f:
56 f.write(self.request.body)
69 f.write(self.request.body)
57 self.set_status(204)
70 self.set_status(204)
58
71
59 @web.authenticated
72 @web.authenticated
60 @json_errors
73 @json_errors
61 def patch(self, section_name):
74 def patch(self, section_name):
62 filename = self.file_name(section_name)
75 filename = self.file_name(section_name)
63 if os.path.isfile(filename):
76 if os.path.isfile(filename):
64 with io.open(filename, encoding='utf-8') as f:
77 with io.open(filename, encoding='utf-8') as f:
65 section = json.load(f)
78 section = json.load(f)
66 else:
79 else:
67 section = {}
80 section = {}
68
81
69 update = self.get_json_body()
82 update = self.get_json_body()
70 recursive_update(section, update)
83 recursive_update(section, update)
71
84
85 self.ensure_config_dir_exists()
72 if PY3:
86 if PY3:
73 f = io.open(filename, 'w', encoding='utf-8')
87 f = io.open(filename, 'w', encoding='utf-8')
74 else:
88 else:
75 f = open(filename, 'wb')
89 f = open(filename, 'wb')
76 with f:
90 with f:
77 json.dump(section, f)
91 json.dump(section, f)
78
92
79 self.finish(json.dumps(section))
93 self.finish(json.dumps(section))
80
94
81
95
82 # URL to handler mappings
96 # URL to handler mappings
83
97
84 section_name_regex = r"(?P<section_name>\w+)"
98 section_name_regex = r"(?P<section_name>\w+)"
85
99
86 default_handlers = [
100 default_handlers = [
87 (r"/api/config/%s" % section_name_regex, ConfigHandler),
101 (r"/api/config/%s" % section_name_regex, ConfigHandler),
88 ]
102 ]
General Comments 0
You need to be logged in to leave comments. Login now