##// 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 def kernel_spec_manager(self):
175 def kernel_spec_manager(self):
176 return self.settings['kernel_spec_manager']
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 # CORS
183 # CORS
180 #---------------------------------------------------------------
184 #---------------------------------------------------------------
@@ -125,19 +125,21 b' def load_handlers(name):'
125 class NotebookWebApplication(web.Application):
125 class NotebookWebApplication(web.Application):
126
126
127 def __init__(self, ipython_app, kernel_manager, contents_manager,
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 base_url, default_url, settings_overrides, jinja_env_options):
130 base_url, default_url, settings_overrides, jinja_env_options):
130
131
131 settings = self.init_settings(
132 settings = self.init_settings(
132 ipython_app, kernel_manager, contents_manager, cluster_manager,
133 ipython_app, kernel_manager, contents_manager, cluster_manager,
133 session_manager, kernel_spec_manager, log, base_url, default_url,
134 session_manager, kernel_spec_manager, config_manager, log, base_url,
134 settings_overrides, jinja_env_options)
135 default_url, settings_overrides, jinja_env_options)
135 handlers = self.init_handlers(settings)
136 handlers = self.init_handlers(settings)
136
137
137 super(NotebookWebApplication, self).__init__(handlers, **settings)
138 super(NotebookWebApplication, self).__init__(handlers, **settings)
138
139
139 def init_settings(self, ipython_app, kernel_manager, contents_manager,
140 def init_settings(self, ipython_app, kernel_manager, contents_manager,
140 cluster_manager, session_manager, kernel_spec_manager,
141 cluster_manager, session_manager, kernel_spec_manager,
142 config_manager,
141 log, base_url, default_url, settings_overrides,
143 log, base_url, default_url, settings_overrides,
142 jinja_env_options=None):
144 jinja_env_options=None):
143
145
@@ -172,6 +174,7 b' class NotebookWebApplication(web.Application):'
172 cluster_manager=cluster_manager,
174 cluster_manager=cluster_manager,
173 session_manager=session_manager,
175 session_manager=session_manager,
174 kernel_spec_manager=kernel_spec_manager,
176 kernel_spec_manager=kernel_spec_manager,
177 config_manager=config_manager,
175
178
176 # IPython stuff
179 # IPython stuff
177 nbextensions_path = ipython_app.nbextensions_path,
180 nbextensions_path = ipython_app.nbextensions_path,
@@ -607,6 +610,11 b' class NotebookApp(BaseIPythonApplication):'
607 help='The cluster manager class to use.'
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 kernel_spec_manager = Instance(KernelSpecManager)
618 kernel_spec_manager = Instance(KernelSpecManager)
611
619
612 def _kernel_spec_manager_default(self):
620 def _kernel_spec_manager_default(self):
@@ -722,6 +730,10 b' class NotebookApp(BaseIPythonApplication):'
722 self.cluster_manager = kls(parent=self, log=self.log)
730 self.cluster_manager = kls(parent=self, log=self.log)
723 self.cluster_manager.update_profiles()
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 def init_logging(self):
737 def init_logging(self):
726 # This prevents double log messages because tornado use a root logger that
738 # This prevents double log messages because tornado use a root logger that
727 # self.log is a child of. The logging module dipatches log messages to a log
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 self.web_app = NotebookWebApplication(
759 self.web_app = NotebookWebApplication(
748 self, self.kernel_manager, self.contents_manager,
760 self, self.kernel_manager, self.contents_manager,
749 self.cluster_manager, self.session_manager, self.kernel_spec_manager,
761 self.cluster_manager, self.session_manager, self.kernel_spec_manager,
762 self.config_manager,
750 self.log, self.base_url, self.default_url, self.tornado_settings,
763 self.log, self.base_url, self.default_url, self.tornado_settings,
751 self.jinja_environment_options
764 self.jinja_environment_options
752 )
765 )
@@ -11,85 +11,27 b' from tornado import web'
11 from IPython.utils.py3compat import PY3
11 from IPython.utils.py3compat import PY3
12 from ...base.handlers import IPythonHandler, json_errors
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 class ConfigHandler(IPythonHandler):
14 class ConfigHandler(IPythonHandler):
35 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH')
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 @web.authenticated
17 @web.authenticated
52 @json_errors
18 @json_errors
53 def get(self, section_name):
19 def get(self, section_name):
54 self.set_header("Content-Type", 'application/json')
20 self.set_header("Content-Type", 'application/json')
55 filename = self.file_name(section_name)
21 self.finish(json.dumps(self.config_manager.get(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("{}")
61
22
62 @web.authenticated
23 @web.authenticated
63 @json_errors
24 @json_errors
64 def put(self, section_name):
25 def put(self, section_name):
65 self.get_json_body() # Will raise 400 if content is not valid JSON
26 data = self.get_json_body() # Will raise 400 if content is not valid JSON
66 filename = self.file_name(section_name)
27 self.config_manager.set(section_name, data)
67 self.ensure_config_dir_exists()
68 with open(filename, 'wb') as f:
69 f.write(self.request.body)
70 self.set_status(204)
28 self.set_status(204)
71
29
72 @web.authenticated
30 @web.authenticated
73 @json_errors
31 @json_errors
74 def patch(self, section_name):
32 def patch(self, section_name):
75 filename = self.file_name(section_name)
33 new_data = self.get_json_body()
76 if os.path.isfile(filename):
34 section = self.config_manager.update(section_name, new_data)
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
93 self.finish(json.dumps(section))
35 self.finish(json.dumps(section))
94
36
95
37
General Comments 0
You need to be logged in to leave comments. Login now