Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions traitlets/config/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import errno
import glob
import io
import json
import os

from six import PY3
from traitlets.config import LoggingConfigurable
from traitlets.traitlets import Unicode
from traitlets.traitlets import Unicode, Bool


def recursive_update(target, new):
Expand All @@ -36,10 +37,12 @@ def recursive_update(target, new):
class BaseJSONConfigManager(LoggingConfigurable):
"""General JSON config manager

Deals with persisting/storing config in a json file
Deals with persisting/storing config in a json file with optionally
default values in a {section_name}.d directory.
"""

config_dir = Unicode('.')
read_directory = Bool(True)

def ensure_config_dir_exists(self):
try:
Expand All @@ -51,18 +54,30 @@ def ensure_config_dir_exists(self):
def file_name(self, section_name):
return os.path.join(self.config_dir, section_name+'.json')

def directory(self, section_name):
return os.path.join(self.config_dir, section_name+'.d')

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 {}
paths = [self.file_name(section_name)]
if self.read_directory:
pattern = os.path.join(self.directory(section_name), '*.json')
# These json files should be processed first so that the
# {section_name}.json take precedence.
# The idea behind this is that installing a Python package may
# put a json file somewhere in the a .d directory, while the
# .json file is probably a user configuration.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

paths = sorted(glob.glob(pattern)) + paths
data = {}
for path in paths:
if os.path.isfile(path):
with io.open(path, encoding='utf-8') as f:
recursive_update(data, json.load(f))
return data

def set(self, section_name, data):
"""Store the given config data.
Expand Down
34 changes: 34 additions & 0 deletions traitlets/config/tests/test_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import json
import os

from traitlets.config.manager import BaseJSONConfigManager


def test_json(tmpdir):
tmpdir = str(tmpdir) # we're ok with a regular string path
with open(os.path.join(tmpdir, 'foo.json'), 'w') as f:
json.dump(dict(a=1), f)
# also make a foo.d/ directory with multiple json files
os.makedirs(os.path.join(tmpdir, 'foo.d'))
with open(os.path.join(tmpdir, 'foo.d', 'a.json'), 'w') as f:
json.dump(dict(a=2, b=1), f)
with open(os.path.join(tmpdir, 'foo.d', 'b.json'), 'w') as f:
json.dump(dict(a=3, b=2, c=3), f)
manager = BaseJSONConfigManager(config_dir=tmpdir, read_directory=False)
data = manager.get('foo')
assert 'a' in data
assert 'b' not in data
assert 'c' not in data
assert data['a'] == 1

manager = BaseJSONConfigManager(config_dir=tmpdir, read_directory=True)
data = manager.get('foo')
assert 'a' in data
assert 'b' in data
assert 'c' in data
# files should be read in order foo.d/a.json foo.d/b.json foo.json
assert data['a'] == 1
assert data['b'] == 2
assert data['c'] == 3