consolidate yaml configuration

workaround ruamel.yaml issue 255,
where once an empty dict or list has been written,
'flow' style is used thereafter, using dense `{key: value}` form
instead of traditional yaml block style.
This commit is contained in:
Min RK
2018-11-01 11:34:16 +01:00
parent d79155380a
commit d331936812
4 changed files with 61 additions and 13 deletions

19
tests/test_yaml.py Normal file
View File

@@ -0,0 +1,19 @@
from tljh.yaml import yaml
def test_no_empty_flow(tmpdir):
path = tmpdir.join("config.yaml")
with path.open("w") as f:
f.write("{}")
# load empty config file
with path.open("r") as f:
config = yaml.load(f)
# set a value
config["key"] = "value"
# write to a file
with path.open("w") as f:
yaml.dump(config, f)
# verify that it didn't use compact '{}' flow-style
with path.open("r") as f:
content = f.read()
assert content.strip() == "key: value"

View File

@@ -13,14 +13,13 @@ tljh-config show firstlevel.second_level
""" """
import argparse import argparse
from collections import Sequence, Mapping
from copy import deepcopy from copy import deepcopy
import os import os
import re import re
import sys import sys
from ruamel.yaml import YAML from .yaml import yaml
from ruamel.yaml.comments import CommentedMap, CommentedSeq
yaml = YAML(typ='rt')
INSTALL_PREFIX = os.environ.get('TLJH_INSTALL_PREFIX', '/opt/tljh') INSTALL_PREFIX = os.environ.get('TLJH_INSTALL_PREFIX', '/opt/tljh')
@@ -211,11 +210,11 @@ def parse_value(value_str):
def _is_dict(item): def _is_dict(item):
return isinstance(item, (dict, CommentedMap)) return isinstance(item, Mapping)
def _is_list(item): def _is_list(item):
return isinstance(item, (list, CommentedSeq)) return isinstance(item, Sequence)
def main(argv=None): def main(argv=None):

View File

@@ -12,7 +12,6 @@ from urllib.error import HTTPError
from urllib.request import urlopen, URLError from urllib.request import urlopen, URLError
import pluggy import pluggy
from ruamel.yaml import YAML
from tljh import ( from tljh import (
apt, apt,
@@ -23,8 +22,7 @@ from tljh import (
traefik, traefik,
user, user,
) )
from .config import (
from tljh.config import (
CONFIG_DIR, CONFIG_DIR,
CONFIG_FILE, CONFIG_FILE,
HUB_ENV_PREFIX, HUB_ENV_PREFIX,
@@ -32,10 +30,10 @@ from tljh.config import (
STATE_DIR, STATE_DIR,
USER_ENV_PREFIX, USER_ENV_PREFIX,
) )
from .yaml import yaml
HERE = os.path.abspath(os.path.dirname(__file__)) HERE = os.path.abspath(os.path.dirname(__file__))
rt_yaml = YAML()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -263,7 +261,7 @@ def ensure_admins(admins):
config_path = CONFIG_FILE config_path = CONFIG_FILE
if os.path.exists(config_path): if os.path.exists(config_path):
with open(config_path, 'r') as f: with open(config_path, 'r') as f:
config = rt_yaml.load(f) config = yaml.load(f)
else: else:
config = {} config = {}
@@ -271,7 +269,7 @@ def ensure_admins(admins):
config['users']['admin'] = list(admins) config['users']['admin'] = list(admins)
with open(config_path, 'w+') as f: with open(config_path, 'w+') as f:
rt_yaml.dump(config, f) yaml.dump(config, f)
def ensure_jupyterhub_running(times=4): def ensure_jupyterhub_running(times=4):
@@ -388,7 +386,7 @@ def ensure_config_yaml(plugin_manager):
if os.path.exists(CONFIG_FILE): if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r') as f: with open(CONFIG_FILE, 'r') as f:
config = rt_yaml.load(f) config = yaml.load(f)
else: else:
config = {} config = {}
@@ -396,7 +394,7 @@ def ensure_config_yaml(plugin_manager):
hook.tljh_config_post_install(config=config) hook.tljh_config_post_install(config=config)
with open(CONFIG_FILE, 'w+') as f: with open(CONFIG_FILE, 'w+') as f:
rt_yaml.dump(config, f) yaml.dump(config, f)
def main(): def main():

32
tljh/yaml.py Normal file
View File

@@ -0,0 +1,32 @@
"""consolidated yaml API
ensures the same yaml settings for reading/writing
throughout tljh
"""
from ruamel.yaml.composer import Composer
from ruamel.yaml import YAML
class _NoEmptyFlowComposer(Composer):
"""yaml composer that avoids setting flow_style on empty
containers.
workaround ruamel.yaml issue #255
"""
def compose_mapping_node(self, anchor):
node = super().compose_mapping_node(anchor)
if not node.value:
node.flow_style = False
return node
def compose_sequence_node(self, anchor):
node = super().compose_sequence_node(anchor)
if not node.value:
node.flow_style = False
return node
# create the global yaml object:
yaml = YAML(typ="rt")
yaml.Composer = _NoEmptyFlowComposer