Skip to content

Commit

Permalink
add overrides.d for settings defaults (#224)
Browse files Browse the repository at this point in the history
  • Loading branch information
bollwyvl committed Dec 13, 2021
1 parent 5f763c6 commit e0fc91b
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 9 deletions.
40 changes: 32 additions & 8 deletions jupyterlab_server/settings_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,26 +266,50 @@ def _path(root_dir, schema_name, make_dirs=False, extension='.json'):

return path


def _get_overrides(app_settings_dir):
"""Get overrides settings from `app_settings_dir`."""
"""Get overrides settings from `app_settings_dir`.
The ordering of paths is:
- {app_settings_dir}/overrides.d/*.{json,json5} (many, namespaced by package)
- {app_settings_dir}/overrides.{json,json5} (singleton, owned by the user)
"""
overrides, error = {}, ""
overrides_path = os.path.join(app_settings_dir, 'overrides.json')

if not os.path.exists(overrides_path):
overrides_path = os.path.join(app_settings_dir, 'overrides.json5')
overrides_d = os.path.join(app_settings_dir, 'overrides.d')

# find (and sort) the conf.d overrides files
all_override_paths = sorted([
*(glob(os.path.join(overrides_d, '*.json'))),
*(glob(os.path.join(overrides_d, '*.json5'))),
])

all_override_paths += [
os.path.join(app_settings_dir, 'overrides.json'),
os.path.join(app_settings_dir, 'overrides.json5')
]

for overrides_path in all_override_paths:
if not os.path.exists(overrides_path):
continue

if os.path.exists(overrides_path):
with open(overrides_path, encoding='utf-8') as fid:
try:
overrides = json5.load(fid)
if overrides_path.endswith('.json5'):
path_overrides = json5.load(fid)
else:
path_overrides = json.load(fid)
for plugin_id, config in path_overrides.items():
recursive_update(overrides.setdefault(plugin_id, {}), config)
print(overrides_path, overrides)
except Exception as e:
error = e

# Allow `default_settings_overrides.json` files in <jupyter_config>/labconfig dirs
# to allow layering of defaults
cm = ConfigManager(config_dir_name="labconfig")
recursive_update(overrides, cm.get('default_setting_overrides'))

for plugin_id, config in cm.get('default_setting_overrides').items():
recursive_update(overrides.setdefault(plugin_id, {}), config)

return overrides, error

Expand Down
25 changes: 24 additions & 1 deletion jupyterlab_server/tests/test_settings_api.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Test the Settings service API.
"""
from pathlib import Path
import json

import pytest
import json
import json5
import tornado

Expand All @@ -28,6 +29,28 @@ async def test_get_settings_overrides_dicts(jp_fetch, labserverapp):
assert len(schema['properties']['codeCellConfig']['default']) == 15


@pytest.mark.parametrize('ext', ['json', 'json5'])
async def test_get_settings_overrides_d_dicts(jp_fetch, labserverapp, ext):
# Check that values that are dictionaries in overrides.d/*.json are
# merged with the schema.
id = '@jupyterlab/apputils-extension:themes'
overrides_d = Path(labserverapp.app_settings_dir) / "overrides.d"
overrides_d.mkdir(exist_ok=True, parents=True)
for i in range(10):
text = json.dumps({id: {'codeCellConfig': {'cursorBlinkRate': 530 + i}}})
if ext == 'json5':
text += '\n// a comment'
(overrides_d / f"foo-{i}.{ext}").write_text(text, encoding='utf-8')
r = await jp_fetch('lab', 'api', 'settings', id)
validate_request(r)
res = r.body.decode()
data = json.loads(res)
assert data['id'] == id
schema = data['schema']
# Check that the last overrides.d/*.json file is respected.
assert schema['properties']['codeCellConfig']['default']['cursorBlinkRate'] == 539


async def test_get_settings(jp_fetch, labserverapp):
id = '@jupyterlab/apputils-extension:themes'
r = await jp_fetch('lab', 'api', 'settings', id)
Expand Down

0 comments on commit e0fc91b

Please sign in to comment.