Browse Source

Errors are correctly gathered from content items

pull/176/head
Louis 9 years ago
parent
commit
1d2a285536
  1. 4
      patacrep/build.py
  2. 31
      patacrep/content/__init__.py
  3. 20
      patacrep/content/include.py
  4. 21
      patacrep/content/section.py
  5. 8
      patacrep/content/song.py
  6. 17
      patacrep/content/songsection.py
  7. 6
      patacrep/content/sorted.py
  8. 10
      patacrep/content/tex.py
  9. 8
      patacrep/latex/syntax.py

4
patacrep/build.py

@ -135,7 +135,9 @@ class Songbook:
def iter_errors(self): def iter_errors(self):
"""Iterate over errors of book and book content.""" """Iterate over errors of book and book content."""
yield from self._errors yield from self._errors
for item in self._config.get('content', list()): content = self._config.get('content', list())
yield from content.iter_errors()
for item in content:
if not hasattr(item, "iter_errors"): if not hasattr(item, "iter_errors"):
continue continue
yield from item.iter_errors() yield from item.iter_errors()

31
patacrep/content/__init__.py

@ -73,7 +73,7 @@ import re
import sys import sys
from patacrep import files from patacrep import files
from patacrep.errors import SongbookError from patacrep.errors import SharedError
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
EOL = '\n' EOL = '\n'
@ -120,7 +120,7 @@ class ContentItem:
"""Return the string to end a block.""" """Return the string to end a block."""
return "" return ""
class ContentError(SongbookError): class ContentError(SharedError):
"""Error in a content plugin.""" """Error in a content plugin."""
def __init__(self, keyword=None, message=None): def __init__(self, keyword=None, message=None):
super(ContentError, self).__init__() super(ContentError, self).__init__()
@ -140,13 +140,15 @@ class ContentList:
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._content = list(*args, **kwargs) self._content = list(*args, **kwargs)
self.errors = [] self._errors = []
def __iter__(self): def __iter__(self):
yield from self._content yield from self._content
def extend(self, iterator): def extend(self, iterator):
return self._content.extend(iterator) self._content.extend(iterator)
if isinstance(iterator, self.__class__):
self._errors.extend(iterator._errors)
def append(self, item): def append(self, item):
return self._content.append(item) return self._content.append(item)
@ -154,6 +156,23 @@ class ContentList:
def __len__(self): def __len__(self):
return len(self._content) return len(self._content)
def append_error(self, error):
LOGGER.warning(error)
self._errors.append(error)
def extend_error(self, errors):
for error in errors:
self.append_error(error)
def iter_errors(self):
yield from self._errors
class EmptyContentList(ContentList):
def __init__(self, *, errors):
super().__init__()
for error in errors:
self.append_error(error)
@jinja2.contextfunction @jinja2.contextfunction
def render(context, content): def render(context, content):
"""Render the content of the songbook as a LaTeX code. """Render the content of the songbook as a LaTeX code.
@ -209,11 +228,11 @@ def process_content(content, config=None):
try: try:
match = keyword_re.match(elem[0]).groupdict() match = keyword_re.match(elem[0]).groupdict()
except AttributeError: except AttributeError:
contentlist.errors.append(ContentError(elem[0], "Cannot parse content type.")) contentlist.append_error(ContentError(elem[0], "Cannot parse content type."))
continue continue
(keyword, argument) = (match['keyword'], match['argument']) (keyword, argument) = (match['keyword'], match['argument'])
if keyword not in plugins: if keyword not in plugins:
contentlist.errors.append(ContentError(keyword, "Unknown content type.")) contentlist.append_error(ContentError(keyword, "Unknown content type."))
continue continue
contentlist.extend(plugins[keyword]( contentlist.extend(plugins[keyword](
keyword, keyword,

20
patacrep/content/include.py

@ -9,7 +9,7 @@ import os
import sys import sys
import logging import logging
from patacrep.content import process_content, ContentError from patacrep.content import process_content, ContentError, ContentList
from patacrep import encoding, errors, files from patacrep import encoding, errors, files
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -38,10 +38,14 @@ def parse(keyword, config, argument, contentlist):
- argument: None; - argument: None;
- contentlist: a list of file paths to be included. - contentlist: a list of file paths to be included.
""" """
new_contentlist = [] new_contentlist = ContentList()
for path in contentlist: for path in contentlist:
filepath = load_from_datadirs(path, config.get('datadir', [])) try:
filepath = load_from_datadirs(path, config.get('datadir', []))
except ContentError as error:
new_contentlist.append_error(error)
continue
content_file = None content_file = None
try: try:
with encoding.open_read( with encoding.open_read(
@ -50,12 +54,14 @@ def parse(keyword, config, argument, contentlist):
) as content_file: ) as content_file:
new_content = json.load(content_file) new_content = json.load(content_file)
except Exception as error: # pylint: disable=broad-except except Exception as error: # pylint: disable=broad-except
LOGGER.warning(error) new_contentlist.append_error(ContentError(
LOGGER.warning("Error while loading file '{}'.".format(filepath)) keyword="include",
sys.exit(1) message="Error while loading file '{}': {}".format(filepath, error),
))
continue
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) new_contentlist.extend(process_content(new_content, config))
config["datadir"].pop() config["datadir"].pop()
return new_contentlist return new_contentlist

21
patacrep/content/section.py

@ -1,6 +1,6 @@
"""Allow LaTeX sections (starred or not) as content of a songbook.""" """Allow LaTeX sections (starred or not) as content of a songbook."""
from patacrep.content import ContentItem, ContentError from patacrep.content import ContentItem, ContentError, ContentList, EmptyContentList
KEYWORDS = [ KEYWORDS = [
"part", "part",
@ -41,14 +41,17 @@ def parse(keyword, argument, contentlist, config):
and long) of the section; and long) of the section;
- config: configuration dictionary of the current songbook. - config: configuration dictionary of the current songbook.
""" """
if (keyword not in KEYWORDS) and (len(contentlist) != 1): try:
raise ContentError( if (keyword not in KEYWORDS) and (len(contentlist) != 1):
keyword, raise ContentError(
"Starred section names must have exactly one argument." keyword,
) "Starred section names must have exactly one argument."
if (len(contentlist) not in [1, 2]): )
raise ContentError(keyword, "Section can have one or two arguments.") if (len(contentlist) not in [1, 2]):
return ContentList(Section(keyword, *contentlist)) raise ContentError(keyword, "Section can have one or two arguments.")
return ContentList([Section(keyword, *contentlist)])
except ContentError as error:
return EmptyContentList(errors=[error])
CONTENT_PLUGINS = dict([ CONTENT_PLUGINS = dict([

8
patacrep/content/song.py

@ -87,14 +87,13 @@ def parse(keyword, argument, contentlist, config):
LOGGER.debug('Parsing file "{}"'.format(filename)) LOGGER.debug('Parsing file "{}"'.format(filename))
extension = filename.split(".")[-1] extension = filename.split(".")[-1]
if extension not in plugins: if extension not in plugins:
LOGGER.warning( songlist.append_error(ContentError(message=(
(
'I do not know how to parse "{}": name does ' 'I do not know how to parse "{}": name does '
'not end with one of {}. Ignored.' 'not end with one of {}. Ignored.'
).format( ).format(
os.path.join(songdir.datadir, filename), os.path.join(songdir.datadir, filename),
", ".join(["'.{}'".format(key) for key in plugins.keys()]), ", ".join(["'.{}'".format(key) for key in plugins.keys()]),
)) )))
continue continue
try: try:
renderer = SongRenderer(plugins[extension]( renderer = SongRenderer(plugins[extension](
@ -103,8 +102,7 @@ def parse(keyword, argument, contentlist, config):
datadir=songdir.datadir, datadir=songdir.datadir,
)) ))
except ContentError as error: except ContentError as error:
# TODO songlist.append_error(error)
songlist.errors.append(error)
continue continue
songlist.append(renderer) songlist.append(renderer)
config["_langs"].add(renderer.song.lang) config["_langs"].add(renderer.song.lang)

17
patacrep/content/songsection.py

@ -1,6 +1,6 @@
"""Allow 'songchapter' and 'songsection' as content of a songbook.""" """Allow 'songchapter' and 'songsection' as content of a songbook."""
from patacrep.content import ContentItem, ContentError from patacrep.content import ContentItem, ContentError, ContentList, EmptyContentList
KEYWORDS = [ KEYWORDS = [
"songchapter", "songchapter",
@ -29,12 +29,15 @@ def parse(keyword, argument, contentlist, config):
- contentlist: a list of one string, which is the name of the section; - contentlist: a list of one string, which is the name of the section;
- config: configuration dictionary of the current songbook. - config: configuration dictionary of the current songbook.
""" """
if (keyword not in KEYWORDS) and (len(contentlist) != 1): try:
raise ContentError( if (keyword not in KEYWORDS) and (len(contentlist) != 1):
keyword, raise ContentError(
"Starred section names must have exactly one argument.", keyword,
) "Starred section names must have exactly one argument.",
return ContentList(SongSection(keyword, contentlist[0])) )
return ContentList([SongSection(keyword, contentlist[0])])
except ContentError as error:
return EmptyContentList(errors=[error])
CONTENT_PLUGINS = dict([ CONTENT_PLUGINS = dict([

6
patacrep/content/sorted.py

@ -9,7 +9,7 @@ import logging
import unidecode import unidecode
from patacrep import files from patacrep import files
from patacrep.content import ContentError from patacrep.content import ContentError, ContentList
from patacrep.content.song import OnlySongsError, process_songs from patacrep.content.song import OnlySongsError, process_songs
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -85,11 +85,11 @@ def parse(keyword, config, argument, contentlist):
try: try:
songlist = process_songs(contentlist, config) songlist = process_songs(contentlist, config)
except OnlySongsError as error: except OnlySongsError as error:
raise ContentError(keyword, ( return EmptyContentError(errors=[ContentError(keyword, (
"Content list of this keyword can be only songs (or content " "Content list of this keyword can be only songs (or content "
"that result into songs), and the following are not:" + "that result into songs), and the following are not:" +
str(error.not_songs) str(error.not_songs)
)) ))])
return sorted(songlist, key=key_generator(sort)) return sorted(songlist, key=key_generator(sort))
CONTENT_PLUGINS = {'sorted': parse} CONTENT_PLUGINS = {'sorted': parse}

10
patacrep/content/tex.py

@ -5,7 +5,7 @@ import logging
import os import os
from patacrep import files, errors from patacrep import files, errors
from patacrep.content import ContentItem, ContentList from patacrep.content import ContentItem, ContentList, ContentError
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -35,7 +35,7 @@ def parse(keyword, argument, contentlist, config):
LOGGER.warning( LOGGER.warning(
"Useless 'tex' content: list of files to include is empty." "Useless 'tex' content: list of files to include is empty."
) )
filelist = ContentList filelist = ContentList()
basefolders = itertools.chain( basefolders = itertools.chain(
(path.fullpath for path in config['_songdir']), (path.fullpath for path in config['_songdir']),
files.iter_datadirs(config['datadir']), files.iter_datadirs(config['datadir']),
@ -51,9 +51,9 @@ def parse(keyword, argument, contentlist, config):
)) ))
break break
if not checked_file: if not checked_file:
LOGGER.warning( filelist.append_error(ContentError(
"{} Compilation may fail later.".format( keyword="tex",
errors.notfound(filename, basefolders) message=errors.notfound(filename, basefolders),
) )
) )
continue continue

8
patacrep/latex/syntax.py

@ -131,13 +131,15 @@ class LatexParser(Parser):
"""dictionary : identifier EQUAL braces dictionary_next """dictionary : identifier EQUAL braces dictionary_next
| identifier EQUAL error dictionary_next | identifier EQUAL error dictionary_next
""" """
symbols[0] = {}
if isinstance(symbols[3], ast.Expression): if isinstance(symbols[3], ast.Expression):
symbols[0] = {}
symbols[0][symbols[1]] = symbols[3] symbols[0][symbols[1]] = symbols[3]
symbols[0].update(symbols[4]) symbols[0].update(symbols[4])
else: else:
# TODO put error in self.errors self.error(
raise ParsingError("Do enclose arguments between braces.") line=symbols.lexer.lineno,
message="Argument '{}' should be enclosed between braces.".format(symbols[1]),
)
@staticmethod @staticmethod
def p_identifier(symbols): def p_identifier(symbols):

Loading…
Cancel
Save