First stab at ConfigManager class

This commit is contained in:
Thomas Kluyver 2014-11-25 18:02:24 -08:00
parent cf91873644
commit febd670716
4 changed files with 112 additions and 66 deletions

View File

@ -175,6 +175,10 @@ class IPythonHandler(AuthenticatedHandler):
def kernel_spec_manager(self): def kernel_spec_manager(self):
return self.settings['kernel_spec_manager'] return self.settings['kernel_spec_manager']
@property
def config_manager(self):
return self.settings['config_manager']
#--------------------------------------------------------------- #---------------------------------------------------------------
# CORS # CORS
#--------------------------------------------------------------- #---------------------------------------------------------------

View File

@ -125,19 +125,21 @@ def load_handlers(name):
class NotebookWebApplication(web.Application): class NotebookWebApplication(web.Application):
def __init__(self, ipython_app, kernel_manager, contents_manager, def __init__(self, ipython_app, kernel_manager, contents_manager,
cluster_manager, session_manager, kernel_spec_manager, log, cluster_manager, session_manager, kernel_spec_manager,
config_manager, log,
base_url, default_url, settings_overrides, jinja_env_options): base_url, default_url, settings_overrides, jinja_env_options):
settings = self.init_settings( settings = self.init_settings(
ipython_app, kernel_manager, contents_manager, cluster_manager, ipython_app, kernel_manager, contents_manager, cluster_manager,
session_manager, kernel_spec_manager, log, base_url, default_url, session_manager, kernel_spec_manager, config_manager, log, base_url,
settings_overrides, jinja_env_options) default_url, settings_overrides, jinja_env_options)
handlers = self.init_handlers(settings) handlers = self.init_handlers(settings)
super(NotebookWebApplication, self).__init__(handlers, **settings) super(NotebookWebApplication, self).__init__(handlers, **settings)
def init_settings(self, ipython_app, kernel_manager, contents_manager, def init_settings(self, ipython_app, kernel_manager, contents_manager,
cluster_manager, session_manager, kernel_spec_manager, cluster_manager, session_manager, kernel_spec_manager,
config_manager,
log, base_url, default_url, settings_overrides, log, base_url, default_url, settings_overrides,
jinja_env_options=None): jinja_env_options=None):
@ -172,6 +174,7 @@ class NotebookWebApplication(web.Application):
cluster_manager=cluster_manager, cluster_manager=cluster_manager,
session_manager=session_manager, session_manager=session_manager,
kernel_spec_manager=kernel_spec_manager, kernel_spec_manager=kernel_spec_manager,
config_manager=config_manager,
# IPython stuff # IPython stuff
nbextensions_path = ipython_app.nbextensions_path, nbextensions_path = ipython_app.nbextensions_path,
@ -607,6 +610,11 @@ class NotebookApp(BaseIPythonApplication):
help='The cluster manager class to use.' help='The cluster manager class to use.'
) )
config_manager_class = DottedObjectName('IPython.html.services.config.manager.ConfigManager',
config = True,
help='The config manager class to use'
)
kernel_spec_manager = Instance(KernelSpecManager) kernel_spec_manager = Instance(KernelSpecManager)
def _kernel_spec_manager_default(self): def _kernel_spec_manager_default(self):
@ -722,6 +730,10 @@ class NotebookApp(BaseIPythonApplication):
self.cluster_manager = kls(parent=self, log=self.log) self.cluster_manager = kls(parent=self, log=self.log)
self.cluster_manager.update_profiles() self.cluster_manager.update_profiles()
kls = import_item(self.config_manager_class)
self.config_manager = kls(parent=self, log=self.log,
profile_dir=self.profile_dir.location)
def init_logging(self): def init_logging(self):
# This prevents double log messages because tornado use a root logger that # This prevents double log messages because tornado use a root logger that
# self.log is a child of. The logging module dipatches log messages to a log # self.log is a child of. The logging module dipatches log messages to a log
@ -747,6 +759,7 @@ class NotebookApp(BaseIPythonApplication):
self.web_app = NotebookWebApplication( self.web_app = NotebookWebApplication(
self, self.kernel_manager, self.contents_manager, self, self.kernel_manager, self.contents_manager,
self.cluster_manager, self.session_manager, self.kernel_spec_manager, self.cluster_manager, self.session_manager, self.kernel_spec_manager,
self.config_manager,
self.log, self.base_url, self.default_url, self.tornado_settings, self.log, self.base_url, self.default_url, self.tornado_settings,
self.jinja_environment_options self.jinja_environment_options
) )

