Engine for LaTeX songbooks http://www.patacrep.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

119 lines
3.5 KiB

"""Some utility functions"""
from collections import UserDict
import yaml
from patacrep import encoding, errors, pkg_datapath, Rx
class DictOfDict(UserDict):
"""Dictionary, with a recursive :meth:`update` method.
By "recursive", we mean: if `self.update(other)` is called, and for some
key both `self[key]` and `other[key]` are dictionary, then `self[key]` is
not replaced by `other[key]`, but instead is updated. This is done
recursively (that is, `self[foo][bar][baz]` is updated with
`other[foo][bar][baz]`, if the corresponding objects are dictionaries).
>>> ordinal = DictOfDict({
... "francais": {
... 1: "premier",
... 2: "deuxieme",
... },
... "english": {
... 1: "first",
... },
... })
>>> ordinal.update({
... "francais": {
... 2: "second",
... 3: "troisieme",
... },
... "espanol": {
... 1: "primero",
... },
... })
>>> ordinal == {
... "francais": {
... 1: "premier",
... 2: "second",
... 3: "troisieme",
... },
... "english": {
... 1: "first",
... },
... "espanol": {
... 1: "primero",
... },
... }
True
"""
def update(self, other):
# pylint: disable=arguments-differ
self._update(self, other)
@staticmethod
def _update(left, right):
"""Equivalent to `left.update(right)`, with recursive update."""
for key in right:
if key not in left:
left[key] = right[key]
elif isinstance(left[key], dict) and isinstance(right[key], dict):
DictOfDict._update(left[key], right[key])
else:
left[key] = right[key]
def yesno(string):
"""Interpret string argument as a boolean.
May raise `ValueError` if argument cannot be interpreted.
"""
yes_strings = ["y", "yes", "true", "1"]
no_strings = ["n", "no", "false", "0"]
if string.lower() in yes_strings:
return True
if string.lower() in no_strings:
return False
raise ValueError("'{}' is supposed to be one of {}.".format(
string,
", ".join(["'{}'".format(string) for string in yes_strings + no_strings]),
))
def remove_keys(data, keys=None, recursive=True):
"""
Remove the keys of the dict
"""
if isinstance(data, dict):
for key in keys:
if key in data:
del data[key]
if recursive:
for key in data:
data[key] = remove_keys(data[key], keys, True)
return data
elif isinstance(data, list) and recursive:
return [remove_keys(elt, keys, True) for elt in data]
return data
def validate_config_schema(config):
"""
Check that the songbook config respects the excepted songbook schema
"""
data = config.copy()
data = remove_keys(data, ['_cache'])
rx_checker = Rx.Factory({"register_core_types": True})
schema_path = pkg_datapath('templates', 'songbook_schema.yml')
with encoding.open_read(schema_path) as schema_file:
schema_struct = yaml.load(schema_file)
schema_struct = remove_keys(schema_struct, ['_description'])
schema = rx_checker.make_schema(schema_struct)
try:
schema.validate(data)
except Rx.SchemaMismatch as exception:
msg = 'Could not parse songbook file:\n' + str(exception)
raise errors.SBFileError(msg)
return True