From 7f26070f1b2b0dba98169789c911748c0a20f7ec Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 11:40:27 +0100 Subject: [PATCH 01/48] Use Rx to check songbook schema --- patacrep/Rx.py | 567 ++++++++++++++++++++ patacrep/build.py | 20 +- patacrep/data/templates/songbook_schema.yml | 1 + 3 files changed, 586 insertions(+), 2 deletions(-) create mode 100644 patacrep/Rx.py create mode 100644 patacrep/data/templates/songbook_schema.yml diff --git a/patacrep/Rx.py b/patacrep/Rx.py new file mode 100644 index 00000000..47219f33 --- /dev/null +++ b/patacrep/Rx.py @@ -0,0 +1,567 @@ +# Downloaded from https://github.com/rjbs/rx +# The contents of the Rx repository are copyright (C) 2008, Ricardo SIGNES. +# They may be distributed under the terms of the +# GNU Public License (GPL) Version 2, June 1991 + +#pylint: skip-file + +import re +from six import string_types # for 2-3 compatibility +import types +from numbers import Number + + +core_types = [ ] + +class SchemaError(Exception): + pass + +class SchemaMismatch(Exception): + pass + +class SchemaTypeMismatch(SchemaMismatch): + def __init__(self, name, desired_type): + SchemaMismatch.__init__(self, '{0} must be {1}'.format(name, desired_type)) + +class SchemaValueMismatch(SchemaMismatch): + def __init__(self, name, value): + SchemaMismatch.__init__(self, '{0} must equal {1}'.format(name, value)) + +class SchemaRangeMismatch(SchemaMismatch): + pass + +def indent(text, level=1, whitespace=' '): + return '\n'.join(whitespace*level+line for line in text.split('\n')) + +class Util(object): + @staticmethod + def make_range_check(opt): + + if not {'min', 'max', 'min-ex', 'max-ex'}.issuperset(opt): + raise ValueError("illegal argument to make_range_check") + if {'min', 'min-ex'}.issubset(opt): + raise ValueError("Cannot define both exclusive and inclusive min") + if {'max', 'max-ex'}.issubset(opt): + raise ValueError("Cannot define both exclusive and inclusive max") + + r = opt.copy() + inf = float('inf') + + def check_range(value): + return( + r.get('min', -inf) <= value and \ + r.get('max', inf) >= value and \ + r.get('min-ex', -inf) < value and \ + r.get('max-ex', inf) > value + ) + + return check_range + + @staticmethod + def make_range_validator(opt): + check_range = Util.make_range_check(opt) + + r = opt.copy() + nan = float('nan') + + def validate_range(value, name='value'): + if not check_range(value): + if r.get('min', nan) == r.get('max', nan): + msg = '{0} must equal {1}'.format(name, r['min']) + raise SchemaRangeMismatch(msg) + + range_str = '' + if 'min' in r: + range_str = '[{0}, '.format(r['min']) + elif 'min-ex' in r: + range_str = '({0}, '.format(r['min-ex']) + else: + range_str = '(-inf, ' + + if 'max' in r: + range_str += '{0}]'.format(r['max']) + elif 'max-ex' in r: + range_str += '{0})'.format(r['max-ex']) + else: + range_str += 'inf)' + + raise SchemaRangeMismatch(name+' must be in range '+range_str) + + return validate_range + + +class Factory(object): + def __init__(self, register_core_types=True): + self.prefix_registry = { + '': 'tag:codesimply.com,2008:rx/core/', + '.meta': 'tag:codesimply.com,2008:rx/meta/', + } + + self.type_registry = {} + if register_core_types: + for t in core_types: self.register_type(t) + + @staticmethod + def _default_prefixes(): pass + + def expand_uri(self, type_name): + if re.match('^\w+:', type_name): return type_name + + m = re.match('^/([-._a-z0-9]*)/([-._a-z0-9]+)$', type_name) + + if not m: + raise ValueError("couldn't understand type name '{0}'".format(type_name)) + + prefix, suffix = m.groups() + + if prefix not in self.prefix_registry: + raise KeyError( + "unknown prefix '{0}' in type name '{1}'".format(prefix, type_name) + ) + + return self.prefix_registry[ prefix ] + suffix + + def add_prefix(self, name, base): + if self.prefix_registry.get(name): + raise SchemaError("the prefix '{0}' is already registered".format(name)) + + self.prefix_registry[name] = base; + + def register_type(self, t): + t_uri = t.uri() + + if t_uri in self.type_registry: + raise ValueError("type already registered for {0}".format(t_uri)) + + self.type_registry[t_uri] = t + + def learn_type(self, uri, schema): + if self.type_registry.get(uri): + raise SchemaError("tried to learn type for already-registered uri {0}".format(uri)) + + # make sure schema is valid + # should this be in a try/except? + self.make_schema(schema) + + self.type_registry[uri] = { 'schema': schema } + + def make_schema(self, schema): + if isinstance(schema, string_types): + schema = { 'type': schema } + + if not isinstance(schema, dict): + raise SchemaError('invalid schema argument to make_schema') + + uri = self.expand_uri(schema['type']) + + if not self.type_registry.get(uri): raise SchemaError("unknown type {0}".format(uri)) + + type_class = self.type_registry[uri] + + if isinstance(type_class, dict): + if not {'type'}.issuperset(schema): + raise SchemaError('composed type does not take check arguments'); + return self.make_schema(type_class['schema']) + else: + return type_class(schema, self) + +class _CoreType(object): + @classmethod + def uri(self): + return 'tag:codesimply.com,2008:rx/core/' + self.subname() + + def __init__(self, schema, rx): + if not {'type'}.issuperset(schema): + raise SchemaError('unknown parameter for //{0}'.format(self.subname())) + + def check(self, value): + try: + self.validate(value) + except SchemaMismatch: + return False + return True + + def validate(self, value, name='value'): + raise SchemaMismatch('Tried to validate abstract base schema class') + +class AllType(_CoreType): + @staticmethod + def subname(): return 'all' + + def __init__(self, schema, rx): + if not {'type', 'of'}.issuperset(schema): + raise SchemaError('unknown parameter for //all') + + if not(schema.get('of') and len(schema.get('of'))): + raise SchemaError('no alternatives given in //all of') + + self.alts = [rx.make_schema(s) for s in schema['of']] + + def validate(self, value, name='value'): + error_messages = [] + for schema in self.alts: + try: + schema.validate(value, name) + except SchemaMismatch as e: + error_messages.append(str(e)) + + if len(error_messages) > 1: + messages = indent('\n'.join(error_messages)) + message = '{0} failed to meet all schema requirements:\n{1}' + message = message.format(name, messages) + raise SchemaMismatch(message) + elif len(error_messages) == 1: + raise SchemaMismatch(error_messages[0]) + +class AnyType(_CoreType): + @staticmethod + def subname(): return 'any' + + def __init__(self, schema, rx): + self.alts = None + + if not {'type', 'of'}.issuperset(schema): + raise SchemaError('unknown parameter for //any') + + if 'of' in schema: + if not schema['of']: raise SchemaError('no alternatives given in //any of') + self.alts = [ rx.make_schema(alt) for alt in schema['of'] ] + + def validate(self, value, name='value'): + if self.alts is None: + return + error_messages = [] + for schema in self.alts: + try: + schema.validate(value, name) + break + except SchemaMismatch as e: + error_messages.append(str(e)) + + if len(error_messages) == len(self.alts): + messages = indent('\n'.join(error_messages)) + message = '{0} failed to meet any schema requirements:\n{1}' + message = message.format(name, messages) + raise SchemaMismatch(message) + +class ArrType(_CoreType): + @staticmethod + def subname(): return 'arr' + + def __init__(self, schema, rx): + self.length = None + + if not {'type', 'contents', 'length'}.issuperset(schema): + raise SchemaError('unknown parameter for //arr') + + if not schema.get('contents'): + raise SchemaError('no contents provided for //arr') + + self.content_schema = rx.make_schema(schema['contents']) + + if schema.get('length'): + self.length = Util.make_range_validator(schema['length']) + + def validate(self, value, name='value'): + if not isinstance(value, (list, tuple)): + raise SchemaTypeMismatch(name, 'array') + + if self.length: + self.length(len(value), name+' length') + + error_messages = [] + + for i, item in enumerate(value): + try: + self.content_schema.validate(item, 'item '+str(i)) + except SchemaMismatch as e: + error_messages.append(str(e)) + + if len(error_messages) > 1: + messages = indent('\n'.join(error_messages)) + message = '{0} sequence contains invalid elements:\n{1}' + message = message.format(name, messages) + raise SchemaMismatch(message) + elif len(error_messages) == 1: + raise SchemaMismatch(name+': '+error_messages[0]) + +class BoolType(_CoreType): + @staticmethod + def subname(): return 'bool' + + def validate(self, value, name='value'): + if not isinstance(value, bool): + raise SchemaTypeMismatch(name, 'boolean') + +class DefType(_CoreType): + @staticmethod + def subname(): return 'def' + + + def validate(self, value, name='value'): + if value is None: + raise SchemaMismatch(name+' must be non-null') + +class FailType(_CoreType): + @staticmethod + def subname(): return 'fail' + + def check(self, value): return False + + def validate(self, value, name='value'): + raise SchemaMismatch(name+' is of fail type, automatically invalid.') + +class IntType(_CoreType): + @staticmethod + def subname(): return 'int' + + def __init__(self, schema, rx): + if not {'type', 'range', 'value'}.issuperset(schema): + raise SchemaError('unknown parameter for //int') + + self.value = None + if 'value' in schema: + if not isinstance(schema['value'], Number) or schema['value'] % 1 != 0: + raise SchemaError('invalid value parameter for //int') + self.value = schema['value'] + + self.range = None + if 'range' in schema: + self.range = Util.make_range_validator(schema['range']) + + def validate(self, value, name='value'): + if not isinstance(value, Number) or isinstance(value, bool) or value%1: + raise SchemaTypeMismatch(name,'integer') + + if self.range: + self.range(value, name) + + if self.value is not None and value != self.value: + raise SchemaValueMismatch(name, self.value) + +class MapType(_CoreType): + @staticmethod + def subname(): return 'map' + + def __init__(self, schema, rx): + self.allowed = set() + + if not {'type', 'values'}.issuperset(schema): + raise SchemaError('unknown parameter for //map') + + if not schema.get('values'): + raise SchemaError('no values given for //map') + + self.value_schema = rx.make_schema(schema['values']) + + def validate(self, value, name='value'): + if not isinstance(value, dict): + raise SchemaTypeMismatch(name, 'map') + + error_messages = [] + + for key, val in value.items(): + try: + self.value_schema.validate(val, key) + except SchemaMismatch as e: + error_messages.append(str(e)) + + if len(error_messages) > 1: + messages = indent('\n'.join(error_messages)) + message = '{0} map contains invalid entries:\n{1}' + message = message.format(name, messages) + raise SchemaMismatch(message) + elif len(error_messages) == 1: + raise SchemaMismatch(name+': '+error_messages[0]) + +class NilType(_CoreType): + @staticmethod + def subname(): return 'nil' + + def check(self, value): return value is None + + def validate(self, value, name='value'): + if value is not None: + raise SchemaTypeMismatch(name, 'null') + +class NumType(_CoreType): + @staticmethod + def subname(): return 'num' + + def __init__(self, schema, rx): + if not {'type', 'range', 'value'}.issuperset(schema): + raise SchemaError('unknown parameter for //num') + + self.value = None + if 'value' in schema: + if not isinstance(schema['value'], Number): + raise SchemaError('invalid value parameter for //num') + self.value = schema['value'] + + self.range = None + + if schema.get('range'): + self.range = Util.make_range_validator(schema['range']) + + def validate(self, value, name='value'): + if not isinstance(value, Number) or isinstance(value, bool): + raise SchemaTypeMismatch(name, 'number') + + if self.range: + self.range(value, name) + + if self.value is not None and value != self.value: + raise SchemaValueMismatch(name, self.value) + +class OneType(_CoreType): + @staticmethod + def subname(): return 'one' + + def validate(self, value, name='value'): + if not isinstance(value, (Number, string_types)): + raise SchemaTypeMismatch(name, 'number or string') + +class RecType(_CoreType): + @staticmethod + def subname(): return 'rec' + + def __init__(self, schema, rx): + if not {'type', 'rest', 'required', 'optional'}.issuperset(schema): + raise SchemaError('unknown parameter for //rec') + + self.known = set() + self.rest_schema = None + if schema.get('rest'): self.rest_schema = rx.make_schema(schema['rest']) + + for which in ('required', 'optional'): + setattr(self, which, {}) + for field in schema.get(which, {}).keys(): + if field in self.known: + raise SchemaError('%s appears in both required and optional' % field) + + self.known.add(field) + + self.__getattribute__(which)[field] = rx.make_schema( + schema[which][field] + ) + + def validate(self, value, name='value'): + if not isinstance(value, dict): + raise SchemaTypeMismatch(name, 'record') + + unknown = [k for k in value.keys() if k not in self.known] + + if unknown and not self.rest_schema: + fields = indent('\n'.join(unknown)) + raise SchemaMismatch(name+' contains unknown fields:\n'+fields) + + error_messages = [] + + for field in self.required: + try: + if field not in value: + raise SchemaMismatch('missing required field: '+field) + self.required[field].validate(value[field], field) + except SchemaMismatch as e: + error_messages.append(str(e)) + + for field in self.optional: + if field not in value: continue + try: + self.optional[field].validate(value[field], field) + except SchemaMismatch as e: + error_messages.append(str(e)) + + if unknown: + rest = {key: value[key] for key in unknown} + try: + self.rest_schema.validate(rest, name) + except SchemaMismatch as e: + error_messages.append(str(e)) + + if len(error_messages) > 1: + messages = indent('\n'.join(error_messages)) + message = '{0} record is invalid:\n{1}' + message = message.format(name, messages) + raise SchemaMismatch(message) + elif len(error_messages) == 1: + raise SchemaMismatch(name+': '+error_messages[0]) + + +class SeqType(_CoreType): + @staticmethod + def subname(): return 'seq' + + def __init__(self, schema, rx): + if not {'type', 'contents', 'tail'}.issuperset(schema): + raise SchemaError('unknown parameter for //seq') + + if not schema.get('contents'): + raise SchemaError('no contents provided for //seq') + + self.content_schema = [ rx.make_schema(s) for s in schema['contents'] ] + + self.tail_schema = None + if (schema.get('tail')): + self.tail_schema = rx.make_schema(schema['tail']) + + def validate(self, value, name='value'): + if not isinstance(value, (list, tuple)): + raise SchemaTypeMismatch(name, 'sequence') + + if len(value) < len(self.content_schema): + raise SchemaMismatch(name+' is less than expected length') + + if len(value) > len(self.content_schema) and not self.tail_schema: + raise SchemaMismatch(name+' exceeds expected length') + + error_messages = [] + + for i, (schema, item) in enumerate(zip(self.content_schema, value)): + try: + schema.validate(item, 'item '+str(i)) + except SchemaMismatch as e: + error_messages.append(str(e)) + + if len(error_messages) > 1: + messages = indent('\n'.join(error_messages)) + message = '{0} sequence is invalid:\n{1}' + message = message.format(name, messages) + raise SchemaMismatch(message) + elif len(error_messages) == 1: + raise SchemaMismatch(name+': '+error_messages[0]) + + if len(value) > len(self.content_schema): + self.tail_schema.validate(value[len(self.content_schema):], name) + +class StrType(_CoreType): + @staticmethod + def subname(): return 'str' + + def __init__(self, schema, rx): + if not {'type', 'value', 'length'}.issuperset(schema): + raise SchemaError('unknown parameter for //str') + + self.value = None + if 'value' in schema: + if not isinstance(schema['value'], string_types): + raise SchemaError('invalid value parameter for //str') + self.value = schema['value'] + + self.length = None + if 'length' in schema: + self.length = Util.make_range_validator(schema['length']) + + def validate(self, value, name='value'): + if not isinstance(value, string_types): + raise SchemaTypeMismatch(name, 'string') + if self.value is not None and value != self.value: + raise SchemaValueMismatch(name, '"{0}"'.format(self.value)) + if self.length: + self.length(len(value), name+' length') + +core_types = [ + AllType, AnyType, ArrType, BoolType, DefType, + FailType, IntType, MapType, NilType, NumType, + OneType, RecType, SeqType, StrType +] diff --git a/patacrep/build.py b/patacrep/build.py index e277067c..ead25cbd 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -8,7 +8,9 @@ import threading import os.path from subprocess import Popen, PIPE, call, check_call -from patacrep import authors, content, errors, files +import yaml + +from patacrep import authors, content, errors, encoding, files, pkg_datapath, Rx from patacrep.index import process_sxd from patacrep.templates import TexBookRenderer from patacrep.songs import DataSubpath, DEFAULT_CONFIG @@ -41,7 +43,7 @@ class Songbook(object): def __init__(self, raw_songbook, basename): super().__init__() - self.config = raw_songbook + self.config = check_config_schema(raw_songbook) self.basename = basename # Some special keys have their value processed. self._set_datadir() @@ -326,3 +328,17 @@ class SongbookBuilder(object): os.unlink(self.basename + ext) except Exception as exception: raise errors.CleaningError(self.basename + ext, exception) + + +def check_config_schema(data): + """ + Check that the data respect the excepted songbook schema + + """ + 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 = rx_checker.make_schema(yaml.load(schema_file)) + if schema.check(data): + return data + raise errors.SBFileError('The songbook file does not respect the schema') diff --git a/patacrep/data/templates/songbook_schema.yml b/patacrep/data/templates/songbook_schema.yml new file mode 100644 index 00000000..e335e82d --- /dev/null +++ b/patacrep/data/templates/songbook_schema.yml @@ -0,0 +1 @@ +type: //any \ No newline at end of file From 20012b4c29076a25ed98301b72fc58656c17bc36 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 12:18:52 +0100 Subject: [PATCH 02/48] Remove _default and _description keys from the schema before using it --- patacrep/build.py | 7 +++++-- patacrep/data/templates/songbook_schema.yml | 3 ++- patacrep/utils.py | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index ead25cbd..60480ff1 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -10,7 +10,7 @@ from subprocess import Popen, PIPE, call, check_call import yaml -from patacrep import authors, content, errors, encoding, files, pkg_datapath, Rx +from patacrep import authors, content, errors, encoding, files, pkg_datapath, Rx, utils from patacrep.index import process_sxd from patacrep.templates import TexBookRenderer from patacrep.songs import DataSubpath, DEFAULT_CONFIG @@ -338,7 +338,10 @@ def check_config_schema(data): 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 = rx_checker.make_schema(yaml.load(schema_file)) + schema_struct = yaml.load(schema_file) + schema_struct = utils.remove_keys(schema_struct, ['_default', '_description']) + schema = rx_checker.make_schema(schema_struct) + if schema.check(data): return data raise errors.SBFileError('The songbook file does not respect the schema') diff --git a/patacrep/data/templates/songbook_schema.yml b/patacrep/data/templates/songbook_schema.yml index e335e82d..c41d2caa 100644 --- a/patacrep/data/templates/songbook_schema.yml +++ b/patacrep/data/templates/songbook_schema.yml @@ -1 +1,2 @@ -type: //any \ No newline at end of file +type: //any +_default: "test" \ No newline at end of file diff --git a/patacrep/utils.py b/patacrep/utils.py index 998fe738..d7a4142d 100644 --- a/patacrep/utils.py +++ b/patacrep/utils.py @@ -75,3 +75,19 @@ def yesno(string): 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 From 8e872dba419b50a5e5f9305a8f47b325c74821cd Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 12:21:42 +0100 Subject: [PATCH 03/48] Remove python 2 compatibility --- patacrep/Rx.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/patacrep/Rx.py b/patacrep/Rx.py index 47219f33..677a3461 100644 --- a/patacrep/Rx.py +++ b/patacrep/Rx.py @@ -6,7 +6,6 @@ #pylint: skip-file import re -from six import string_types # for 2-3 compatibility import types from numbers import Number @@ -146,7 +145,7 @@ class Factory(object): self.type_registry[uri] = { 'schema': schema } def make_schema(self, schema): - if isinstance(schema, string_types): + if isinstance(schema, str): schema = { 'type': schema } if not isinstance(schema, dict): @@ -418,7 +417,7 @@ class OneType(_CoreType): def subname(): return 'one' def validate(self, value, name='value'): - if not isinstance(value, (Number, string_types)): + if not isinstance(value, (Number, str)): raise SchemaTypeMismatch(name, 'number or string') class RecType(_CoreType): @@ -544,7 +543,7 @@ class StrType(_CoreType): self.value = None if 'value' in schema: - if not isinstance(schema['value'], string_types): + if not isinstance(schema['value'], str): raise SchemaError('invalid value parameter for //str') self.value = schema['value'] @@ -553,7 +552,7 @@ class StrType(_CoreType): self.length = Util.make_range_validator(schema['length']) def validate(self, value, name='value'): - if not isinstance(value, string_types): + if not isinstance(value, str): raise SchemaTypeMismatch(name, 'string') if self.value is not None and value != self.value: raise SchemaValueMismatch(name, '"{0}"'.format(self.value)) From 02551f16d085f9643ece0d2817e2e98f601a5ba1 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 15:29:18 +0100 Subject: [PATCH 04/48] Complete schema --- examples/example-all.yaml.sb | 36 +++++--- patacrep/build.py | 22 +---- .../data/templates/default_songbook.sb.yml | 46 ++++++++++ patacrep/data/templates/songbook_schema.yml | 91 ++++++++++++++++++- patacrep/utils.py | 26 ++++++ test/test_songbook/datadir.sb | 12 +-- 6 files changed, 191 insertions(+), 42 deletions(-) create mode 100644 patacrep/data/templates/default_songbook.sb.yml diff --git a/examples/example-all.yaml.sb b/examples/example-all.yaml.sb index 3a167565..4f287c6d 100644 --- a/examples/example-all.yaml.sb +++ b/examples/example-all.yaml.sb @@ -1,17 +1,23 @@ -bookoptions: - - "diagram" - - "repeatchords" - - "lilypond" - - "pictures" -booktype: "chorded" -datadir: "." -template: "patacrep.tex" -lang: "fr" -encoding: "utf8" -authwords: - sep: +# bookoptions: +# - "diagram" +# - "repeatchords" +# - "pictures" +# booktype: "chorded" +# datadir: "." +# template: "patacrep.tex" +# lang: "fr" +# encoding: "utf8" +book: + lang: fr + datadir: "." + pictures: yes + #type: chorded +chords: + show: true + diagramreminder: none + +authors: + separators: - "and" - "et" -content: - - - - "sorted" \ No newline at end of file +content: '[["sorted"]]' \ No newline at end of file diff --git a/patacrep/build.py b/patacrep/build.py index 60480ff1..065c8604 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -10,7 +10,7 @@ from subprocess import Popen, PIPE, call, check_call import yaml -from patacrep import authors, content, errors, encoding, files, pkg_datapath, Rx, utils +from patacrep import authors, content, errors, encoding, files, utils from patacrep.index import process_sxd from patacrep.templates import TexBookRenderer from patacrep.songs import DataSubpath, DEFAULT_CONFIG @@ -43,7 +43,8 @@ class Songbook(object): def __init__(self, raw_songbook, basename): super().__init__() - self.config = check_config_schema(raw_songbook) + self.config = raw_songbook + utils.validate_config_schema(raw_songbook) self.basename = basename # Some special keys have their value processed. self._set_datadir() @@ -328,20 +329,3 @@ class SongbookBuilder(object): os.unlink(self.basename + ext) except Exception as exception: raise errors.CleaningError(self.basename + ext, exception) - - -def check_config_schema(data): - """ - Check that the data respect the excepted songbook schema - - """ - 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 = utils.remove_keys(schema_struct, ['_default', '_description']) - schema = rx_checker.make_schema(schema_struct) - - if schema.check(data): - return data - raise errors.SBFileError('The songbook file does not respect the schema') diff --git a/patacrep/data/templates/default_songbook.sb.yml b/patacrep/data/templates/default_songbook.sb.yml new file mode 100644 index 00000000..54a8291d --- /dev/null +++ b/patacrep/data/templates/default_songbook.sb.yml @@ -0,0 +1,46 @@ +content: + +book: + lang: en + datadir: + encoding: utf-8 + pictures: yes + +chords: # Options relatives aux accords + diagramreminder: ENUM[important, none, all] (anciennement importantdiagramonly) + diagrampage: BOOLEAN Montrer la page d'accords + repeatchords: BOOLEAN + lilypond: BOOLEAN + instruments: ENUM[guitar, ukulele] + show: BOOLEAN (anciennement booktype=chorded ou booktype=lyrics) + notation: ENUM[alphascale, solfedge] + +authors: # Comment sont analysés les auteurs + separators: LISTE + ignore: LISTE + by: LISTE + +titles: # Comment sont analysés les titres + prefix: LISTE + +template: # Des choses spécifiques au template + file: STRING + latex: # Des choses spécifiques au LaTeX + classoptions: STRING + + # Peut dépendre fortement du template + color: # Des couleurs + songnumber: STRING + notebg: STRING + indexbg: STRING + + titlepage: #Configuration de la page de garde + title: STRING + author: STRING + subtitle: STRING + version: STRING + url: STRING + email: STRING + picture: STRING + picturecopyright: STRING + footer: STRING \ No newline at end of file diff --git a/patacrep/data/templates/songbook_schema.yml b/patacrep/data/templates/songbook_schema.yml index c41d2caa..6bbb7ba3 100644 --- a/patacrep/data/templates/songbook_schema.yml +++ b/patacrep/data/templates/songbook_schema.yml @@ -1,2 +1,89 @@ -type: //any -_default: "test" \ No newline at end of file +type: //rec +optional: + content: //str + book: + type: //rec + optional: + encoding: //str + lang: //str + pictures: //bool + datadir: + type: //arr + contents: //str + chords: + type: //rec + optional: + show: //bool + diagrampage: //bool + repeatchords: //bool + lilypond: //bool + diagramreminder: + type: //any + of: + - type: //str + value: "none" + - type: //str + value: "important" + - type: //str + value: "all" + instruments: + type: //any + of: + - type: //str + value: "guitar" + - type: //str + value: "ukulele" + notation: + type: //any + of: + - type: //str + value: "alphascale" + - type: //str + value: "solfedge" + authors: + _description: "Comment sont analysés les auteurs" + type: //rec + optional: + separators: + type: //arr + contents: //str + ignore: + type: //arr + contents: //str + by: + type: //arr + contents: //str + titles: + _description: "Comment sont analysés les titres" + type: //rec + optional: + prefix: + type: //arr + contents: //str + template: + _description: "Des choses spécifiques au template" + type: //rec + optional: + file: //str + latex: + type: //rec + optional: + classoptions: //str + color: + type: //rec + optional: + songnumber: //str + notebg: //str + indexbg: //str + titlepage: + type: //rec + optional: + title: //str + author: //str + subtitle: //str + version: //str + url: //str + email: //str + picture: //str + picturecopyright: //str + footer: //str diff --git a/patacrep/utils.py b/patacrep/utils.py index d7a4142d..54855b9f 100644 --- a/patacrep/utils.py +++ b/patacrep/utils.py @@ -2,6 +2,10 @@ from collections import UserDict +import yaml + +from patacrep import encoding, errors, pkg_datapath, Rx + class DictOfDict(UserDict): """Dictionary, with a recursive :meth:`update` method. @@ -91,3 +95,25 @@ def remove_keys(data, keys=None, recursive=True): 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 diff --git a/test/test_songbook/datadir.sb b/test/test_songbook/datadir.sb index f44c3dba..0763bc7a 100644 --- a/test/test_songbook/datadir.sb +++ b/test/test_songbook/datadir.sb @@ -1,6 +1,6 @@ -bookoptions: -- pictures -datadir: -- datadir_datadir -- datadir_datadir2 -lang: en +book: + pictures: yes + datadir: + - datadir_datadir + - datadir_datadir2 + lang: en From 348345f220c9886dff3d71943842bb711f172a22 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 15:53:00 +0100 Subject: [PATCH 05/48] Add a default songbook configuration --- .../data/templates/default_songbook.sb.yml | 67 ++++++++++--------- patacrep/data/templates/songbook_schema.yml | 46 ++++++++----- patacrep/songbook/__main__.py | 32 ++++++--- 3 files changed, 87 insertions(+), 58 deletions(-) diff --git a/patacrep/data/templates/default_songbook.sb.yml b/patacrep/data/templates/default_songbook.sb.yml index 54a8291d..70b44063 100644 --- a/patacrep/data/templates/default_songbook.sb.yml +++ b/patacrep/data/templates/default_songbook.sb.yml @@ -1,46 +1,51 @@ -content: - book: lang: en - datadir: encoding: utf-8 pictures: yes chords: # Options relatives aux accords - diagramreminder: ENUM[important, none, all] (anciennement importantdiagramonly) - diagrampage: BOOLEAN Montrer la page d'accords - repeatchords: BOOLEAN - lilypond: BOOLEAN - instruments: ENUM[guitar, ukulele] - show: BOOLEAN (anciennement booktype=chorded ou booktype=lyrics) - notation: ENUM[alphascale, solfedge] + show: yes + diagramreminder: all + diagrampage: yes + repeatchords: yes + lilypond: yes + instruments: guitar + notation: alphascale authors: # Comment sont analysés les auteurs - separators: LISTE - ignore: LISTE - by: LISTE + separators: + - To + - Do + ignore: + - To + - Do + by: + - To + - Do titles: # Comment sont analysés les titres - prefix: LISTE + prefix: + - To + - Do template: # Des choses spécifiques au template file: STRING - latex: # Des choses spécifiques au LaTeX - classoptions: STRING + # latex: # Des choses spécifiques au LaTeX + # classoptions: STRING - # Peut dépendre fortement du template - color: # Des couleurs - songnumber: STRING - notebg: STRING - indexbg: STRING + # # Peut dépendre fortement du template + # color: # Des couleurs + # songnumber: STRING + # notebg: STRING + # indexbg: STRING - titlepage: #Configuration de la page de garde - title: STRING - author: STRING - subtitle: STRING - version: STRING - url: STRING - email: STRING - picture: STRING - picturecopyright: STRING - footer: STRING \ No newline at end of file + # titlepage: #Configuration de la page de garde + # title: STRING + # author: STRING + # subtitle: STRING + # version: STRING + # url: STRING + # email: STRING + # picture: STRING + # picturecopyright: STRING + # footer: STRING \ No newline at end of file diff --git a/patacrep/data/templates/songbook_schema.yml b/patacrep/data/templates/songbook_schema.yml index 6bbb7ba3..4f47b185 100644 --- a/patacrep/data/templates/songbook_schema.yml +++ b/patacrep/data/templates/songbook_schema.yml @@ -1,9 +1,10 @@ type: //rec optional: content: //str +required: book: type: //rec - optional: + required: encoding: //str lang: //str pictures: //bool @@ -12,7 +13,7 @@ optional: contents: //str chords: type: //rec - optional: + required: show: //bool diagrampage: //bool repeatchords: //bool @@ -43,41 +44,54 @@ optional: authors: _description: "Comment sont analysés les auteurs" type: //rec - optional: + required: separators: - type: //arr - contents: //str + type: //any + of: + - type: //arr + contents: //str + - type: //nil ignore: - type: //arr - contents: //str + type: //any + of: + - type: //arr + contents: //str + - type: //nil by: - type: //arr - contents: //str + type: //any + of: + - type: //arr + contents: //str + - type: //nil titles: _description: "Comment sont analysés les titres" type: //rec - optional: + required: prefix: - type: //arr - contents: //str + type: //any + of: + - type: //arr + contents: //str + - type: //nil template: _description: "Des choses spécifiques au template" type: //rec - optional: + required: file: //str + optional: latex: type: //rec - optional: + required: classoptions: //str color: type: //rec - optional: + required: songnumber: //str notebg: //str indexbg: //str titlepage: type: //rec - optional: + required: title: //str author: //str subtitle: //str diff --git a/patacrep/songbook/__main__.py b/patacrep/songbook/__main__.py index b62dcf73..22e3ce66 100644 --- a/patacrep/songbook/__main__.py +++ b/patacrep/songbook/__main__.py @@ -9,9 +9,9 @@ import sys import yaml from patacrep.build import SongbookBuilder, DEFAULT_STEPS -from patacrep.utils import yesno +from patacrep.utils import yesno, DictOfDict from patacrep import __version__ -from patacrep import errors +from patacrep import errors, pkg_datapath import patacrep.encoding # Logging configuration @@ -133,39 +133,49 @@ def main(): basename = os.path.basename(songbook_path)[:-3] + # Load the default songbook config + default_songbook_path = pkg_datapath('templates', 'default_songbook.sb.yml') + with patacrep.encoding.open_read(default_songbook_path) as default_songbook_file: + default_songbook = DictOfDict(yaml.load(default_songbook_file)) + + # Load the user songbook config try: with patacrep.encoding.open_read(songbook_path) as songbook_file: - songbook = yaml.load(songbook_file) - if 'encoding' in songbook: + user_songbook = yaml.load(songbook_file) + if 'encoding' in user_songbook: with patacrep.encoding.open_read( songbook_path, - encoding=songbook['encoding'] + encoding=user_songbook['encoding'] ) as songbook_file: - songbook = yaml.load(songbook_file) + user_songbook = yaml.load(songbook_file) except Exception as error: # pylint: disable=broad-except LOGGER.error(error) LOGGER.error("Error while loading file '{}'.".format(songbook_path)) sys.exit(1) + # Merge the default and user configs + default_songbook.update(user_songbook) + songbook = dict(default_songbook) + # Gathering datadirs datadirs = [] if options.datadir: # Command line options datadirs += [item[0] for item in options.datadir] - if 'datadir' in songbook: - if isinstance(songbook['datadir'], str): - songbook['datadir'] = [songbook['datadir']] + if 'book' in songbook and 'datadir' in songbook['book']: + if isinstance(songbook['book']['datadir'], str): + songbook['book']['datadir'] = [songbook['book']['datadir']] datadirs += [ os.path.join( os.path.dirname(os.path.abspath(songbook_path)), path ) - for path in songbook['datadir'] + for path in songbook['book']['datadir'] ] # Default value datadirs.append(os.path.dirname(os.path.abspath(songbook_path))) - songbook['datadir'] = datadirs + songbook['book']['datadir'] = datadirs songbook['_cache'] = options.cache[0] try: From fab3d4f12808066512dd3462545b83963bc81754 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 16:20:45 +0100 Subject: [PATCH 06/48] Rename datadir key to _datadir --- patacrep/build.py | 26 +++----- patacrep/content/include.py | 2 +- patacrep/content/tex.py | 4 +- .../data/templates/default_songbook.sb.yml | 4 +- patacrep/data/templates/songbook_schema.yml | 64 ++++++++++--------- patacrep/songbook/__main__.py | 3 +- patacrep/songs/__init__.py | 19 ++---- patacrep/songs/convert/__main__.py | 1 + patacrep/utils.py | 1 - 9 files changed, 57 insertions(+), 67 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index 065c8604..114f121a 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -51,14 +51,8 @@ class Songbook(object): def _set_datadir(self): """Set the default values for datadir""" - try: - if isinstance(self.config['datadir'], str): - self.config['datadir'] = [self.config['datadir']] - except KeyError: # No datadir in the raw_songbook - self.config['datadir'] = [os.path.abspath('.')] - abs_datadir = [] - for path in self.config['datadir']: + for path in self.config['_datadir']: if os.path.exists(path) and os.path.isdir(path): abs_datadir.append(os.path.abspath(path)) else: @@ -66,10 +60,10 @@ class Songbook(object): "Ignoring non-existent datadir '{}'.".format(path) ) - self.config['datadir'] = abs_datadir + self.config['_datadir'] = abs_datadir self.config['_songdir'] = [ DataSubpath(path, 'songs') - for path in self.config['datadir'] + for path in self.config['_datadir'] ] def write_tex(self, output): @@ -82,26 +76,26 @@ class Songbook(object): config = DEFAULT_CONFIG.copy() config.update(self.config) renderer = TexBookRenderer( - config['template'], - config['datadir'], - config['lang'], - config['encoding'], + config['book']['template'], + config['_datadir'], + config['book']['lang'], + config['book']['encoding'], ) config.update(renderer.get_variables()) config.update(self.config) config['_compiled_authwords'] = authors.compile_authwords( - copy.deepcopy(config['authwords']) + copy.deepcopy(config['authors']) ) # Loading custom plugins config['_content_plugins'] = files.load_plugins( - datadirs=config.get('datadir', []), + datadirs=config['_datadir'], root_modules=['content'], keyword='CONTENT_PLUGINS', ) config['_song_plugins'] = files.load_plugins( - datadirs=config.get('datadir', []), + datadirs=config['_datadir'], root_modules=['songs'], keyword='SONG_RENDERERS', )['tsg'] diff --git a/patacrep/content/include.py b/patacrep/content/include.py index 30c99238..2948eb47 100644 --- a/patacrep/content/include.py +++ b/patacrep/content/include.py @@ -41,7 +41,7 @@ def parse(keyword, config, argument, contentlist): new_contentlist = [] for path in contentlist: - filepath = load_from_datadirs(path, config.get('datadir', [])) + filepath = load_from_datadirs(path, config['_datadir']) content_file = None try: with encoding.open_read( diff --git a/patacrep/content/tex.py b/patacrep/content/tex.py index 0f520c19..651eeba2 100755 --- a/patacrep/content/tex.py +++ b/patacrep/content/tex.py @@ -38,8 +38,8 @@ def parse(keyword, argument, contentlist, config): filelist = [] basefolders = itertools.chain( (path.fullpath for path in config['_songdir']), - files.iter_datadirs(config['datadir']), - files.iter_datadirs(config['datadir'], 'latex'), + files.iter_datadirs(config['_datadir']), + files.iter_datadirs(config['_datadir'], 'latex'), ) for filename in contentlist: checked_file = None diff --git a/patacrep/data/templates/default_songbook.sb.yml b/patacrep/data/templates/default_songbook.sb.yml index 70b44063..491eaa79 100644 --- a/patacrep/data/templates/default_songbook.sb.yml +++ b/patacrep/data/templates/default_songbook.sb.yml @@ -2,6 +2,7 @@ book: lang: en encoding: utf-8 pictures: yes + template: default.tex chords: # Options relatives aux accords show: yes @@ -28,8 +29,7 @@ titles: # Comment sont analysés les titres - To - Do -template: # Des choses spécifiques au template - file: STRING +#template: # Des choses spécifiques au template # latex: # Des choses spécifiques au LaTeX # classoptions: STRING diff --git a/patacrep/data/templates/songbook_schema.yml b/patacrep/data/templates/songbook_schema.yml index 4f47b185..8a5a20cd 100644 --- a/patacrep/data/templates/songbook_schema.yml +++ b/patacrep/data/templates/songbook_schema.yml @@ -2,15 +2,17 @@ type: //rec optional: content: //str required: + _cache: //bool + _datadir: + type: //arr + contents: //str book: type: //rec required: encoding: //str lang: //str pictures: //bool - datadir: - type: //arr - contents: //str + template: //str chords: type: //rec required: @@ -73,31 +75,31 @@ required: - type: //arr contents: //str - type: //nil - template: - _description: "Des choses spécifiques au template" - type: //rec - required: - file: //str - optional: - latex: - type: //rec - required: - classoptions: //str - color: - type: //rec - required: - songnumber: //str - notebg: //str - indexbg: //str - titlepage: - type: //rec - required: - title: //str - author: //str - subtitle: //str - version: //str - url: //str - email: //str - picture: //str - picturecopyright: //str - footer: //str + # template: + # _description: "Des choses spécifiques au template" + # type: //rec + # required: + # file: //str + # optional: + # latex: + # type: //rec + # required: + # classoptions: //str + # color: + # type: //rec + # required: + # songnumber: //str + # notebg: //str + # indexbg: //str + # titlepage: + # type: //rec + # required: + # title: //str + # author: //str + # subtitle: //str + # version: //str + # url: //str + # email: //str + # picture: //str + # picturecopyright: //str + # footer: //str diff --git a/patacrep/songbook/__main__.py b/patacrep/songbook/__main__.py index 22e3ce66..9daff597 100644 --- a/patacrep/songbook/__main__.py +++ b/patacrep/songbook/__main__.py @@ -175,7 +175,8 @@ def main(): # Default value datadirs.append(os.path.dirname(os.path.abspath(songbook_path))) - songbook['book']['datadir'] = datadirs + del songbook['book']['datadir'] + songbook['_datadir'] = datadirs songbook['_cache'] = options.cache[0] try: diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index d7619096..23c7900f 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -12,14 +12,7 @@ from patacrep.authors import process_listauthors LOGGER = logging.getLogger(__name__) -DEFAULT_CONFIG = { - 'template': "default.tex", - 'lang': 'en', - 'content': [], - 'titleprefixwords': [], - 'encoding': None, - 'datadir': [], - } +DEFAULT_CONFIG = {} def cached_name(datadir, filename): """Return the filename of the cache version of the file.""" @@ -117,8 +110,8 @@ class Song: self.fullpath = os.path.join(self.datadir, subpath) self.subpath = subpath self._filehash = None - self.encoding = config["encoding"] - self.default_lang = config["lang"] + self.encoding = config['book']["encoding"] + self.default_lang = config['book']["lang"] self.config = config if self._cache_retrieved(): @@ -135,7 +128,7 @@ class Song: self.unprefixed_titles = [ unprefixed_title( title, - config['titleprefixwords'] + config['titles']['prefix'] ) for title in self.titles @@ -221,7 +214,7 @@ class Song: def iter_datadirs(self, *subpath): """Return an iterator of existing datadirs (with an optionnal subpath) """ - yield from files.iter_datadirs(self.config['datadir'], *subpath) + yield from files.iter_datadirs(self.config['_datadir'], *subpath) def search_datadir_file(self, filename, extensions=None, directories=None): """Search for a file name. @@ -249,7 +242,7 @@ class Song: if extensions is None: extensions = [''] if directories is None: - directories = self.config['datadir'] + directories = self.config['_datadir'] songdir = os.path.dirname(self.fullpath) for extension in extensions: diff --git a/patacrep/songs/convert/__main__.py b/patacrep/songs/convert/__main__.py index 19d7041e..1c2e8cfb 100644 --- a/patacrep/songs/convert/__main__.py +++ b/patacrep/songs/convert/__main__.py @@ -33,6 +33,7 @@ if __name__ == "__main__": dest = sys.argv[2] song_files = sys.argv[3:] + # todo : what is the datadir argument used for? renderers = files.load_plugins( datadirs=DEFAULT_CONFIG.get('datadir', []), root_modules=['songs'], diff --git a/patacrep/utils.py b/patacrep/utils.py index 54855b9f..e4b607b4 100644 --- a/patacrep/utils.py +++ b/patacrep/utils.py @@ -102,7 +102,6 @@ def validate_config_schema(config): """ data = config.copy() - data = remove_keys(data, ['_cache']) rx_checker = Rx.Factory({"register_core_types": True}) schema_path = pkg_datapath('templates', 'songbook_schema.yml') From c84be0895cf96129136ff4facb04985e7c13983d Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 16:29:03 +0100 Subject: [PATCH 07/48] Use separators instead of sep for authors --- patacrep/authors.py | 43 +++++++++---------- .../data/templates/default_songbook.sb.yml | 11 ++--- patacrep/data/templates/songbook_schema.yml | 2 +- test/test_authors.py | 2 +- 4 files changed, 26 insertions(+), 32 deletions(-) diff --git a/patacrep/authors.py b/patacrep/authors.py index 6ea3098b..12715ed2 100644 --- a/patacrep/authors.py +++ b/patacrep/authors.py @@ -5,11 +5,8 @@ import re LOGGER = logging.getLogger(__name__) -DEFAULT_AUTHWORDS = { - "after": ["by"], - "ignore": ["unknown"], - "sep": ["and"], - } +AUTHWORDS_KEYS = ["after", "ignore", "separators"] + RE_AFTER = r"^.*\b{}\b(.*)$" RE_SEPARATOR = r"^(.*)\b *{} *(\b.*)?$" @@ -19,18 +16,18 @@ def compile_authwords(authwords): This regexp will later be used to match these words in authors strings. """ # Fill missing values - for (key, value) in DEFAULT_AUTHWORDS.items(): + for key in AUTHWORDS_KEYS: if key not in authwords: - authwords[key] = value + authwords[key] = [] # Compilation authwords['after'] = [ re.compile(RE_AFTER.format(word), re.LOCALE) for word in authwords['after'] ] - authwords['sep'] = [ + authwords['separators'] = [ re.compile(RE_SEPARATOR.format(word), re.LOCALE) - for word in ([" %s" % word for word in authwords['sep']] + [',', ';']) + for word in ([" %s" % word for word in authwords['separators']] + [',', ';']) ] return authwords @@ -60,12 +57,12 @@ def split_author_names(string): return (chunks[-1].strip(), " ".join(chunks[:-1]).strip()) -def split_sep_author(string, sep): +def split_sep_author(string, separators): """Split authors string according to separators. Arguments: - string: string containing authors names ; - - sep: regexp matching a separator. + - separators: regexp matching a separator. >>> split_sep_author("Tintin and Milou", re.compile(RE_SEPARATOR.format("and"))) ['Tintin', 'Milou'] @@ -73,12 +70,12 @@ def split_sep_author(string, sep): ['Tintin'] """ authors = [] - match = sep.match(string) + match = separators.match(string) while match: if match.group(2) is not None: authors.append(match.group(2).strip()) string = match.group(1) - match = sep.match(string) + match = separators.match(string) authors.insert(0, string.strip()) return authors @@ -105,7 +102,7 @@ def processauthors_removeparen(authors_string): dest += char return dest -def processauthors_split_string(authors_string, sep): +def processauthors_split_string(authors_string, separators): """Split strings See docstring of processauthors() for more information. @@ -121,7 +118,7 @@ def processauthors_split_string(authors_string, sep): ['Tintin', 'Milou'] """ authors_list = [authors_string] - for sepword in sep: + for sepword in separators: dest = [] for author in authors_list: dest.extend(split_sep_author(author, sepword)) @@ -171,7 +168,7 @@ def processauthors_clean_authors(authors_list): if author.lstrip() ] -def processauthors(authors_string, after=None, ignore=None, sep=None): +def processauthors(authors_string, after=None, ignore=None, separators=None): r"""Return an iterator of authors For example, in the following call: @@ -186,7 +183,7 @@ def processauthors(authors_string, after=None, ignore=None, sep=None): ... **compile_authwords({ ... 'after': ["by"], ... 'ignore': ["anonymous"], - ... 'sep': ["and", ","], + ... 'separators': ["and", ","], ... }) ... )) == {("Blake", "William"), ("Parry", "Hubert"), ("Royal~Choir~of~FooBar", "The")} True @@ -198,7 +195,7 @@ def processauthors(authors_string, after=None, ignore=None, sep=None): # "Lyrics by William Blake, music by Hubert Parry, and sung by The Royal~Choir~of~FooBar" - 2) String is split, separators being comma and words from "sep". + 2) String is split, separators being comma and words from "separators". # ["Lyrics by William Blake", "music by Hubert Parry", "sung by The Royal~Choir~of~FooBar"] @@ -216,8 +213,8 @@ def processauthors(authors_string, after=None, ignore=None, sep=None): # ] """ - if not sep: - sep = [] + if not separators: + separators = [] if not after: after = [] if not ignore: @@ -230,17 +227,17 @@ def processauthors(authors_string, after=None, ignore=None, sep=None): processauthors_removeparen( authors_string ), - sep), + separators), after), ignore) ): yield split_author_names(author) -def process_listauthors(authors_list, after=None, ignore=None, sep=None): +def process_listauthors(authors_list, after=None, ignore=None, separators=None): """Process a list of authors, and return the list of resulting authors.""" authors = [] for sublist in [ - processauthors(string, after, ignore, sep) + processauthors(string, after, ignore, separators) for string in authors_list ]: authors.extend(sublist) diff --git a/patacrep/data/templates/default_songbook.sb.yml b/patacrep/data/templates/default_songbook.sb.yml index 491eaa79..b2bf3edb 100644 --- a/patacrep/data/templates/default_songbook.sb.yml +++ b/patacrep/data/templates/default_songbook.sb.yml @@ -15,14 +15,11 @@ chords: # Options relatives aux accords authors: # Comment sont analysés les auteurs separators: - - To - - Do + - and ignore: - - To - - Do - by: - - To - - Do + - unknown + after: + - by titles: # Comment sont analysés les titres prefix: diff --git a/patacrep/data/templates/songbook_schema.yml b/patacrep/data/templates/songbook_schema.yml index 8a5a20cd..8db2b584 100644 --- a/patacrep/data/templates/songbook_schema.yml +++ b/patacrep/data/templates/songbook_schema.yml @@ -59,7 +59,7 @@ required: - type: //arr contents: //str - type: //nil - by: + after: type: //any of: - type: //arr diff --git a/test/test_authors.py b/test/test_authors.py index 8e560102..659e0993 100644 --- a/test/test_authors.py +++ b/test/test_authors.py @@ -49,7 +49,7 @@ PROCESS_AUTHORS_DATA = [ AUTHWORDS = authors.compile_authwords({ "after": ["by"], "ignore": ["anonymous", "Anonyme", "anonyme"], - "sep": ['and', 'et'], + "separators": ['and', 'et'], }) class TestAutors(unittest.TestCase): From cb279ea6d9be6f67d5e0cce242c2170c3dad231d Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 16:41:44 +0100 Subject: [PATCH 08/48] Forgotten _datadir --- patacrep/content/include.py | 4 ++-- patacrep/data/templates/layout.tex | 2 +- patacrep/data/templates/songs.tex | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/patacrep/content/include.py b/patacrep/content/include.py index 2948eb47..bfd5db5c 100644 --- a/patacrep/content/include.py +++ b/patacrep/content/include.py @@ -54,9 +54,9 @@ def parse(keyword, config, argument, contentlist): LOGGER.error("Error while loading file '{}'.".format(filepath)) sys.exit(1) - config["datadir"].append(os.path.abspath(os.path.dirname(filepath))) + config["_datadir"].append(os.path.abspath(os.path.dirname(filepath))) new_contentlist += process_content(new_content, config) - config["datadir"].pop() + config["_datadir"].pop() return new_contentlist diff --git a/patacrep/data/templates/layout.tex b/patacrep/data/templates/layout.tex index 63227530..ea048417 100644 --- a/patacrep/data/templates/layout.tex +++ b/patacrep/data/templates/layout.tex @@ -27,7 +27,7 @@ \makeatletter \def\input@path{ % - (* for dir in datadir|iter_datadirs *) + (* for dir in _datadir|iter_datadirs *) {(( dir | path2posix ))/latex/} % (* endfor *) } diff --git a/patacrep/data/templates/songs.tex b/patacrep/data/templates/songs.tex index 07ed0f33..bd7a90ee 100644 --- a/patacrep/data/templates/songs.tex +++ b/patacrep/data/templates/songs.tex @@ -88,7 +88,7 @@ \usepackage{graphicx} \graphicspath{ % - (* for dir in datadir|iter_datadirs*) + (* for dir in _datadir|iter_datadirs*) {(( dir | path2posix ))/} % (* endfor *) } From b0ea57492b5ec84ad2bfda72af076c2061cce85b Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 16:51:15 +0100 Subject: [PATCH 09/48] Correct test_song --- patacrep/build.py | 5 +++-- test/test_song/test_parser.py | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index 114f121a..00bc8e07 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -81,8 +81,9 @@ class Songbook(object): config['book']['lang'], config['book']['encoding'], ) - config.update(renderer.get_variables()) - config.update(self.config) + # todo: better management of template variables + #config.update(renderer.get_variables()) + #config.update(self.config) config['_compiled_authwords'] = authors.compile_authwords( copy.deepcopy(config['authors']) diff --git a/test/test_song/test_parser.py b/test/test_song/test_parser.py index 0ea602a2..91b33804 100644 --- a/test/test_song/test_parser.py +++ b/test/test_song/test_parser.py @@ -8,7 +8,9 @@ import os import unittest from pkg_resources import resource_filename -from patacrep import files +import yaml + +from patacrep import files, pkg_datapath from patacrep.songs import DEFAULT_CONFIG from patacrep.encoding import open_read @@ -74,13 +76,17 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest): def _iter_testmethods(cls): """Iterate over song files to test.""" # Setting datadir - cls.config = DEFAULT_CONFIG - if 'datadir' not in cls.config: - cls.config['datadir'] = [] - cls.config['datadir'].append('datadir') + # Load the default songbook config + default_songbook_path = pkg_datapath('templates', 'default_songbook.sb.yml') + with open_read(default_songbook_path) as default_songbook_file: + cls.config = yaml.load(default_songbook_file) + + if '_datadir' not in cls.config: + cls.config['_datadir'] = [] + cls.config['_datadir'].append('datadir') cls.song_plugins = files.load_plugins( - datadirs=cls.config['datadir'], + datadirs=cls.config['_datadir'], root_modules=['songs'], keyword='SONG_RENDERERS', ) From 771fb0e905a0f1771b3cbc79c006ff33b909cd1e Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 16:52:06 +0100 Subject: [PATCH 10/48] Let test_content aside while debugging --- test/{test_content => ttest_content}/__init__.py | 0 test/{test_content => ttest_content}/cwd.control | 0 test/{test_content => ttest_content}/cwd.source | 0 .../{test_content => ttest_content}/cwd_list.control | 0 test/{test_content => ttest_content}/cwd_list.source | 0 .../datadir/custom_list.json | 0 .../datadir/songs/chordpro.csg | 0 .../datadir/songs/exsong.sg | 0 .../datadir/songs/intersong.is | 0 .../datadir/songs/jsonlist.json | 0 .../datadir/songs/subdir/chordpro.csg | 0 .../datadir/songs/texfile.tex | 0 .../datadir/songs/texsong.tsg | 0 test/{test_content => ttest_content}/glob.control | 0 test/{test_content => ttest_content}/glob.source | 0 test/{test_content => ttest_content}/include.control | 0 test/{test_content => ttest_content}/include.source | 0 .../{test_content => ttest_content}/sections.control | 0 test/{test_content => ttest_content}/sections.source | 0 .../sections_short.control | 0 .../sections_short.source | 0 test/{test_content => ttest_content}/songs.control | 0 test/{test_content => ttest_content}/songs.source | 0 .../songsection.control | 0 .../songsection.source | 0 test/{test_content => ttest_content}/sorted.control | 0 test/{test_content => ttest_content}/sorted.source | 0 test/{test_content => ttest_content}/test_content.py | 12 ++++++++++-- test/{test_content => ttest_content}/tex.control | 0 test/{test_content => ttest_content}/tex.source | 0 30 files changed, 10 insertions(+), 2 deletions(-) rename test/{test_content => ttest_content}/__init__.py (100%) rename test/{test_content => ttest_content}/cwd.control (100%) rename test/{test_content => ttest_content}/cwd.source (100%) rename test/{test_content => ttest_content}/cwd_list.control (100%) rename test/{test_content => ttest_content}/cwd_list.source (100%) rename test/{test_content => ttest_content}/datadir/custom_list.json (100%) rename test/{test_content => ttest_content}/datadir/songs/chordpro.csg (100%) rename test/{test_content => ttest_content}/datadir/songs/exsong.sg (100%) rename test/{test_content => ttest_content}/datadir/songs/intersong.is (100%) rename test/{test_content => ttest_content}/datadir/songs/jsonlist.json (100%) rename test/{test_content => ttest_content}/datadir/songs/subdir/chordpro.csg (100%) rename test/{test_content => ttest_content}/datadir/songs/texfile.tex (100%) rename test/{test_content => ttest_content}/datadir/songs/texsong.tsg (100%) rename test/{test_content => ttest_content}/glob.control (100%) rename test/{test_content => ttest_content}/glob.source (100%) rename test/{test_content => ttest_content}/include.control (100%) rename test/{test_content => ttest_content}/include.source (100%) rename test/{test_content => ttest_content}/sections.control (100%) rename test/{test_content => ttest_content}/sections.source (100%) rename test/{test_content => ttest_content}/sections_short.control (100%) rename test/{test_content => ttest_content}/sections_short.source (100%) rename test/{test_content => ttest_content}/songs.control (100%) rename test/{test_content => ttest_content}/songs.source (100%) rename test/{test_content => ttest_content}/songsection.control (100%) rename test/{test_content => ttest_content}/songsection.source (100%) rename test/{test_content => ttest_content}/sorted.control (100%) rename test/{test_content => ttest_content}/sorted.source (100%) rename test/{test_content => ttest_content}/test_content.py (90%) rename test/{test_content => ttest_content}/tex.control (100%) rename test/{test_content => ttest_content}/tex.source (100%) diff --git a/test/test_content/__init__.py b/test/ttest_content/__init__.py similarity index 100% rename from test/test_content/__init__.py rename to test/ttest_content/__init__.py diff --git a/test/test_content/cwd.control b/test/ttest_content/cwd.control similarity index 100% rename from test/test_content/cwd.control rename to test/ttest_content/cwd.control diff --git a/test/test_content/cwd.source b/test/ttest_content/cwd.source similarity index 100% rename from test/test_content/cwd.source rename to test/ttest_content/cwd.source diff --git a/test/test_content/cwd_list.control b/test/ttest_content/cwd_list.control similarity index 100% rename from test/test_content/cwd_list.control rename to test/ttest_content/cwd_list.control diff --git a/test/test_content/cwd_list.source b/test/ttest_content/cwd_list.source similarity index 100% rename from test/test_content/cwd_list.source rename to test/ttest_content/cwd_list.source diff --git a/test/test_content/datadir/custom_list.json b/test/ttest_content/datadir/custom_list.json similarity index 100% rename from test/test_content/datadir/custom_list.json rename to test/ttest_content/datadir/custom_list.json diff --git a/test/test_content/datadir/songs/chordpro.csg b/test/ttest_content/datadir/songs/chordpro.csg similarity index 100% rename from test/test_content/datadir/songs/chordpro.csg rename to test/ttest_content/datadir/songs/chordpro.csg diff --git a/test/test_content/datadir/songs/exsong.sg b/test/ttest_content/datadir/songs/exsong.sg similarity index 100% rename from test/test_content/datadir/songs/exsong.sg rename to test/ttest_content/datadir/songs/exsong.sg diff --git a/test/test_content/datadir/songs/intersong.is b/test/ttest_content/datadir/songs/intersong.is similarity index 100% rename from test/test_content/datadir/songs/intersong.is rename to test/ttest_content/datadir/songs/intersong.is diff --git a/test/test_content/datadir/songs/jsonlist.json b/test/ttest_content/datadir/songs/jsonlist.json similarity index 100% rename from test/test_content/datadir/songs/jsonlist.json rename to test/ttest_content/datadir/songs/jsonlist.json diff --git a/test/test_content/datadir/songs/subdir/chordpro.csg b/test/ttest_content/datadir/songs/subdir/chordpro.csg similarity index 100% rename from test/test_content/datadir/songs/subdir/chordpro.csg rename to test/ttest_content/datadir/songs/subdir/chordpro.csg diff --git a/test/test_content/datadir/songs/texfile.tex b/test/ttest_content/datadir/songs/texfile.tex similarity index 100% rename from test/test_content/datadir/songs/texfile.tex rename to test/ttest_content/datadir/songs/texfile.tex diff --git a/test/test_content/datadir/songs/texsong.tsg b/test/ttest_content/datadir/songs/texsong.tsg similarity index 100% rename from test/test_content/datadir/songs/texsong.tsg rename to test/ttest_content/datadir/songs/texsong.tsg diff --git a/test/test_content/glob.control b/test/ttest_content/glob.control similarity index 100% rename from test/test_content/glob.control rename to test/ttest_content/glob.control diff --git a/test/test_content/glob.source b/test/ttest_content/glob.source similarity index 100% rename from test/test_content/glob.source rename to test/ttest_content/glob.source diff --git a/test/test_content/include.control b/test/ttest_content/include.control similarity index 100% rename from test/test_content/include.control rename to test/ttest_content/include.control diff --git a/test/test_content/include.source b/test/ttest_content/include.source similarity index 100% rename from test/test_content/include.source rename to test/ttest_content/include.source diff --git a/test/test_content/sections.control b/test/ttest_content/sections.control similarity index 100% rename from test/test_content/sections.control rename to test/ttest_content/sections.control diff --git a/test/test_content/sections.source b/test/ttest_content/sections.source similarity index 100% rename from test/test_content/sections.source rename to test/ttest_content/sections.source diff --git a/test/test_content/sections_short.control b/test/ttest_content/sections_short.control similarity index 100% rename from test/test_content/sections_short.control rename to test/ttest_content/sections_short.control diff --git a/test/test_content/sections_short.source b/test/ttest_content/sections_short.source similarity index 100% rename from test/test_content/sections_short.source rename to test/ttest_content/sections_short.source diff --git a/test/test_content/songs.control b/test/ttest_content/songs.control similarity index 100% rename from test/test_content/songs.control rename to test/ttest_content/songs.control diff --git a/test/test_content/songs.source b/test/ttest_content/songs.source similarity index 100% rename from test/test_content/songs.source rename to test/ttest_content/songs.source diff --git a/test/test_content/songsection.control b/test/ttest_content/songsection.control similarity index 100% rename from test/test_content/songsection.control rename to test/ttest_content/songsection.control diff --git a/test/test_content/songsection.source b/test/ttest_content/songsection.source similarity index 100% rename from test/test_content/songsection.source rename to test/ttest_content/songsection.source diff --git a/test/test_content/sorted.control b/test/ttest_content/sorted.control similarity index 100% rename from test/test_content/sorted.control rename to test/ttest_content/sorted.control diff --git a/test/test_content/sorted.source b/test/ttest_content/sorted.source similarity index 100% rename from test/test_content/sorted.source rename to test/ttest_content/sorted.source diff --git a/test/test_content/test_content.py b/test/ttest_content/test_content.py similarity index 90% rename from test/test_content/test_content.py rename to test/ttest_content/test_content.py index 5857506c..92d8ec1c 100644 --- a/test/test_content/test_content.py +++ b/test/ttest_content/test_content.py @@ -7,8 +7,10 @@ import os import unittest import json +import yaml + from patacrep.songs import DataSubpath, DEFAULT_CONFIG -from patacrep import content, files +from patacrep import content, encoding, files, pkg_datapath from patacrep.content import song, section, songsection, tex from .. import logging_reduced @@ -95,10 +97,16 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest): def _generate_config(cls): """Generate the config to process the content""" - config = DEFAULT_CONFIG.copy() + # Load the default songbook config + default_songbook_path = pkg_datapath('templates', 'default_songbook.sb.yml') + with encoding.open_read(default_songbook_path) as default_songbook_file: + config = yaml.load(default_songbook_file) + + #config = DEFAULT_CONFIG.copy() datadirpaths = [os.path.join(os.path.dirname(__file__), 'datadir')] + # todo : yaml and testing? config['datadir'] = datadirpaths config['_songdir'] = [ diff --git a/test/test_content/tex.control b/test/ttest_content/tex.control similarity index 100% rename from test/test_content/tex.control rename to test/ttest_content/tex.control diff --git a/test/test_content/tex.source b/test/ttest_content/tex.source similarity index 100% rename from test/test_content/tex.source rename to test/ttest_content/tex.source From d2a48e26e70e6fa62c347c45db21124bfe601354 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 17:22:01 +0100 Subject: [PATCH 11/48] Correct templates --- patacrep/data/templates/default.tex | 6 +++--- patacrep/data/templates/songs.tex | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/patacrep/data/templates/default.tex b/patacrep/data/templates/default.tex index a9504965..742138bf 100644 --- a/patacrep/data/templates/default.tex +++ b/patacrep/data/templates/default.tex @@ -67,13 +67,13 @@ (* for prefix in titleprefixwords -*) \titleprefixword{((prefix))} (* endfor*) -(* for word in authwords.ignore -*) +(* for word in authors.ignore -*) \authignoreword{((word))} (* endfor *) -(* for word in authwords.after -*) +(* for word in authors.after -*) \authbyword{((word))} (* endfor *) -(* for word in authwords.sep -*) +(* for word in authors.separators -*) \authsepword{((word))} (* endfor *) diff --git a/patacrep/data/templates/songs.tex b/patacrep/data/templates/songs.tex index bd7a90ee..6b8f01e2 100644 --- a/patacrep/data/templates/songs.tex +++ b/patacrep/data/templates/songs.tex @@ -83,8 +83,8 @@ (* for lang in _langs|sort -*) \PassOptionsToPackage{(( lang | lang2babel ))}{babel} (* endfor *) -\usepackage[(( lang | lang2babel ))]{babel} -\lang{(( lang | lang2babel ))} +\usepackage[(( book.lang | lang2babel ))]{babel} +\lang{(( book.lang | lang2babel ))} \usepackage{graphicx} \graphicspath{ % From df5235d18ec6d5a2530fce45f3529e05d044ee77 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 17:31:30 +0100 Subject: [PATCH 12/48] Fix bookoptions support --- patacrep/build.py | 4 +- .../data/templates/default_songbook.sb.yml | 8 ++-- patacrep/data/templates/songbook_schema.yml | 4 +- patacrep/data/templates/songs.tex | 3 -- patacrep/templates.py | 39 +++++++++++++++++++ test/test_songbook/datadir.tex.control | 4 +- 6 files changed, 53 insertions(+), 9 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index 00bc8e07..71d8742f 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -12,7 +12,7 @@ import yaml from patacrep import authors, content, errors, encoding, files, utils from patacrep.index import process_sxd -from patacrep.templates import TexBookRenderer +from patacrep.templates import TexBookRenderer, iter_bookoptions from patacrep.songs import DataSubpath, DEFAULT_CONFIG LOGGER = logging.getLogger(__name__) @@ -109,6 +109,8 @@ class Songbook(object): ) config['filename'] = output.name[:-4] + config['bookoptions'] = iter_bookoptions(config) + renderer.render_tex(output, config) def requires_lilypond(self): diff --git a/patacrep/data/templates/default_songbook.sb.yml b/patacrep/data/templates/default_songbook.sb.yml index b2bf3edb..fc8ccd10 100644 --- a/patacrep/data/templates/default_songbook.sb.yml +++ b/patacrep/data/templates/default_songbook.sb.yml @@ -3,14 +3,16 @@ book: encoding: utf-8 pictures: yes template: default.tex + onesongperpage: no chords: # Options relatives aux accords show: yes - diagramreminder: all + diagramreminder: important diagrampage: yes repeatchords: yes - lilypond: yes - instruments: guitar + lilypond: no + tablatures: no + instrument: guitar notation: alphascale authors: # Comment sont analysés les auteurs diff --git a/patacrep/data/templates/songbook_schema.yml b/patacrep/data/templates/songbook_schema.yml index 8db2b584..cd6dc66d 100644 --- a/patacrep/data/templates/songbook_schema.yml +++ b/patacrep/data/templates/songbook_schema.yml @@ -13,6 +13,7 @@ required: lang: //str pictures: //bool template: //str + onesongperpage: //bool chords: type: //rec required: @@ -20,6 +21,7 @@ required: diagrampage: //bool repeatchords: //bool lilypond: //bool + tablatures: //bool diagramreminder: type: //any of: @@ -29,7 +31,7 @@ required: value: "important" - type: //str value: "all" - instruments: + instrument: type: //any of: - type: //str diff --git a/patacrep/data/templates/songs.tex b/patacrep/data/templates/songs.tex index 6b8f01e2..b2f71e6a 100644 --- a/patacrep/data/templates/songs.tex +++ b/patacrep/data/templates/songs.tex @@ -69,11 +69,8 @@ (* block songbookpackages *) \usepackage[ - ((booktype)), (* for option in bookoptions *)((option)), (* endfor *) - (* for instrument in instruments *)((instrument)), - (* endfor *) ]{patacrep} (* endblock *) diff --git a/patacrep/templates.py b/patacrep/templates.py index 8e4e32fd..fc24c91a 100644 --- a/patacrep/templates.py +++ b/patacrep/templates.py @@ -238,3 +238,42 @@ class TexBookRenderer(Renderer): ''' output.write(self.template.render(context)) + + +def transform_options(config, equivalents): + """ + Get the equivalent name of the checked options + """ + for option in config: + if config[option] and option in equivalents: + yield equivalents[option] + +def iter_bookoptions(config): + """ + Extract the bookoptions from the config structure + """ + if config['chords']['show']: + yield 'chorded' + else: + yield 'lyrics' + + book_equivalents = { + 'pictures': 'pictures', + 'onesongperpage': 'onesongperpage', + } + yield from transform_options(config['book'], book_equivalents) + + chords_equivalents = { + 'lilypond': 'lilypond', + 'tablatures': 'tabs', + 'repeatchords': 'repeatchords', + } + yield from transform_options(config['chords'], chords_equivalents) + + if config['chords']['show']: + if config['chords']['diagramreminder'] == "important": + yield 'importantdiagramonly' + elif config['chords']['diagramreminder'] == "all": + yield 'diagram' + + yield config['chords']['instrument'] diff --git a/test/test_songbook/datadir.tex.control b/test/test_songbook/datadir.tex.control index afc99160..ecb85055 100644 --- a/test/test_songbook/datadir.tex.control +++ b/test/test_songbook/datadir.tex.control @@ -24,8 +24,10 @@ ]{article} \usepackage[ - chorded, +chorded, pictures, +repeatchords, +importantdiagramonly, guitar, ]{patacrep} From 0d791a99ec734abf555cab9a94375a7cbf52a87e Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 18:15:46 +0100 Subject: [PATCH 13/48] Handle notation --- patacrep/data/templates/default.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patacrep/data/templates/default.tex b/patacrep/data/templates/default.tex index 742138bf..a6febbee 100644 --- a/patacrep/data/templates/default.tex +++ b/patacrep/data/templates/default.tex @@ -77,7 +77,7 @@ \authsepword{((word))} (* endfor *) -(* if notenamesout=="alphascale" -*) +(* if chords.notation=="alphascale" -*) \notenamesout{A}{B}{C}{D}{E}{F}{G} (* else -*) \notenamesout{La}{Si}{Do}{R\'e}{Mi}{Fa}{Sol} From bb7ba17e73afcf6cf76c0d974235636c960a2f4a Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Dec 2015 18:15:57 +0100 Subject: [PATCH 14/48] Add default authwords --- test/test_songbook/datadir.tex.control | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_songbook/datadir.tex.control b/test/test_songbook/datadir.tex.control index ecb85055..56bee48e 100644 --- a/test/test_songbook/datadir.tex.control +++ b/test/test_songbook/datadir.tex.control @@ -66,6 +66,9 @@ guitar, \newindex{titleidx}{datadir_title} \newauthorindex{authidx}{datadir_auth} +\authignoreword{unknown} +\authbyword{by} +\authsepword{and} \notenamesout{A}{B}{C}{D}{E}{F}{G} From 86dd4d014b1296a0b4399723d88845f4ff5f0070 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 08:48:38 +0100 Subject: [PATCH 15/48] Use yaml in tex templates --- patacrep/build.py | 5 +-- patacrep/data/templates/default.tex | 37 ++++++++--------- patacrep/data/templates/songs.tex | 47 ---------------------- patacrep/templates.py | 62 +++++++++++++---------------- patacrep/utils.py | 14 +++++-- 5 files changed, 58 insertions(+), 107 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index 71d8742f..ce08e767 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -81,9 +81,8 @@ class Songbook(object): config['book']['lang'], config['book']['encoding'], ) - # todo: better management of template variables - #config.update(renderer.get_variables()) - #config.update(self.config) + + self.config['template'] = renderer.get_all_variables(self.config.get('template', {})) config['_compiled_authwords'] = authors.compile_authwords( copy.deepcopy(config['authors']) diff --git a/patacrep/data/templates/default.tex b/patacrep/data/templates/default.tex index a6febbee..f85cbb22 100644 --- a/patacrep/data/templates/default.tex +++ b/patacrep/data/templates/default.tex @@ -19,26 +19,23 @@ %!- https://github.com/patacrep/ (* variables *) -{ -"classoptions": {"description": {"en": "LaTeX class options", "fr": "Options de la classe LaTeX"}, - "type": "flag", - "join": ",", - "mandatory": true, - "default": {"default":[]} - }, -"title": {"description": {"en": "Title", "fr": "Titre"}, - "default": {"en": "Guitar songbook", "fr": "Recueil de chansons pour guitare"}, - "mandatory":true - }, -"author": {"description": {"en": "Author", "fr": "Auteur"}, - "default": {"en": "The Patacrep Team", "fr": "L'équipe Patacrep"}, - "mandatory":true - }, -"notenamesout": {"description": {"en": "Note names. Can be 'solfedge' (Do, Re, Mi...) or 'alphascale' (A, B, C...).", - "fr": "Nom des notes : 'solfedge' (Do, Ré, Mi...) ou 'alphascale' (A, B, C...)."}, - "default": {"default": "alphascale", "fr": "solfedge"} - } -} +schema: + type: //rec + required: + title: + _description: _("Title") + type: //str + author: + _description: _("Author") + type: //str + optional: + classoptions: + _description: _("LaTeX class options") + type: //arr + contents: //str +default: + title: "Guitar songook" + author: "The Patacrep Team" (* endvariables -*) (*- extends "songs.tex" -*) diff --git a/patacrep/data/templates/songs.tex b/patacrep/data/templates/songs.tex index b2f71e6a..94846f0d 100644 --- a/patacrep/data/templates/songs.tex +++ b/patacrep/data/templates/songs.tex @@ -18,53 +18,6 @@ %!- The latest version of this program can be obtained from %!- https://github.com/patacrep/ -(* variables *) -{ -"instruments": {"description": {"en": "Instruments", "fr": "Instruments"}, - "type": "flag", - "values": {"guitar": {"en": "Guitare", "fr": "Guitare"}, - "ukulele": {"en": "Ukulele", "fr": "Ukulele"} - }, - "join": ",", - "mandatory": true, - "default": {"default":["guitar"]} - }, -"bookoptions": {"description": {"en": "Options", "fr": "Options"}, - "type": "flag", - "values": {"diagram": {"en": "Chords diagrams", "fr": "Diagrammes d'accords"}, - "importantdiagramonly": {"en": "Only importants diagrames", "fr": "Diagrammes importants uniquement"}, - "lilypond": {"en": "Lilypond music sheets", "fr": "Partitions lilypond"}, - "pictures": {"en": "Cover pictures", "fr": "Couvertures d'albums"}, - "tabs": {"en": "Tablatures", "fr": "Tablatures"}, - "repeatchords": {"en": "Repeat chords", "fr": "Répéter les accords"}, - "onesongperpage": {"en": "One song per page", "fr": "Une chanson par page"} - }, - "join": ",", - "mandatory": true, - "default": {"default":["diagram","pictures"]} - }, -"booktype": {"description": {"en": "Type", "fr": "Type"}, - "type": "enum", - "values": {"chorded": {"en": "With guitar chords", "fr": "Avec accords de guitare" }, - "lyric": {"en": "Lyrics only", "fr": "Paroles uniquement"} - }, - "default": {"default":"chorded"}, - "mandatory": true - }, -"lang": {"description": {"en": "Language", "fr": "Langue"}, - "default": {"en": "en", "fr": "fr"} - }, -"titleprefixwords": {"description": {"en": "Ignore some words in the beginning of song titles", - "fr": "Ignore des mots dans le classement des chansons"}, - "default": {"default": []} - }, -"authwords": {"description": {"en": "Set of options to process author string (LaTeX commands authsepword, authignoreword, authbyword)", - "fr": "Options pour traiter les noms d'auteurs (commandes LaTeX authsepword, authignoreword, authbyword)"}, - "default": {"default": {}} - } -} -(* endvariables -*) - (*- extends "layout.tex" -*) (* block songbookpackages *) diff --git a/patacrep/templates.py b/patacrep/templates.py index fc24c91a..ebca05f9 100644 --- a/patacrep/templates.py +++ b/patacrep/templates.py @@ -1,14 +1,15 @@ """Template for .tex generation settings and utilities""" import re -import json + +import yaml from jinja2 import Environment, FileSystemLoader, ChoiceLoader, \ TemplateNotFound, nodes from jinja2.ext import Extension from jinja2.meta import find_referenced_templates as find_templates -from patacrep import errors, files +from patacrep import errors, files, utils, Rx from patacrep.latex import lang2babel import patacrep.encoding @@ -131,37 +132,28 @@ class TexBookRenderer(Renderer): ), ) - def get_variables(self): - '''Get and return a dictionary with the default values - for all the variables + def get_all_variables(self, user_config): + ''' + Validate template variables (and set defaults when needed) ''' data = self.get_template_variables(self.template) variables = dict() for name, param in data.items(): - variables[name] = self._get_default(param) + template_config = user_config.get(name, {}) + variables[name] = self._get_variables(param, template_config) return variables - def _get_default(self, parameter): + def _get_variables(self, parameter, user_config): '''Get the default value for the parameter, according to the language. ''' - default = None - try: - default = parameter['default'] - except KeyError: - return None - - if self.lang in default: - variable = default[self.lang] - elif "default" in default: - variable = default["default"] - elif "en" in default: - variable = default["en"] - elif len(default): - variable = default.popitem()[1] - else: - variable = None - - return variable + schema = parameter.get('schema', {}).copy() + schema = utils.remove_keys(schema, ['_description']) + + data = utils.DictOfDict(parameter.get('default', {})) + data.update(user_config) + + utils.validate_yaml_schema(data, schema) + return data def get_template_variables(self, template, skip=None): """Parse the template to extract the variables as a dictionary. @@ -177,16 +169,18 @@ class TexBookRenderer(Renderer): skip = [] variables = {} (current, templates) = self.parse_template(template) + if current: + variables[template.name] = current + for subtemplate in templates: if subtemplate in skip: continue - variables.update( - self.get_template_variables( + subtemplate = self.jinjaenv.get_template(subtemplate) + variables.update(self.get_template_variables( subtemplate, skip + templates ) - ) - variables.update(current) + ) return variables def parse_template(self, template): @@ -213,17 +207,17 @@ class TexBookRenderer(Renderer): if match: for var in match: try: - subvariables.update(json.loads(var)) + subvariables.update(yaml.load(var)) except ValueError as exception: raise errors.TemplateError( exception, ( - "Error while parsing json in file " - "{filename}. The json string was:" - "\n'''\n{jsonstring}\n'''" + "Error while parsing yaml in file " + "{filename}. The yaml string was:" + "\n'''\n{yamlstring}\n'''" ).format( filename=templatename, - jsonstring=var, + yamlstring=var, ) ) diff --git a/patacrep/utils.py b/patacrep/utils.py index e4b607b4..bcdb04e0 100644 --- a/patacrep/utils.py +++ b/patacrep/utils.py @@ -99,7 +99,6 @@ def remove_keys(data, keys=None, recursive=True): def validate_config_schema(config): """ Check that the songbook config respects the excepted songbook schema - """ data = config.copy() @@ -108,11 +107,20 @@ def validate_config_schema(config): 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) + validate_yaml_schema(data, schema_struct) + +def validate_yaml_schema(data, schema): + """ + Check that the data respects the schema + """ + rx_checker = Rx.Factory({"register_core_types": True}) + schema = rx_checker.make_schema(schema) + + if not isinstance(data, dict): + data = dict(data) try: schema.validate(data) except Rx.SchemaMismatch as exception: msg = 'Could not parse songbook file:\n' + str(exception) raise errors.SBFileError(msg) - return True From 0730695da2f1b86aedbc0dc59ea9963728b38c50 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 09:29:50 +0100 Subject: [PATCH 16/48] Use template variables --- patacrep/build.py | 2 +- patacrep/data/templates/default.tex | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index ce08e767..6331c2dc 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -82,7 +82,7 @@ class Songbook(object): config['book']['encoding'], ) - self.config['template'] = renderer.get_all_variables(self.config.get('template', {})) + config['_template'] = renderer.get_all_variables(self.config.get('template', {})) config['_compiled_authwords'] = authors.compile_authwords( copy.deepcopy(config['authors']) diff --git a/patacrep/data/templates/default.tex b/patacrep/data/templates/default.tex index f85cbb22..c0c1077a 100644 --- a/patacrep/data/templates/default.tex +++ b/patacrep/data/templates/default.tex @@ -34,7 +34,7 @@ schema: type: //arr contents: //str default: - title: "Guitar songook" + title: "Guitar songbook" author: "The Patacrep Team" (* endvariables -*) @@ -55,8 +55,8 @@ default: \usepackage{chords} -\title{((title))} -\author{((author))} +\title{(( _template["default.tex"].title ))} +\author{(( _template["default.tex"].author ))} \newindex{titleidx}{((filename))_title} \newauthorindex{authidx}{((filename))_auth} From a1b334ef0cf7674d0ee0965b8f9b90d024017288 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 09:33:13 +0100 Subject: [PATCH 17/48] Update language.sb --- test/test_songbook/languages.sb | 5 +++-- test/test_songbook/languages.tex.control | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/test/test_songbook/languages.sb b/test/test_songbook/languages.sb index ff589a74..43e7ab85 100644 --- a/test/test_songbook/languages.sb +++ b/test/test_songbook/languages.sb @@ -1,2 +1,3 @@ -datadir: -- languages_datadir +book: + datadir: + - languages_datadir diff --git a/test/test_songbook/languages.tex.control b/test/test_songbook/languages.tex.control index 6f08614f..c8b8cfd2 100644 --- a/test/test_songbook/languages.tex.control +++ b/test/test_songbook/languages.tex.control @@ -23,9 +23,10 @@ ]{article} \usepackage[ - chorded, -diagram, +chorded, pictures, +repeatchords, +importantdiagramonly, guitar, ]{patacrep} @@ -65,6 +66,9 @@ guitar, \newindex{titleidx}{languages_title} \newauthorindex{authidx}{languages_auth} +\authignoreword{unknown} +\authbyword{by} +\authsepword{and} \notenamesout{A}{B}{C}{D}{E}{F}{G} From df741a3d95bc8221aa118bc822aff53900653ec5 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 09:34:13 +0100 Subject: [PATCH 18/48] Delete key only if existent --- patacrep/songbook/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patacrep/songbook/__main__.py b/patacrep/songbook/__main__.py index 9daff597..587b1d8d 100644 --- a/patacrep/songbook/__main__.py +++ b/patacrep/songbook/__main__.py @@ -172,10 +172,10 @@ def main(): ) for path in songbook['book']['datadir'] ] + del songbook['book']['datadir'] + # Default value datadirs.append(os.path.dirname(os.path.abspath(songbook_path))) - - del songbook['book']['datadir'] songbook['_datadir'] = datadirs songbook['_cache'] = options.cache[0] From 152f50647ad9125a02651a0a5d925ce4c0f5c805 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 09:35:32 +0100 Subject: [PATCH 19/48] Update syntax.sb --- test/test_songbook/syntax.sb | 7 ++++--- test/test_songbook/syntax.tex.control | 8 ++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/test/test_songbook/syntax.sb b/test/test_songbook/syntax.sb index 4b1f4ef2..aca40b87 100644 --- a/test/test_songbook/syntax.sb +++ b/test/test_songbook/syntax.sb @@ -1,3 +1,4 @@ -datadir: -- syntax_datadir -lang: en +book: + datadir: + - syntax_datadir + lang: en diff --git a/test/test_songbook/syntax.tex.control b/test/test_songbook/syntax.tex.control index b2147b06..e142867e 100644 --- a/test/test_songbook/syntax.tex.control +++ b/test/test_songbook/syntax.tex.control @@ -23,9 +23,10 @@ ]{article} \usepackage[ - chorded, -diagram, +chorded, pictures, +repeatchords, +importantdiagramonly, guitar, ]{patacrep} @@ -62,6 +63,9 @@ guitar, \newindex{titleidx}{syntax_title} \newauthorindex{authidx}{syntax_auth} +\authignoreword{unknown} +\authbyword{by} +\authsepword{and} \notenamesout{A}{B}{C}{D}{E}{F}{G} From 853c34ab219b1e128426fc3dc667107b9c5aebc7 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 09:37:24 +0100 Subject: [PATCH 20/48] Update unicode.sb --- test/test_songbook/unicode.sb | 7 ++++--- test/test_songbook/unicode.tex.control | 8 ++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/test/test_songbook/unicode.sb b/test/test_songbook/unicode.sb index 19392a88..9f5163c5 100644 --- a/test/test_songbook/unicode.sb +++ b/test/test_songbook/unicode.sb @@ -1,3 +1,4 @@ -datadir: -- unicode_datadir -lang: en +book: + datadir: + - unicode_datadir + lang: en diff --git a/test/test_songbook/unicode.tex.control b/test/test_songbook/unicode.tex.control index 759a15d8..358938b6 100644 --- a/test/test_songbook/unicode.tex.control +++ b/test/test_songbook/unicode.tex.control @@ -23,9 +23,10 @@ ]{article} \usepackage[ - chorded, -diagram, +chorded, pictures, +repeatchords, +importantdiagramonly, guitar, ]{patacrep} @@ -62,6 +63,9 @@ guitar, \newindex{titleidx}{unicode_title} \newauthorindex{authidx}{unicode_auth} +\authignoreword{unknown} +\authbyword{by} +\authsepword{and} \notenamesout{A}{B}{C}{D}{E}{F}{G} From 368c15c580c2fec7c465504b283b3200e37c8a83 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 09:49:56 +0100 Subject: [PATCH 21/48] Rename 'sep' key on the fly --- patacrep/index.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/patacrep/index.py b/patacrep/index.py index 716a344a..346c1acd 100644 --- a/patacrep/index.py +++ b/patacrep/index.py @@ -77,6 +77,8 @@ class Index(object): def add_keyword(self, key, word): """Add 'word' to self.keywords[key].""" + if key == 'sep': + key = 'separators' if key not in self.keywords: self.keywords[key] = [] self.keywords[key].append(word) From 0b266c0308115412279da5ec836bd6b19d1ced86 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 09:51:35 +0100 Subject: [PATCH 22/48] Properly disable test_content --- test/{ttest_content => test_content}/__init__.py | 0 test/{ttest_content => test_content}/cwd.control | 0 test/{ttest_content => test_content}/cwd.source | 0 test/{ttest_content => test_content}/cwd_list.control | 0 test/{ttest_content => test_content}/cwd_list.source | 0 test/{ttest_content => test_content}/datadir/custom_list.json | 0 test/{ttest_content => test_content}/datadir/songs/chordpro.csg | 0 test/{ttest_content => test_content}/datadir/songs/exsong.sg | 0 test/{ttest_content => test_content}/datadir/songs/intersong.is | 0 test/{ttest_content => test_content}/datadir/songs/jsonlist.json | 0 .../datadir/songs/subdir/chordpro.csg | 0 test/{ttest_content => test_content}/datadir/songs/texfile.tex | 0 test/{ttest_content => test_content}/datadir/songs/texsong.tsg | 0 test/{ttest_content => test_content}/glob.control | 0 test/{ttest_content => test_content}/glob.source | 0 test/{ttest_content => test_content}/include.control | 0 test/{ttest_content => test_content}/include.source | 0 test/{ttest_content => test_content}/sections.control | 0 test/{ttest_content => test_content}/sections.source | 0 test/{ttest_content => test_content}/sections_short.control | 0 test/{ttest_content => test_content}/sections_short.source | 0 test/{ttest_content => test_content}/songs.control | 0 test/{ttest_content => test_content}/songs.source | 0 test/{ttest_content => test_content}/songsection.control | 0 test/{ttest_content => test_content}/songsection.source | 0 test/{ttest_content => test_content}/sorted.control | 0 test/{ttest_content => test_content}/sorted.source | 0 test/{ttest_content => test_content}/test_content.py | 1 + test/{ttest_content => test_content}/tex.control | 0 test/{ttest_content => test_content}/tex.source | 0 30 files changed, 1 insertion(+) rename test/{ttest_content => test_content}/__init__.py (100%) rename test/{ttest_content => test_content}/cwd.control (100%) rename test/{ttest_content => test_content}/cwd.source (100%) rename test/{ttest_content => test_content}/cwd_list.control (100%) rename test/{ttest_content => test_content}/cwd_list.source (100%) rename test/{ttest_content => test_content}/datadir/custom_list.json (100%) rename test/{ttest_content => test_content}/datadir/songs/chordpro.csg (100%) rename test/{ttest_content => test_content}/datadir/songs/exsong.sg (100%) rename test/{ttest_content => test_content}/datadir/songs/intersong.is (100%) rename test/{ttest_content => test_content}/datadir/songs/jsonlist.json (100%) rename test/{ttest_content => test_content}/datadir/songs/subdir/chordpro.csg (100%) rename test/{ttest_content => test_content}/datadir/songs/texfile.tex (100%) rename test/{ttest_content => test_content}/datadir/songs/texsong.tsg (100%) rename test/{ttest_content => test_content}/glob.control (100%) rename test/{ttest_content => test_content}/glob.source (100%) rename test/{ttest_content => test_content}/include.control (100%) rename test/{ttest_content => test_content}/include.source (100%) rename test/{ttest_content => test_content}/sections.control (100%) rename test/{ttest_content => test_content}/sections.source (100%) rename test/{ttest_content => test_content}/sections_short.control (100%) rename test/{ttest_content => test_content}/sections_short.source (100%) rename test/{ttest_content => test_content}/songs.control (100%) rename test/{ttest_content => test_content}/songs.source (100%) rename test/{ttest_content => test_content}/songsection.control (100%) rename test/{ttest_content => test_content}/songsection.source (100%) rename test/{ttest_content => test_content}/sorted.control (100%) rename test/{ttest_content => test_content}/sorted.source (100%) rename test/{ttest_content => test_content}/test_content.py (99%) rename test/{ttest_content => test_content}/tex.control (100%) rename test/{ttest_content => test_content}/tex.source (100%) diff --git a/test/ttest_content/__init__.py b/test/test_content/__init__.py similarity index 100% rename from test/ttest_content/__init__.py rename to test/test_content/__init__.py diff --git a/test/ttest_content/cwd.control b/test/test_content/cwd.control similarity index 100% rename from test/ttest_content/cwd.control rename to test/test_content/cwd.control diff --git a/test/ttest_content/cwd.source b/test/test_content/cwd.source similarity index 100% rename from test/ttest_content/cwd.source rename to test/test_content/cwd.source diff --git a/test/ttest_content/cwd_list.control b/test/test_content/cwd_list.control similarity index 100% rename from test/ttest_content/cwd_list.control rename to test/test_content/cwd_list.control diff --git a/test/ttest_content/cwd_list.source b/test/test_content/cwd_list.source similarity index 100% rename from test/ttest_content/cwd_list.source rename to test/test_content/cwd_list.source diff --git a/test/ttest_content/datadir/custom_list.json b/test/test_content/datadir/custom_list.json similarity index 100% rename from test/ttest_content/datadir/custom_list.json rename to test/test_content/datadir/custom_list.json diff --git a/test/ttest_content/datadir/songs/chordpro.csg b/test/test_content/datadir/songs/chordpro.csg similarity index 100% rename from test/ttest_content/datadir/songs/chordpro.csg rename to test/test_content/datadir/songs/chordpro.csg diff --git a/test/ttest_content/datadir/songs/exsong.sg b/test/test_content/datadir/songs/exsong.sg similarity index 100% rename from test/ttest_content/datadir/songs/exsong.sg rename to test/test_content/datadir/songs/exsong.sg diff --git a/test/ttest_content/datadir/songs/intersong.is b/test/test_content/datadir/songs/intersong.is similarity index 100% rename from test/ttest_content/datadir/songs/intersong.is rename to test/test_content/datadir/songs/intersong.is diff --git a/test/ttest_content/datadir/songs/jsonlist.json b/test/test_content/datadir/songs/jsonlist.json similarity index 100% rename from test/ttest_content/datadir/songs/jsonlist.json rename to test/test_content/datadir/songs/jsonlist.json diff --git a/test/ttest_content/datadir/songs/subdir/chordpro.csg b/test/test_content/datadir/songs/subdir/chordpro.csg similarity index 100% rename from test/ttest_content/datadir/songs/subdir/chordpro.csg rename to test/test_content/datadir/songs/subdir/chordpro.csg diff --git a/test/ttest_content/datadir/songs/texfile.tex b/test/test_content/datadir/songs/texfile.tex similarity index 100% rename from test/ttest_content/datadir/songs/texfile.tex rename to test/test_content/datadir/songs/texfile.tex diff --git a/test/ttest_content/datadir/songs/texsong.tsg b/test/test_content/datadir/songs/texsong.tsg similarity index 100% rename from test/ttest_content/datadir/songs/texsong.tsg rename to test/test_content/datadir/songs/texsong.tsg diff --git a/test/ttest_content/glob.control b/test/test_content/glob.control similarity index 100% rename from test/ttest_content/glob.control rename to test/test_content/glob.control diff --git a/test/ttest_content/glob.source b/test/test_content/glob.source similarity index 100% rename from test/ttest_content/glob.source rename to test/test_content/glob.source diff --git a/test/ttest_content/include.control b/test/test_content/include.control similarity index 100% rename from test/ttest_content/include.control rename to test/test_content/include.control diff --git a/test/ttest_content/include.source b/test/test_content/include.source similarity index 100% rename from test/ttest_content/include.source rename to test/test_content/include.source diff --git a/test/ttest_content/sections.control b/test/test_content/sections.control similarity index 100% rename from test/ttest_content/sections.control rename to test/test_content/sections.control diff --git a/test/ttest_content/sections.source b/test/test_content/sections.source similarity index 100% rename from test/ttest_content/sections.source rename to test/test_content/sections.source diff --git a/test/ttest_content/sections_short.control b/test/test_content/sections_short.control similarity index 100% rename from test/ttest_content/sections_short.control rename to test/test_content/sections_short.control diff --git a/test/ttest_content/sections_short.source b/test/test_content/sections_short.source similarity index 100% rename from test/ttest_content/sections_short.source rename to test/test_content/sections_short.source diff --git a/test/ttest_content/songs.control b/test/test_content/songs.control similarity index 100% rename from test/ttest_content/songs.control rename to test/test_content/songs.control diff --git a/test/ttest_content/songs.source b/test/test_content/songs.source similarity index 100% rename from test/ttest_content/songs.source rename to test/test_content/songs.source diff --git a/test/ttest_content/songsection.control b/test/test_content/songsection.control similarity index 100% rename from test/ttest_content/songsection.control rename to test/test_content/songsection.control diff --git a/test/ttest_content/songsection.source b/test/test_content/songsection.source similarity index 100% rename from test/ttest_content/songsection.source rename to test/test_content/songsection.source diff --git a/test/ttest_content/sorted.control b/test/test_content/sorted.control similarity index 100% rename from test/ttest_content/sorted.control rename to test/test_content/sorted.control diff --git a/test/ttest_content/sorted.source b/test/test_content/sorted.source similarity index 100% rename from test/ttest_content/sorted.source rename to test/test_content/sorted.source diff --git a/test/ttest_content/test_content.py b/test/test_content/test_content.py similarity index 99% rename from test/ttest_content/test_content.py rename to test/test_content/test_content.py index 92d8ec1c..fdcca1c1 100644 --- a/test/ttest_content/test_content.py +++ b/test/test_content/test_content.py @@ -33,6 +33,7 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest): @classmethod def _iter_testmethods(cls): + return """Iterate over dynamically generated test methods""" for source in sorted(glob.glob(os.path.join( os.path.dirname(__file__), diff --git a/test/ttest_content/tex.control b/test/test_content/tex.control similarity index 100% rename from test/ttest_content/tex.control rename to test/test_content/tex.control diff --git a/test/ttest_content/tex.source b/test/test_content/tex.source similarity index 100% rename from test/ttest_content/tex.source rename to test/test_content/tex.source From 615dc009928431f1fe8d942cbf7d9a769bd8ae49 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 10:01:39 +0100 Subject: [PATCH 23/48] [pylint] cleaning --- patacrep/build.py | 4 +--- patacrep/templates.py | 13 +++++++------ patacrep/utils.py | 1 - test/test_song/test_parser.py | 1 - 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index 6331c2dc..b22af9ce 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -8,9 +8,7 @@ import threading import os.path from subprocess import Popen, PIPE, call, check_call -import yaml - -from patacrep import authors, content, errors, encoding, files, utils +from patacrep import authors, content, errors, files, utils from patacrep.index import process_sxd from patacrep.templates import TexBookRenderer, iter_bookoptions from patacrep.songs import DataSubpath, DEFAULT_CONFIG diff --git a/patacrep/templates.py b/patacrep/templates.py index ebca05f9..57c34fda 100644 --- a/patacrep/templates.py +++ b/patacrep/templates.py @@ -9,7 +9,7 @@ from jinja2 import Environment, FileSystemLoader, ChoiceLoader, \ from jinja2.ext import Extension from jinja2.meta import find_referenced_templates as find_templates -from patacrep import errors, files, utils, Rx +from patacrep import errors, files, utils from patacrep.latex import lang2babel import patacrep.encoding @@ -143,7 +143,8 @@ class TexBookRenderer(Renderer): variables[name] = self._get_variables(param, template_config) return variables - def _get_variables(self, parameter, user_config): + @staticmethod + def _get_variables(parameter, user_config): '''Get the default value for the parameter, according to the language. ''' schema = parameter.get('schema', {}).copy() @@ -177,10 +178,10 @@ class TexBookRenderer(Renderer): continue subtemplate = self.jinjaenv.get_template(subtemplate) variables.update(self.get_template_variables( - subtemplate, - skip + templates - ) - ) + subtemplate, + skip + templates + ) + ) return variables def parse_template(self, template): diff --git a/patacrep/utils.py b/patacrep/utils.py index bcdb04e0..ca143449 100644 --- a/patacrep/utils.py +++ b/patacrep/utils.py @@ -102,7 +102,6 @@ def validate_config_schema(config): """ data = config.copy() - 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) diff --git a/test/test_song/test_parser.py b/test/test_song/test_parser.py index 91b33804..6eefe382 100644 --- a/test/test_song/test_parser.py +++ b/test/test_song/test_parser.py @@ -11,7 +11,6 @@ from pkg_resources import resource_filename import yaml from patacrep import files, pkg_datapath -from patacrep.songs import DEFAULT_CONFIG from patacrep.encoding import open_read from .. import logging_reduced From 2eca79ea085bfe828be5c45c4f07bc530ec77813 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 10:02:25 +0100 Subject: [PATCH 24/48] Skip pylint on test_content --- test/test_content/test_content.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_content/test_content.py b/test/test_content/test_content.py index fdcca1c1..2bc39d7a 100644 --- a/test/test_content/test_content.py +++ b/test/test_content/test_content.py @@ -2,6 +2,8 @@ # pylint: disable=too-few-public-methods +#pylint: skip-file + import glob import os import unittest From 22d768a6ed89aaf7072fd1fd8ba7d7db61a1dc22 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 10:14:36 +0100 Subject: [PATCH 25/48] Delay import to prevent installation crash on windows --- patacrep/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/patacrep/utils.py b/patacrep/utils.py index ca143449..58dd30ab 100644 --- a/patacrep/utils.py +++ b/patacrep/utils.py @@ -2,8 +2,6 @@ from collections import UserDict -import yaml - from patacrep import encoding, errors, pkg_datapath, Rx class DictOfDict(UserDict): @@ -103,6 +101,7 @@ def validate_config_schema(config): data = config.copy() schema_path = pkg_datapath('templates', 'songbook_schema.yml') + import yaml with encoding.open_read(schema_path) as schema_file: schema_struct = yaml.load(schema_file) schema_struct = remove_keys(schema_struct, ['_description']) From a28169aa8722fb2ce48f315d92d73b83ca711f0d Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 10:20:14 +0100 Subject: [PATCH 26/48] Move config validation out of utils --- patacrep/build.py | 17 +++++++++++++++-- patacrep/utils.py | 15 +-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index b22af9ce..a3ce2984 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -8,7 +8,9 @@ import threading import os.path from subprocess import Popen, PIPE, call, check_call -from patacrep import authors, content, errors, files, utils +import yaml + +from patacrep import authors, content, encoding, errors, files, pkg_datapath, utils from patacrep.index import process_sxd from patacrep.templates import TexBookRenderer, iter_bookoptions from patacrep.songs import DataSubpath, DEFAULT_CONFIG @@ -42,11 +44,22 @@ class Songbook(object): def __init__(self, raw_songbook, basename): super().__init__() self.config = raw_songbook - utils.validate_config_schema(raw_songbook) + self.validate_config_schema(raw_songbook) self.basename = basename # Some special keys have their value processed. self._set_datadir() + @staticmethod + def validate_config_schema(raw_songbook): + """ + Check that the songbook config respects the excepted songbook schema + """ + 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 = utils.remove_keys(schema_struct, ['_description']) + utils.validate_yaml_schema(raw_songbook, schema_struct) + def _set_datadir(self): """Set the default values for datadir""" abs_datadir = [] diff --git a/patacrep/utils.py b/patacrep/utils.py index 58dd30ab..a4a4feeb 100644 --- a/patacrep/utils.py +++ b/patacrep/utils.py @@ -2,7 +2,7 @@ from collections import UserDict -from patacrep import encoding, errors, pkg_datapath, Rx +from patacrep import errors, Rx class DictOfDict(UserDict): """Dictionary, with a recursive :meth:`update` method. @@ -94,19 +94,6 @@ def remove_keys(data, keys=None, recursive=True): 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() - - schema_path = pkg_datapath('templates', 'songbook_schema.yml') - import yaml - with encoding.open_read(schema_path) as schema_file: - schema_struct = yaml.load(schema_file) - schema_struct = remove_keys(schema_struct, ['_description']) - validate_yaml_schema(data, schema_struct) - def validate_yaml_schema(data, schema): """ Check that the data respects the schema From 304cb3df8cbf30d4a01daa7587752db3e7988ab7 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 10:43:31 +0100 Subject: [PATCH 27/48] classoptions is a template variable --- patacrep/data/templates/default.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patacrep/data/templates/default.tex b/patacrep/data/templates/default.tex index c0c1077a..3d9aef15 100644 --- a/patacrep/data/templates/default.tex +++ b/patacrep/data/templates/default.tex @@ -43,7 +43,7 @@ default: (* block documentclass *) \documentclass[ - (* for option in classoptions *) + (* for option in _template["default.tex"].classoptions *) ((option)), (* endfor *) ]{article} From 67b4511d4eb2e3f404e753555674b9eccc5793d0 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 10:45:33 +0100 Subject: [PATCH 28/48] Add template section in global schema --- patacrep/data/templates/songbook_schema.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/patacrep/data/templates/songbook_schema.yml b/patacrep/data/templates/songbook_schema.yml index cd6dc66d..d569f983 100644 --- a/patacrep/data/templates/songbook_schema.yml +++ b/patacrep/data/templates/songbook_schema.yml @@ -1,6 +1,7 @@ type: //rec optional: content: //str + template: //any required: _cache: //bool _datadir: From fe752dbe15c0bc244f9784f3430e35a4d8d98e46 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 11:06:41 +0100 Subject: [PATCH 29/48] Update patacrep.tex --- .../data/templates/default_songbook.sb.yml | 16 ++- patacrep/data/templates/patacrep.tex | 101 ++++++++---------- 2 files changed, 56 insertions(+), 61 deletions(-) diff --git a/patacrep/data/templates/default_songbook.sb.yml b/patacrep/data/templates/default_songbook.sb.yml index fc8ccd10..a9c7664a 100644 --- a/patacrep/data/templates/default_songbook.sb.yml +++ b/patacrep/data/templates/default_songbook.sb.yml @@ -25,8 +25,20 @@ authors: # Comment sont analysés les auteurs titles: # Comment sont analysés les titres prefix: - - To - - Do + - The + - Le + - La + - "L'" + - A + - Au + - Ces + - De + - Des + - El + - Les + - Ma + - Mon + - Un #template: # Des choses spécifiques au template # latex: # Des choses spécifiques au LaTeX diff --git a/patacrep/data/templates/patacrep.tex b/patacrep/data/templates/patacrep.tex index b9a01f1e..f1406d4e 100644 --- a/patacrep/data/templates/patacrep.tex +++ b/patacrep/data/templates/patacrep.tex @@ -19,60 +19,43 @@ %!- https://github.com/patacrep/ (* variables *) -{ -"subtitle": {"description": {"en": "Subtitle", "fr": "Sous-titre"}, - "default": {"default": ""} - }, -"version":{ "description": {"en": "Version", "fr": "Version"}, - "default": {"default": "undefined"} - }, -"web": {"description": {"en": "Website", "fr": "Site web"}, - "default": {"default": "http://www.patacrep.com"} - }, -"mail": {"description": {"en": "Email", "fr": "Adresse électronique"}, - "default": {"default": "crep@team-on-fire.com"} - }, -"picture": {"description": {"en": "Cover picture", "fr": "Image de couverture"}, - "type": "file", - "default": {"default": "img/treble_a"} - }, -"picturecopyright": {"description": {"en": "Copyright for the cover picture", "fr": "Copyright pour l'image de couverture"}, - "default": {"default": "Dbolton \\url{http://commons.wikimedia.org/wiki/User:Dbolton}"} - }, -"footer": {"description": {"en": "Footer", "fr": "Pied de page"}, - "default": {"default": "Generated using Songbook (\\url{http://www.patacrep.com})"} - }, -"songnumberbgcolor": {"description": {"en": "Number Shade", "fr": "Couleur des numéros"}, - "type": "color", - "default": {"default": "D1E4AE"} - }, -"notebgcolor": {"description": {"en": "Note Shade", "fr": "Couleur des notes"}, - "type": "color", - "default": {"default": "D1E4AE"} - }, -"indexbgcolor": {"description": {"en": "Index Shade", "fr": "Couleur d'index"}, - "type": "color", - "default": {"default": "D1E4AE"} - }, -"titleprefixwords": {"description": {"en": "Ignore some words in the beginning of song titles", - "fr": "Ignore des mots dans le classement des chansons"}, - "default": {"default": ["The", "Le", "La", "L'", "A", "Au", "Ces", "De", - "Des", "El", "Les", "Ma", "Mon", "Un"]} - } -} +schema: + type: //rec + optional: + version: //str + required: + subtitle: //str + url: //str + email: //str + picture: //str + picturecopyright: //str + footer: //str + bgcolor: + type: //rec + required: + songnumber: //str + note: //str + index: //str + +default: + subtitle: "" + version: "" + url: http://www.patacrep.com" + email: crep@team-on-fire.com + picture: img/treble_a + picturecopyright: "Dbolton \\url{http://commons.wikimedia.org/wiki/User:Dbolton}" + footer: "Generated using Songbook (\\url{http://www.patacrep.com})" + bgcolor: + songnumber: D1E4AE + note: D1E4AE + index: D1E4AE (* endvariables -*) (*- extends "default.tex" -*) (* block songbookpackages *) -%! booktype, bookoptions and instruments are defined in "songs.tex" \usepackage[ - ((booktype)), - (* for option in bookoptions *) - ((option)), - (* endfor *) - (* for instrument in instruments *) - ((instrument)), + (* for option in bookoptions *)((option)), (* endfor *) ]{crepbook} (* endblock *) @@ -91,9 +74,9 @@ \pagestyle{empty} -\definecolor{SongNumberBgColor}{HTML}{((songnumberbgcolor))} -\definecolor{NoteBgColor}{HTML}{((notebgcolor))} -\definecolor{IndexBgColor}{HTML}{((indexbgcolor))} +\definecolor{SongNumberBgColor}{HTML}{(( _template["patacrep.tex"].bgcolor.songnumber ))} +\definecolor{NoteBgColor}{HTML}{(( _template["patacrep.tex"].bgcolor.note ))} +\definecolor{IndexBgColor}{HTML}{(( _template["patacrep.tex"].bgcolor.index ))} \renewcommand{\snumbgcolor}{SongNumberBgColor} \renewcommand{\notebgcolor}{NoteBgColor} @@ -111,13 +94,13 @@ ]{hyperref} -\subtitle{((subtitle))} -(* if version!="undefined" -*) - \version{((version))} +\subtitle{(( _template["patacrep.tex"].subtitle ))} +(* if _template["patacrep.tex"].version -*) + \version{(( _template["patacrep.tex"].version ))} (* endif *) -\mail{((mail))} -\web{((web))} -\picture{((picture))} -\picturecopyright{((picturecopyright))} -\footer{((footer))} +\mail{(( _template["patacrep.tex"].email ))} +\web{(( _template["patacrep.tex"].url ))} +\picture{(( _template["patacrep.tex"].picture ))} +\picturecopyright{(( _template["patacrep.tex"].picturecopyright ))} +\footer{(( _template["patacrep.tex"].footer ))} (* endblock *) From a2688edd5723837106212544ed036d10cd7bf0aa Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 11:36:14 +0100 Subject: [PATCH 30/48] template default and schema are managed inside the templates --- .../data/templates/default_songbook.sb.yml | 21 -------------- patacrep/data/templates/songbook_schema.yml | 28 ------------------- 2 files changed, 49 deletions(-) diff --git a/patacrep/data/templates/default_songbook.sb.yml b/patacrep/data/templates/default_songbook.sb.yml index a9c7664a..67a9a464 100644 --- a/patacrep/data/templates/default_songbook.sb.yml +++ b/patacrep/data/templates/default_songbook.sb.yml @@ -39,24 +39,3 @@ titles: # Comment sont analysés les titres - Ma - Mon - Un - -#template: # Des choses spécifiques au template - # latex: # Des choses spécifiques au LaTeX - # classoptions: STRING - - # # Peut dépendre fortement du template - # color: # Des couleurs - # songnumber: STRING - # notebg: STRING - # indexbg: STRING - - # titlepage: #Configuration de la page de garde - # title: STRING - # author: STRING - # subtitle: STRING - # version: STRING - # url: STRING - # email: STRING - # picture: STRING - # picturecopyright: STRING - # footer: STRING \ No newline at end of file diff --git a/patacrep/data/templates/songbook_schema.yml b/patacrep/data/templates/songbook_schema.yml index d569f983..cb4463df 100644 --- a/patacrep/data/templates/songbook_schema.yml +++ b/patacrep/data/templates/songbook_schema.yml @@ -78,31 +78,3 @@ required: - type: //arr contents: //str - type: //nil - # template: - # _description: "Des choses spécifiques au template" - # type: //rec - # required: - # file: //str - # optional: - # latex: - # type: //rec - # required: - # classoptions: //str - # color: - # type: //rec - # required: - # songnumber: //str - # notebg: //str - # indexbg: //str - # titlepage: - # type: //rec - # required: - # title: //str - # author: //str - # subtitle: //str - # version: //str - # url: //str - # email: //str - # picture: //str - # picturecopyright: //str - # footer: //str From 33eded681fa5d2a9bb2e675a109473b35b750733 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 11:40:07 +0100 Subject: [PATCH 31/48] Remove _description key --- patacrep/build.py | 1 - patacrep/data/templates/default.tex | 7 ++++--- patacrep/data/templates/songbook_schema.yml | 2 -- patacrep/templates.py | 1 - 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index a3ce2984..29b2502e 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -57,7 +57,6 @@ class Songbook(object): 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 = utils.remove_keys(schema_struct, ['_description']) utils.validate_yaml_schema(raw_songbook, schema_struct) def _set_datadir(self): diff --git a/patacrep/data/templates/default.tex b/patacrep/data/templates/default.tex index 3d9aef15..f02d0895 100644 --- a/patacrep/data/templates/default.tex +++ b/patacrep/data/templates/default.tex @@ -23,19 +23,20 @@ schema: type: //rec required: title: - _description: _("Title") type: //str author: - _description: _("Author") type: //str optional: classoptions: - _description: _("LaTeX class options") type: //arr contents: //str default: title: "Guitar songbook" author: "The Patacrep Team" +description.en: + title: "Title" + author: "Author" + classoptions: "LaTeX class options" (* endvariables -*) (*- extends "songs.tex" -*) diff --git a/patacrep/data/templates/songbook_schema.yml b/patacrep/data/templates/songbook_schema.yml index cb4463df..ee75be64 100644 --- a/patacrep/data/templates/songbook_schema.yml +++ b/patacrep/data/templates/songbook_schema.yml @@ -47,7 +47,6 @@ required: - type: //str value: "solfedge" authors: - _description: "Comment sont analysés les auteurs" type: //rec required: separators: @@ -69,7 +68,6 @@ required: contents: //str - type: //nil titles: - _description: "Comment sont analysés les titres" type: //rec required: prefix: diff --git a/patacrep/templates.py b/patacrep/templates.py index 57c34fda..3ba3867d 100644 --- a/patacrep/templates.py +++ b/patacrep/templates.py @@ -148,7 +148,6 @@ class TexBookRenderer(Renderer): '''Get the default value for the parameter, according to the language. ''' schema = parameter.get('schema', {}).copy() - schema = utils.remove_keys(schema, ['_description']) data = utils.DictOfDict(parameter.get('default', {})) data.update(user_config) From 727c6a20c6c3bb087019937715be9c2746ada028 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 12:06:52 +0100 Subject: [PATCH 32/48] Merge schema and default arguments --- patacrep/build.py | 28 ++-- .../data/templates/default_songbook.sb.yml | 41 ------ patacrep/data/templates/songbook_model.yml | 121 ++++++++++++++++++ patacrep/data/templates/songbook_schema.yml | 78 ----------- patacrep/songbook/__main__.py | 10 +- test/test_song/test_parser.py | 5 +- 6 files changed, 143 insertions(+), 140 deletions(-) delete mode 100644 patacrep/data/templates/default_songbook.sb.yml create mode 100644 patacrep/data/templates/songbook_model.yml delete mode 100644 patacrep/data/templates/songbook_schema.yml diff --git a/patacrep/build.py b/patacrep/build.py index 29b2502e..01553bc0 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -44,21 +44,15 @@ class Songbook(object): def __init__(self, raw_songbook, basename): super().__init__() self.config = raw_songbook - self.validate_config_schema(raw_songbook) + + # Validate config + schema = config_model('schema') + utils.validate_yaml_schema(raw_songbook, schema) + self.basename = basename # Some special keys have their value processed. self._set_datadir() - @staticmethod - def validate_config_schema(raw_songbook): - """ - Check that the songbook config respects the excepted songbook schema - """ - schema_path = pkg_datapath('templates', 'songbook_schema.yml') - with encoding.open_read(schema_path) as schema_file: - schema_struct = yaml.load(schema_file) - utils.validate_yaml_schema(raw_songbook, schema_struct) - def _set_datadir(self): """Set the default values for datadir""" abs_datadir = [] @@ -335,3 +329,15 @@ class SongbookBuilder(object): os.unlink(self.basename + ext) except Exception as exception: raise errors.CleaningError(self.basename + ext, exception) + + +def config_model(*args): + """Get the model structure with schema and default options""" + model_path = pkg_datapath('templates', 'songbook_model.yml') + with encoding.open_read(model_path) as model_file: + data = yaml.load(model_file) + + while data and args: + name, *args = args + data = data.get(name) + return data diff --git a/patacrep/data/templates/default_songbook.sb.yml b/patacrep/data/templates/default_songbook.sb.yml deleted file mode 100644 index 67a9a464..00000000 --- a/patacrep/data/templates/default_songbook.sb.yml +++ /dev/null @@ -1,41 +0,0 @@ -book: - lang: en - encoding: utf-8 - pictures: yes - template: default.tex - onesongperpage: no - -chords: # Options relatives aux accords - show: yes - diagramreminder: important - diagrampage: yes - repeatchords: yes - lilypond: no - tablatures: no - instrument: guitar - notation: alphascale - -authors: # Comment sont analysés les auteurs - separators: - - and - ignore: - - unknown - after: - - by - -titles: # Comment sont analysés les titres - prefix: - - The - - Le - - La - - "L'" - - A - - Au - - Ces - - De - - Des - - El - - Les - - Ma - - Mon - - Un diff --git a/patacrep/data/templates/songbook_model.yml b/patacrep/data/templates/songbook_model.yml new file mode 100644 index 00000000..1f588996 --- /dev/null +++ b/patacrep/data/templates/songbook_model.yml @@ -0,0 +1,121 @@ +schema: + type: //rec + optional: + content: //str + template: //any + required: + _cache: //bool + _datadir: + type: //arr + contents: //str + book: + type: //rec + required: + encoding: //str + lang: //str + pictures: //bool + template: //str + onesongperpage: //bool + chords: + type: //rec + required: + show: //bool + diagrampage: //bool + repeatchords: //bool + lilypond: //bool + tablatures: //bool + diagramreminder: + type: //any + of: + - type: //str + value: "none" + - type: //str + value: "important" + - type: //str + value: "all" + instrument: + type: //any + of: + - type: //str + value: "guitar" + - type: //str + value: "ukulele" + notation: + type: //any + of: + - type: //str + value: "alphascale" + - type: //str + value: "solfedge" + authors: + type: //rec + required: + separators: + type: //any + of: + - type: //arr + contents: //str + - type: //nil + ignore: + type: //any + of: + - type: //arr + contents: //str + - type: //nil + after: + type: //any + of: + - type: //arr + contents: //str + - type: //nil + titles: + type: //rec + required: + prefix: + type: //any + of: + - type: //arr + contents: //str + - type: //nil +default: + book: + lang: en + encoding: utf-8 + pictures: yes + template: default.tex + onesongperpage: no + + chords: # Options relatives aux accords + show: yes + diagramreminder: important + diagrampage: yes + repeatchords: yes + lilypond: no + tablatures: no + instrument: guitar + notation: alphascale + + authors: # Comment sont analysés les auteurs + separators: + - and + ignore: + - unknown + after: + - by + + titles: # Comment sont analysés les titres + prefix: + - The + - Le + - La + - "L'" + - A + - Au + - Ces + - De + - Des + - El + - Les + - Ma + - Mon + - Un diff --git a/patacrep/data/templates/songbook_schema.yml b/patacrep/data/templates/songbook_schema.yml deleted file mode 100644 index ee75be64..00000000 --- a/patacrep/data/templates/songbook_schema.yml +++ /dev/null @@ -1,78 +0,0 @@ -type: //rec -optional: - content: //str - template: //any -required: - _cache: //bool - _datadir: - type: //arr - contents: //str - book: - type: //rec - required: - encoding: //str - lang: //str - pictures: //bool - template: //str - onesongperpage: //bool - chords: - type: //rec - required: - show: //bool - diagrampage: //bool - repeatchords: //bool - lilypond: //bool - tablatures: //bool - diagramreminder: - type: //any - of: - - type: //str - value: "none" - - type: //str - value: "important" - - type: //str - value: "all" - instrument: - type: //any - of: - - type: //str - value: "guitar" - - type: //str - value: "ukulele" - notation: - type: //any - of: - - type: //str - value: "alphascale" - - type: //str - value: "solfedge" - authors: - type: //rec - required: - separators: - type: //any - of: - - type: //arr - contents: //str - - type: //nil - ignore: - type: //any - of: - - type: //arr - contents: //str - - type: //nil - after: - type: //any - of: - - type: //arr - contents: //str - - type: //nil - titles: - type: //rec - required: - prefix: - type: //any - of: - - type: //arr - contents: //str - - type: //nil diff --git a/patacrep/songbook/__main__.py b/patacrep/songbook/__main__.py index 587b1d8d..adc581c5 100644 --- a/patacrep/songbook/__main__.py +++ b/patacrep/songbook/__main__.py @@ -8,10 +8,10 @@ import textwrap import sys import yaml -from patacrep.build import SongbookBuilder, DEFAULT_STEPS +from patacrep.build import SongbookBuilder, DEFAULT_STEPS, config_model from patacrep.utils import yesno, DictOfDict from patacrep import __version__ -from patacrep import errors, pkg_datapath +from patacrep import errors import patacrep.encoding # Logging configuration @@ -133,11 +133,6 @@ def main(): basename = os.path.basename(songbook_path)[:-3] - # Load the default songbook config - default_songbook_path = pkg_datapath('templates', 'default_songbook.sb.yml') - with patacrep.encoding.open_read(default_songbook_path) as default_songbook_file: - default_songbook = DictOfDict(yaml.load(default_songbook_file)) - # Load the user songbook config try: with patacrep.encoding.open_read(songbook_path) as songbook_file: @@ -154,6 +149,7 @@ def main(): sys.exit(1) # Merge the default and user configs + default_songbook = DictOfDict(config_model('default')) default_songbook.update(user_songbook) songbook = dict(default_songbook) diff --git a/test/test_song/test_parser.py b/test/test_song/test_parser.py index 6eefe382..0e7f4dcd 100644 --- a/test/test_song/test_parser.py +++ b/test/test_song/test_parser.py @@ -12,6 +12,7 @@ import yaml from patacrep import files, pkg_datapath from patacrep.encoding import open_read +from patacrep.build import config_model from .. import logging_reduced from .. import dynamic # pylint: disable=unused-import @@ -76,9 +77,7 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest): """Iterate over song files to test.""" # Setting datadir # Load the default songbook config - default_songbook_path = pkg_datapath('templates', 'default_songbook.sb.yml') - with open_read(default_songbook_path) as default_songbook_file: - cls.config = yaml.load(default_songbook_file) + cls.config = config_model('default') if '_datadir' not in cls.config: cls.config['_datadir'] = [] From 69abfcc9eea6e80fd3ceaa4e9650ecd0f415e631 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 12:18:48 +0100 Subject: [PATCH 33/48] remove_keys is now useless --- patacrep/utils.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/patacrep/utils.py b/patacrep/utils.py index a4a4feeb..b9f0de88 100644 --- a/patacrep/utils.py +++ b/patacrep/utils.py @@ -78,22 +78,6 @@ def yesno(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_yaml_schema(data, schema): """ Check that the data respects the schema From 80d69a1060a921c589d586f43c31459ee83310e5 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 12:22:55 +0100 Subject: [PATCH 34/48] Remove DEFAULT_CONFIG --- patacrep/build.py | 6 ++---- patacrep/songs/__init__.py | 4 +--- test/test_content/test_content.py | 2 -- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index 01553bc0..29388952 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -13,7 +13,7 @@ import yaml from patacrep import authors, content, encoding, errors, files, pkg_datapath, utils from patacrep.index import process_sxd from patacrep.templates import TexBookRenderer, iter_bookoptions -from patacrep.songs import DataSubpath, DEFAULT_CONFIG +from patacrep.songs import DataSubpath LOGGER = logging.getLogger(__name__) EOL = "\n" @@ -76,9 +76,7 @@ class Songbook(object): Arguments: - output: a file object, in which the file will be written. """ - # Updating configuration - config = DEFAULT_CONFIG.copy() - config.update(self.config) + config = self.config.copy() renderer = TexBookRenderer( config['book']['template'], config['_datadir'], diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index 23c7900f..c3636bca 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -12,8 +12,6 @@ from patacrep.authors import process_listauthors LOGGER = logging.getLogger(__name__) -DEFAULT_CONFIG = {} - def cached_name(datadir, filename): """Return the filename of the cache version of the file.""" fullpath = os.path.abspath(os.path.join(datadir, '.cache', filename)) @@ -97,7 +95,7 @@ class Song: def __init__(self, subpath, config=None, *, datadir=None): if config is None: - config = DEFAULT_CONFIG.copy() + config = {} if datadir is None: self.datadir = "" diff --git a/test/test_content/test_content.py b/test/test_content/test_content.py index 2bc39d7a..868b8fc5 100644 --- a/test/test_content/test_content.py +++ b/test/test_content/test_content.py @@ -105,8 +105,6 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest): with encoding.open_read(default_songbook_path) as default_songbook_file: config = yaml.load(default_songbook_file) - #config = DEFAULT_CONFIG.copy() - datadirpaths = [os.path.join(os.path.dirname(__file__), 'datadir')] # todo : yaml and testing? From 0204919280067da93c7512a4fe6f32410192e3e2 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 12:27:10 +0100 Subject: [PATCH 35/48] Restore test_content tests --- patacrep/content/include.py | 2 +- test/test_content/test_content.py | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/patacrep/content/include.py b/patacrep/content/include.py index bfd5db5c..0dbba32c 100644 --- a/patacrep/content/include.py +++ b/patacrep/content/include.py @@ -46,7 +46,7 @@ def parse(keyword, config, argument, contentlist): try: with encoding.open_read( filepath, - encoding=config['encoding'] + encoding=config['book']['encoding'] ) as content_file: new_content = json.load(content_file) except Exception as error: # pylint: disable=broad-except diff --git a/test/test_content/test_content.py b/test/test_content/test_content.py index 868b8fc5..4d824346 100644 --- a/test/test_content/test_content.py +++ b/test/test_content/test_content.py @@ -2,8 +2,6 @@ # pylint: disable=too-few-public-methods -#pylint: skip-file - import glob import os import unittest @@ -11,9 +9,10 @@ import json import yaml -from patacrep.songs import DataSubpath, DEFAULT_CONFIG +from patacrep.songs import DataSubpath from patacrep import content, encoding, files, pkg_datapath from patacrep.content import song, section, songsection, tex +from patacrep.build import config_model from .. import logging_reduced from .. import dynamic # pylint: disable=unused-import @@ -35,7 +34,6 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest): @classmethod def _iter_testmethods(cls): - return """Iterate over dynamically generated test methods""" for source in sorted(glob.glob(os.path.join( os.path.dirname(__file__), @@ -101,14 +99,12 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest): """Generate the config to process the content""" # Load the default songbook config - default_songbook_path = pkg_datapath('templates', 'default_songbook.sb.yml') - with encoding.open_read(default_songbook_path) as default_songbook_file: - config = yaml.load(default_songbook_file) + config = config_model('default') datadirpaths = [os.path.join(os.path.dirname(__file__), 'datadir')] # todo : yaml and testing? - config['datadir'] = datadirpaths + config['_datadir'] = datadirpaths config['_songdir'] = [ DataSubpath(path, 'songs') From 666f4d38f4944d085199bf66440c1c3c60db2a39 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 16:26:19 +0100 Subject: [PATCH 36/48] [pylint] Remove unused imports --- test/test_content/test_content.py | 4 +--- test/test_song/test_parser.py | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/test/test_content/test_content.py b/test/test_content/test_content.py index 4d824346..83e78797 100644 --- a/test/test_content/test_content.py +++ b/test/test_content/test_content.py @@ -7,10 +7,8 @@ import os import unittest import json -import yaml - from patacrep.songs import DataSubpath -from patacrep import content, encoding, files, pkg_datapath +from patacrep import content, files from patacrep.content import song, section, songsection, tex from patacrep.build import config_model diff --git a/test/test_song/test_parser.py b/test/test_song/test_parser.py index 0e7f4dcd..1cdfae5b 100644 --- a/test/test_song/test_parser.py +++ b/test/test_song/test_parser.py @@ -8,9 +8,7 @@ import os import unittest from pkg_resources import resource_filename -import yaml - -from patacrep import files, pkg_datapath +from patacrep import files from patacrep.encoding import open_read from patacrep.build import config_model From bc163b146dc6bfdc21b507c49d927483d0dc35a3 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 16:35:18 +0100 Subject: [PATCH 37/48] Correct content parameter --- examples/example-all.yaml.sb | 19 ++++++------------- patacrep/data/templates/songbook_model.yml | 2 +- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/examples/example-all.yaml.sb b/examples/example-all.yaml.sb index 4f287c6d..c121ca14 100644 --- a/examples/example-all.yaml.sb +++ b/examples/example-all.yaml.sb @@ -1,23 +1,16 @@ -# bookoptions: -# - "diagram" -# - "repeatchords" -# - "pictures" -# booktype: "chorded" -# datadir: "." -# template: "patacrep.tex" -# lang: "fr" -# encoding: "utf8" book: lang: fr + encoding: utf8 + template: patacrep.tex datadir: "." pictures: yes - #type: chorded chords: - show: true - diagramreminder: none + show: yes + diagramreminder: all + repeatchords: yes authors: separators: - "and" - "et" -content: '[["sorted"]]' \ No newline at end of file +content: [["sorted"]] \ No newline at end of file diff --git a/patacrep/data/templates/songbook_model.yml b/patacrep/data/templates/songbook_model.yml index 1f588996..7567de47 100644 --- a/patacrep/data/templates/songbook_model.yml +++ b/patacrep/data/templates/songbook_model.yml @@ -1,7 +1,7 @@ schema: type: //rec optional: - content: //str + content: //any template: //any required: _cache: //bool From b93062921197b746d4f70d63f35020cf68a67c2c Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 16:43:10 +0100 Subject: [PATCH 38/48] Rename template_var --- patacrep/data/templates/patacrep.tex | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/patacrep/data/templates/patacrep.tex b/patacrep/data/templates/patacrep.tex index f1406d4e..8023ed3b 100644 --- a/patacrep/data/templates/patacrep.tex +++ b/patacrep/data/templates/patacrep.tex @@ -74,9 +74,11 @@ default: \pagestyle{empty} -\definecolor{SongNumberBgColor}{HTML}{(( _template["patacrep.tex"].bgcolor.songnumber ))} -\definecolor{NoteBgColor}{HTML}{(( _template["patacrep.tex"].bgcolor.note ))} -\definecolor{IndexBgColor}{HTML}{(( _template["patacrep.tex"].bgcolor.index ))} +(*- set template_var = _template["patacrep.tex"] -*) + +\definecolor{SongNumberBgColor}{HTML}{(( template_var.bgcolor.songnumber ))} +\definecolor{NoteBgColor}{HTML}{(( template_var.bgcolor.note ))} +\definecolor{IndexBgColor}{HTML}{(( template_var.bgcolor.index ))} \renewcommand{\snumbgcolor}{SongNumberBgColor} \renewcommand{\notebgcolor}{NoteBgColor} @@ -94,13 +96,13 @@ default: ]{hyperref} -\subtitle{(( _template["patacrep.tex"].subtitle ))} -(* if _template["patacrep.tex"].version -*) - \version{(( _template["patacrep.tex"].version ))} +\subtitle{(( template_var.subtitle ))} +(* if template_var.version -*) + \version{(( template_var.version ))} (* endif *) -\mail{(( _template["patacrep.tex"].email ))} -\web{(( _template["patacrep.tex"].url ))} -\picture{(( _template["patacrep.tex"].picture ))} -\picturecopyright{(( _template["patacrep.tex"].picturecopyright ))} -\footer{(( _template["patacrep.tex"].footer ))} +\mail{(( template_var.email ))} +\web{(( template_var.url ))} +\picture{(( template_var.picture ))} +\picturecopyright{(( template_var.picturecopyright ))} +\footer{(( template_var.footer ))} (* endblock *) From a46e42bfce3be0da203724013199ebe79de1364b Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 16:46:49 +0100 Subject: [PATCH 39/48] Demonstrate color arguments --- examples/example-all.yaml.sb | 12 +++++++++++- patacrep/data/templates/patacrep.tex | 12 ++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/examples/example-all.yaml.sb b/examples/example-all.yaml.sb index c121ca14..01a95015 100644 --- a/examples/example-all.yaml.sb +++ b/examples/example-all.yaml.sb @@ -13,4 +13,14 @@ authors: separators: - "and" - "et" -content: [["sorted"]] \ No newline at end of file +content: [["sorted"]] + +template: + patacrep.tex: + color: + songlink: FF0000 + hyperlink: 0000FF + bgcolor: + note: D1E4AE + songnumber: AED1E4 + index: E4AED1 #not enough songs to see it \ No newline at end of file diff --git a/patacrep/data/templates/patacrep.tex b/patacrep/data/templates/patacrep.tex index 8023ed3b..48cb7bd4 100644 --- a/patacrep/data/templates/patacrep.tex +++ b/patacrep/data/templates/patacrep.tex @@ -30,6 +30,11 @@ schema: picture: //str picturecopyright: //str footer: //str + color: + type: //rec + required: + songlink: //str + hyperlink: //str bgcolor: type: //rec required: @@ -45,6 +50,9 @@ default: picture: img/treble_a picturecopyright: "Dbolton \\url{http://commons.wikimedia.org/wiki/User:Dbolton}" footer: "Generated using Songbook (\\url{http://www.patacrep.com})" + color: + songlink: 4e9a06 + hyperlink: 204a87 bgcolor: songnumber: D1E4AE note: D1E4AE @@ -84,8 +92,8 @@ default: \renewcommand{\notebgcolor}{NoteBgColor} \renewcommand{\idxbgcolor}{IndexBgColor} -\definecolor{tango-green-3}{HTML}{4e9a06} -\definecolor{tango-blue-3}{HTML}{204a87} +\definecolor{tango-green-3}{HTML}{(( template_var.color.songlink ))} +\definecolor{tango-blue-3}{HTML}{(( template_var.color.hyperlink ))} \usepackage[ bookmarks, bookmarksopen, From 55d67014035ee7a0c93a6cf1ac96ee3dcfc56420 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 16:49:01 +0100 Subject: [PATCH 40/48] Validate the config first --- patacrep/build.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index 29388952..73ba64f3 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -43,12 +43,11 @@ class Songbook(object): def __init__(self, raw_songbook, basename): super().__init__() - self.config = raw_songbook - # Validate config schema = config_model('schema') utils.validate_yaml_schema(raw_songbook, schema) + self.config = raw_songbook self.basename = basename # Some special keys have their value processed. self._set_datadir() From c235cea7eef79aa2adec4a8a30d745ab429139fa Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 16:53:55 +0100 Subject: [PATCH 41/48] Use template_var for default.tex --- patacrep/data/templates/default.tex | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/patacrep/data/templates/default.tex b/patacrep/data/templates/default.tex index f02d0895..5fbf5c10 100644 --- a/patacrep/data/templates/default.tex +++ b/patacrep/data/templates/default.tex @@ -41,10 +41,11 @@ description.en: (*- extends "songs.tex" -*) (*- set indexes = "titleidx,authidx" -*) +(*- set template_var = _template["default.tex"] -*) (* block documentclass *) \documentclass[ - (* for option in _template["default.tex"].classoptions *) + (* for option in template_var.classoptions *) ((option)), (* endfor *) ]{article} @@ -56,8 +57,8 @@ description.en: \usepackage{chords} -\title{(( _template["default.tex"].title ))} -\author{(( _template["default.tex"].author ))} +\title{(( template_var.title ))} +\author{(( template_var.author ))} \newindex{titleidx}{((filename))_title} \newauthorindex{authidx}{((filename))_auth} From 4ec1152feae36c47620348b1a7467959673eee21 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 16:56:35 +0100 Subject: [PATCH 42/48] Explain statement --- patacrep/index.py | 1 + 1 file changed, 1 insertion(+) diff --git a/patacrep/index.py b/patacrep/index.py index 346c1acd..c29b88ab 100644 --- a/patacrep/index.py +++ b/patacrep/index.py @@ -77,6 +77,7 @@ class Index(object): def add_keyword(self, key, word): """Add 'word' to self.keywords[key].""" + # Because LaTeX uses 'sep' if key == 'sep': key = 'separators' if key not in self.keywords: From 81eb55811c08835eb399cbec39703e47e43463c8 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 17:00:22 +0100 Subject: [PATCH 43/48] Correct indentation --- patacrep/templates.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/patacrep/templates.py b/patacrep/templates.py index 3ba3867d..f86b1d99 100644 --- a/patacrep/templates.py +++ b/patacrep/templates.py @@ -176,11 +176,12 @@ class TexBookRenderer(Renderer): if subtemplate in skip: continue subtemplate = self.jinjaenv.get_template(subtemplate) - variables.update(self.get_template_variables( - subtemplate, - skip + templates + variables.update( + self.get_template_variables( + subtemplate, + skip + templates + ) ) - ) return variables def parse_template(self, template): From 2547a750a4e8fb402133731f8fef09fd69020e64 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Dec 2015 17:35:38 +0100 Subject: [PATCH 44/48] Remove useless comment --- test/test_content/test_content.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_content/test_content.py b/test/test_content/test_content.py index 83e78797..23d498bc 100644 --- a/test/test_content/test_content.py +++ b/test/test_content/test_content.py @@ -101,7 +101,6 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest): datadirpaths = [os.path.join(os.path.dirname(__file__), 'datadir')] - # todo : yaml and testing? config['_datadir'] = datadirpaths config['_songdir'] = [ From 342ffe8919ffc42ff1b9055b412b26d07fe47ce0 Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 22 Dec 2015 19:53:11 +0100 Subject: [PATCH 45/48] Function `compile_authwords()` no longer has side effects Closes #185 --- patacrep/authors.py | 30 +++++++++++------------------- patacrep/build.py | 5 +---- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/patacrep/authors.py b/patacrep/authors.py index 12715ed2..c8b65361 100644 --- a/patacrep/authors.py +++ b/patacrep/authors.py @@ -5,8 +5,6 @@ import re LOGGER = logging.getLogger(__name__) -AUTHWORDS_KEYS = ["after", "ignore", "separators"] - RE_AFTER = r"^.*\b{}\b(.*)$" RE_SEPARATOR = r"^(.*)\b *{} *(\b.*)?$" @@ -15,23 +13,17 @@ def compile_authwords(authwords): This regexp will later be used to match these words in authors strings. """ - # Fill missing values - for key in AUTHWORDS_KEYS: - if key not in authwords: - authwords[key] = [] - - # Compilation - authwords['after'] = [ - re.compile(RE_AFTER.format(word), re.LOCALE) - for word in authwords['after'] - ] - authwords['separators'] = [ - re.compile(RE_SEPARATOR.format(word), re.LOCALE) - for word in ([" %s" % word for word in authwords['separators']] + [',', ';']) - ] - - return authwords - + return { + 'ignore': authwords.get('ignore', []), + 'after': [ + re.compile(RE_AFTER.format(word), re.LOCALE) + for word in authwords['after'] + ], + 'separators': [ + re.compile(RE_SEPARATOR.format(word), re.LOCALE) + for word in ([" %s" % word for word in authwords['separators']] + [',', ';']) + ], + } def split_author_names(string): r"""Split author between first and last name. diff --git a/patacrep/build.py b/patacrep/build.py index 73ba64f3..ed24664d 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -1,7 +1,6 @@ """Build a songbook, according to parameters found in a .sb file.""" import codecs -import copy import glob import logging import threading @@ -85,9 +84,7 @@ class Songbook(object): config['_template'] = renderer.get_all_variables(self.config.get('template', {})) - config['_compiled_authwords'] = authors.compile_authwords( - copy.deepcopy(config['authors']) - ) + config['_compiled_authwords'] = authors.compile_authwords(config['authors']) # Loading custom plugins config['_content_plugins'] = files.load_plugins( From 7c37a848118a302632935a97bd0ae1f7ef667513 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 24 Dec 2015 08:32:01 +0100 Subject: [PATCH 46/48] Useless dict.copy --- patacrep/templates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patacrep/templates.py b/patacrep/templates.py index f86b1d99..370b3d88 100644 --- a/patacrep/templates.py +++ b/patacrep/templates.py @@ -147,7 +147,7 @@ class TexBookRenderer(Renderer): def _get_variables(parameter, user_config): '''Get the default value for the parameter, according to the language. ''' - schema = parameter.get('schema', {}).copy() + schema = parameter.get('schema', {}) data = utils.DictOfDict(parameter.get('default', {})) data.update(user_config) From b0ece3246704acc82a4421f218b538cf1dc528fa Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 24 Dec 2015 08:38:49 +0100 Subject: [PATCH 47/48] Correct docstring --- patacrep/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/patacrep/utils.py b/patacrep/utils.py index b9f0de88..7ddd0eca 100644 --- a/patacrep/utils.py +++ b/patacrep/utils.py @@ -79,8 +79,9 @@ def yesno(string): )) def validate_yaml_schema(data, schema): - """ - Check that the data respects the schema + """Check that the data respects the schema + + Will raise `SBFileError` if the schema is not respected. """ rx_checker = Rx.Factory({"register_core_types": True}) schema = rx_checker.make_schema(schema) From 1c4b48c9e754f325699a65dcfbf1202eb9f0d7ec Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Mon, 4 Jan 2016 18:58:51 +0100 Subject: [PATCH 48/48] Simplify lilypond check --- patacrep/build.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index 94416383..96a09424 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -148,9 +148,7 @@ class Songbook: def requires_lilypond(self): """Tell if lilypond is part of the bookoptions""" - return ('chords' in self._config - and 'lilypond' in self._config['chords'] - and self._config['chords']['lilypond']) + return 'lilypond' in iter_bookoptions(self._config) def _log_pipe(pipe): """Log content from `pipe`."""