View File

@ -11,85 +11,27 @@ from tornado import web
from IPython.utils.py3compat import PY3 from IPython.utils.py3compat import PY3
from ...base.handlers import IPythonHandler, json_errors from ...base.handlers import IPythonHandler, json_errors
def recursive_update(target, new):
"""Recursively update one dictionary using another.
None values will delete their keys.
"""
for k, v in new.items():
if isinstance(v, dict):
if k not in target:
target[k] = {}
recursive_update(target[k], v)
if not target[k]:
# Prune empty subdicts
del target[k]
elif v is None:
target.pop(k, None)
else:
target[k] = v
class ConfigHandler(IPythonHandler): class ConfigHandler(IPythonHandler):
SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH') SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH')
@property
def config_dir(self):
return os.path.join(self.profile_dir, 'nbconfig')
def ensure_config_dir_exists(self):
try:
os.mkdir(self.config_dir, 0o755)
except OSError as e:
if e.errno != errno.EEXIST:
raise
def file_name(self, section_name):
return os.path.join(self.config_dir, section_name+'.json')
@web.authenticated @web.authenticated
@json_errors @json_errors
def get(self, section_name): def get(self, section_name):
self.set_header("Content-Type", 'application/json') self.set_header("Content-Type", 'application/json')
filename = self.file_name(section_name) self.finish(json.dumps(self.config_manager.get(section_name)))
if os.path.isfile(filename):
with io.open(filename, encoding='utf-8') as f:
self.finish(f.read())
else:
self.finish("{}")
@web.authenticated @web.authenticated
@json_errors @json_errors
def put(self, section_name): def put(self, section_name):
self.get_json_body() # Will raise 400 if content is not valid JSON data = self.get_json_body() # Will raise 400 if content is not valid JSON
filename = self.file_name(section_name) self.config_manager.set(section_name, data)
self.ensure_config_dir_exists()
with open(filename, 'wb') as f:
f.write(self.request.body)
self.set_status(204) self.set_status(204)
@web.authenticated @web.authenticated
@json_errors @json_errors
def patch(self, section_name): def patch(self, section_name):
filename = self.file_name(section_name) new_data = self.get_json_body()
if os.path.isfile(filename): section = self.config_manager.update(section_name, new_data)
with io.open(filename, encoding='utf-8') as f:
section = json.load(f)
else:
section = {}
update = self.get_json_body()
recursive_update(section, update)
self.ensure_config_dir_exists()
if PY3:
f = io.open(filename, 'w', encoding='utf-8')
else:
f = open(filename, 'wb')
with f:
json.dump(section, f)
self.finish(json.dumps(section)) self.finish(json.dumps(section))

View File

@ -0,0 +1,87 @@
"""Manager to read and modify frontend config data in JSON files.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import errno
import io
import json
import os
from IPython.config import LoggingConfigurable
from IPython.utils.py3compat import PY3
from IPython.utils.traitlets import Unicode
def recursive_update(target, new):
"""Recursively update one dictionary using another.
None values will delete their keys.
"""
for k, v in new.items():
if isinstance(v, dict):
if k not in target:
target[k] = {}
recursive_update(target[k], v)
if not target[k]:
# Prune empty subdicts
del target[k]
elif v is None:
target.pop(k, None)
else:
target[k] = v
class ConfigManager(LoggingConfigurable):
profile_dir = Unicode()
@property
def config_dir(self):
return os.path.join(self.profile_dir, 'nbconfig')
def ensure_config_dir_exists(self):
try:
os.mkdir(self.config_dir, 0o755)
except OSError as e:
if e.errno != errno.EEXIST:
raise
def file_name(self, section_name):
return os.path.join(self.config_dir, section_name+'.json')
def get(self, section_name):
"""Retrieve the config data for the specified section.
Returns the data as a dictionary, or an empty dictionary if the file
doesn't exist.
"""
filename = self.file_name(section_name)
if os.path.isfile(filename):
with io.open(filename, encoding='utf-8') as f:
return json.load(f)
else:
return {}
def set(self, section_name, data):
"""Store the given config data.
"""
filename = self.file_name(section_name)
self.ensure_config_dir_exists()
if PY3:
f = io.open(filename, 'w', encoding='utf-8')
else:
f = open(filename, 'wb')
with f:
json.dump(data, f)
def update(self, section_name, new_data):
"""Modify the config section by recursively updating it with new_data.
Returns the modified config data as a dictionary.
"""
data = self.get(section_name)
recursive_update(data, new_data)
self.set(section_name, data)
return data