##// END OF EJS Templates
First stab at ConfigManager class
Thomas Kluyver -
Show More
@@ -0,0 +1,87 b''
1 """Manager to read and modify frontend config data in JSON files.
2 """
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5 import errno
6 import io
7 import json
8 import os
9
10 from IPython.config import LoggingConfigurable
11 from IPython.utils.py3compat import PY3
12 from IPython.utils.traitlets import Unicode
13
14
15 def recursive_update(target, new):
16 """Recursively update one dictionary using another.
17
18 None values will delete their keys.
19 """
20 for k, v in new.items():
21 if isinstance(v, dict):
22 if k not in target:
23 target[k] = {}
24 recursive_update(target[k], v)
25 if not target[k]:
26 # Prune empty subdicts
27 del target[k]
28
29 elif v is None:
30 target.pop(k, None)
31
32 else:
33 target[k] = v
34
35
36 class ConfigManager(LoggingConfigurable):
37 profile_dir = Unicode()
38
39 @property
40 def config_dir(self):
41 return os.path.join(self.profile_dir, 'nbconfig')
42
43 def ensure_config_dir_exists(self):
44 try:
45 os.mkdir(self.config_dir, 0o755)
46 except OSError as e:
47 if e.errno != errno.EEXIST:
48 raise
49
50 def file_name(self, section_name):
51 return os.path.join(self.config_dir, section_name+'.json')
52
53 def get(self, section_name):
54 """Retrieve the config data for the specified section.
55
56 Returns the data as a dictionary, or an empty dictionary if the file
57 doesn't exist.
58 """
59 filename = self.file_name(section_name)
60 if os.path.isfile(filename):
61 with io.open(filename, encoding='utf-8') as f:
62 return json.load(f)
63 else:
64 return {}
65
66 def set(self, section_name, data):
67 """Store the given config data.
68 """
69 filename = self.file_name(section_name)
70 self.ensure_config_dir_exists()
71
72 if PY3:
73 f = io.open(filename, 'w', encoding='utf-8')
74 else:
75 f = open(filename, 'wb')
76 with f:
77 json.dump(data, f)
78
79 def update(self, section_name, new_data):
80 """Modify the config section by recursively updating it with new_data.
81
82 Returns the modified config data as a dictionary.
83 """
84 data = self.get(section_name)
85 recursive_update(data, new_data)
86 self.set(section_name, data)
87 return data
@@ -175,6 +175,10 b' class IPythonHandler(AuthenticatedHandler):'
175 175 def kernel_spec_manager(self):
176 176 return self.settings['kernel_spec_manager']
177 177
178 @property
179 def config_manager(self):
180 return self.settings['config_manager']
181
178 182 #---------------------------------------------------------------
179 183 # CORS
180 184 #---------------------------------------------------------------
@@ -125,19 +125,21 b' def load_handlers(name):'
125 125 class NotebookWebApplication(web.Application):
126 126
127 127 def __init__(self, ipython_app, kernel_manager, contents_manager,
128 cluster_manager, session_manager, kernel_spec_manager, log,
128 cluster_manager, session_manager, kernel_spec_manager,
129 config_manager, log,
129 130 base_url, default_url, settings_overrides, jinja_env_options):
130 131
131 132 settings = self.init_settings(
132 133 ipython_app, kernel_manager, contents_manager, cluster_manager,
133 session_manager, kernel_spec_manager, log, base_url, default_url,
134 settings_overrides, jinja_env_options)
134 session_manager, kernel_spec_manager, config_manager, log, base_url,
135 default_url, settings_overrides, jinja_env_options)
135 136 handlers = self.init_handlers(settings)
136 137
137 138 super(NotebookWebApplication, self).__init__(handlers, **settings)
138 139
139 140 def init_settings(self, ipython_app, kernel_manager, contents_manager,
140 141 cluster_manager, session_manager, kernel_spec_manager,
142 config_manager,
141 143 log, base_url, default_url, settings_overrides,
142 144 jinja_env_options=None):
143 145
@@ -172,6 +174,7 b' class NotebookWebApplication(web.Application):'
172 174 cluster_manager=cluster_manager,
173 175 session_manager=session_manager,
174 176 kernel_spec_manager=kernel_spec_manager,
177 config_manager=config_manager,
175 178
176 179 # IPython stuff
177 180 nbextensions_path = ipython_app.nbextensions_path,
@@ -607,6 +610,11 b' class NotebookApp(BaseIPythonApplication):'
607 610 help='The cluster manager class to use.'
608 611 )
609 612
613 config_manager_class = DottedObjectName('IPython.html.services.config.manager.ConfigManager',
614 config = True,
615 help='The config manager class to use'
616 )
617
610 618 kernel_spec_manager = Instance(KernelSpecManager)
611 619
612 620 def _kernel_spec_manager_default(self):
@@ -722,6 +730,10 b' class NotebookApp(BaseIPythonApplication):'
722 730 self.cluster_manager = kls(parent=self, log=self.log)
723 731 self.cluster_manager.update_profiles()
724 732
733 kls = import_item(self.config_manager_class)
734 self.config_manager = kls(parent=self, log=self.log,
735 profile_dir=self.profile_dir.location)
736
725 737 def init_logging(self):
726 738 # This prevents double log messages because tornado use a root logger that
727 739 # self.log is a child of. The logging module dipatches log messages to a log
@@ -747,6 +759,7 b' class NotebookApp(BaseIPythonApplication):'
747 759 self.web_app = NotebookWebApplication(
748 760 self, self.kernel_manager, self.contents_manager,
749 761 self.cluster_manager, self.session_manager, self.kernel_spec_manager,
762 self.config_manager,
750 763 self.log, self.base_url, self.default_url, self.tornado_settings,
751 764 self.jinja_environment_options
752 765 )
@@ -11,85 +11,27 b' from tornado import web'
11 11 from IPython.utils.py3compat import PY3
12 12 from ...base.handlers import IPythonHandler, json_errors
13 13
14 def recursive_update(target, new):
15 """Recursively update one dictionary using another.
16
17 None values will delete their keys.
18 """
19 for k, v in new.items():
20 if isinstance(v, dict):
21 if k not in target:
22 target[k] = {}
23 recursive_update(target[k], v)
24 if not target[k]:
25 # Prune empty subdicts
26 del target[k]
27
28 elif v is None:
29 target.pop(k, None)
30
31 else:
32 target[k] = v
33
34 14 class ConfigHandler(IPythonHandler):
35 15 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH')
36 16
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
48 def file_name(self, section_name):
49 return os.path.join(self.config_dir, section_name+'.json')
50
51 17 @web.authenticated
52 18 @json_errors
53 19 def get(self, section_name):
54 20 self.set_header("Content-Type", 'application/json')
55 filename = self.file_name(section_name)
56 if os.path.isfile(filename):
57 with io.open(filename, encoding='utf-8') as f:
58 self.finish(f.read())
59 else:
60 self.finish("{}")
21 self.finish(json.dumps(self.config_manager.get(section_name)))
61 22
62 23 @web.authenticated
63 24 @json_errors
64 25 def put(self, section_name):
65 self.get_json_body() # Will raise 400 if content is not valid JSON
66 filename = self.file_name(section_name)
67 self.ensure_config_dir_exists()
68 with open(filename, 'wb') as f:
69 f.write(self.request.body)
26 data = self.get_json_body() # Will raise 400 if content is not valid JSON
27 self.config_manager.set(section_name, data)
70 28 self.set_status(204)
71 29
72 30 @web.authenticated
73 31 @json_errors
74 32 def patch(self, section_name):
75 filename = self.file_name(section_name)
76 if os.path.isfile(filename):
77 with io.open(filename, encoding='utf-8') as f:
78 section = json.load(f)
79 else:
80 section = {}
81
82 update = self.get_json_body()
83 recursive_update(section, update)
84
85 self.ensure_config_dir_exists()
86 if PY3:
87 f = io.open(filename, 'w', encoding='utf-8')
88 else:
89 f = open(filename, 'wb')
90 with f:
91 json.dump(section, f)
92
33 new_data = self.get_json_body()
34 section = self.config_manager.update(section_name, new_data)
93 35 self.finish(json.dumps(section))
94 36
95 37
General Comments 0
You need to be logged in to leave comments. Login now