Browse Source

Validate plugin argument with a decorator

pull/190/head
Oliverpool 9 years ago
parent
commit
fbdafd0162
  1. 65
      patacrep/content/__init__.py
  2. 15
      patacrep/content/cwd.py
  3. 17
      patacrep/content/include.py
  4. 23
      patacrep/content/section.py
  5. 14
      patacrep/content/song.py
  6. 9
      patacrep/content/songsection.py
  7. 31
      patacrep/content/sorted.py
  8. 17
      patacrep/content/tex.py

65
patacrep/content/__init__.py

@ -68,7 +68,7 @@ import sys
import jinja2 import jinja2
import yaml import yaml
from patacrep import files, utils from patacrep import files, Rx, utils
from patacrep.errors import SBFileError, SharedError from patacrep.errors import SBFileError, SharedError
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -223,37 +223,31 @@ def render(context, content):
return rendered return rendered
def build_plugin_schema(plugins): def validate_parser_argument(raw_schema):
"""Builds the Rx schema for the ContentItem""" """Check that the parser argument respects the schema
plugin_schemas = {}
for keyword, parser in plugins.items(): Will raise `SBFileError` if the schema is not respected.
subschema = getattr(parser, 'rxschema', '//any') """
plugin_schemas[keyword] = yaml.load(subschema) rx_checker = Rx.Factory({"register_core_types": True})
plugin_schema = [{ schema = rx_checker.make_schema(yaml.load(raw_schema))
'type': '//rec',
'optional': plugin_schemas, def wrap(parse):
}] """Wrap the parse function"""
song_schema = { def wrapped(keyword, argument, config):
'type': '//str', """Check the argument schema before calling the plugin parser"""
} try:
plugin_schema.append(song_schema) schema.validate(argument)
return { except Rx.SchemaMismatch as exception:
'type': '//any', msg = 'Invalid `{}` syntax:\n---\n{}---\n{}'.format(
'of': plugin_schema, keyword,
} yaml.dump({keyword: argument}, default_flow_style=False),
str(exception)
)
raise SBFileError(msg)
return parse(keyword, argument=argument, config=config)
return wrapped
return wrap
def validate_content(content, plugins):
"""Validate the content against the Rx content schema"""
plugin_schema = build_plugin_schema(plugins)
content_schema = {
'type': '//any',
'of': [
plugin_schema,
{'type': '//arr', 'contents':plugin_schema},
#{'type': '//nil'},
]
}
utils.validate_yaml_schema(content, content_schema)
def process_content(content, config=None): def process_content(content, config=None):
"""Process content, and return a list of ContentItem() objects. """Process content, and return a list of ContentItem() objects.
@ -269,19 +263,14 @@ def process_content(content, config=None):
contentlist = ContentList() contentlist = ContentList()
plugins = config.get('_content_plugins', {}) plugins = config.get('_content_plugins', {})
if not content: if not content:
content = [{'song': ""}] content = [{'song': None}]
try:
validate_content(content, plugins)
except SBFileError as error:
contentlist.append_error(ContentError("Invalid content", str(error)))
return contentlist
for elem in content: for elem in content:
if isinstance(elem, str): if isinstance(elem, str):
elem = {'song': [elem]} elem = {'song': [elem]}
if isinstance(elem, dict): if isinstance(elem, dict):
for keyword, argument in elem.items(): for keyword, argument in elem.items():
if keyword not in plugins: if keyword not in plugins:
contentlist.append_error(ContentError(keyword, "Unknown content type.")) contentlist.append_error(ContentError(keyword, "Unknown content keyword."))
continue continue
contentlist.extend(plugins[keyword]( contentlist.extend(plugins[keyword](
keyword, keyword,

15
patacrep/content/cwd.py

@ -1,9 +1,15 @@
"""Change base directory before importing songs.""" """Change base directory before importing songs."""
from patacrep.content import process_content from patacrep.content import process_content, validate_parser_argument
from patacrep.songs import DataSubpath from patacrep.songs import DataSubpath
#pylint: disable=unused-argument #pylint: disable=unused-argument
@validate_parser_argument("""
type: //rec
required:
path: //str
content: //any
""")
def parse(keyword, config, argument): def parse(keyword, config, argument):
"""Return a list songs, whith a different base path. """Return a list songs, whith a different base path.
@ -33,11 +39,4 @@ def parse(keyword, config, argument):
config['_songdir'] = old_songdir config['_songdir'] = old_songdir
return processed_content return processed_content
parse.rxschema = """
type: //rec
required:
path: //str
content: //any
"""
CONTENT_PLUGINS = {'cwd': parse} CONTENT_PLUGINS = {'cwd': parse}

17
patacrep/content/include.py

@ -8,7 +8,7 @@ import json
import os import os
import logging import logging
from patacrep.content import process_content, ContentError, ContentList from patacrep.content import process_content, ContentError, ContentList, validate_parser_argument
from patacrep import encoding, errors, files from patacrep import encoding, errors, files
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -28,6 +28,13 @@ def load_from_datadirs(path, datadirs):
) )
#pylint: disable=unused-argument #pylint: disable=unused-argument
@validate_parser_argument("""
type: //any
of:
- type: //str
- type: //arr
contents: //str
""")
def parse(keyword, config, argument): def parse(keyword, config, argument):
"""Include an external file content. """Include an external file content.
@ -69,12 +76,4 @@ def parse(keyword, config, argument):
return new_contentlist return new_contentlist
parse.rxschema = """
type: //any
of:
- type: //str
- type: //arr
contents: //str
"""
CONTENT_PLUGINS = {'include': parse} CONTENT_PLUGINS = {'include': parse}

23
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, ContentList from patacrep.content import ContentItem, ContentList, validate_parser_argument
KEYWORDS = [ KEYWORDS = [
"part", "part",
@ -29,6 +29,16 @@ class Section(ContentItem):
return r'\{}[{}]{{{}}}'.format(self.keyword, self.short, self.name) return r'\{}[{}]{{{}}}'.format(self.keyword, self.short, self.name)
#pylint: disable=unused-argument #pylint: disable=unused-argument
@validate_parser_argument("""
type: //any
of:
- type: //str
- type: //rec
required:
name: //str
optional:
short: //str
""")
def parse(keyword, argument, config): def parse(keyword, argument, config):
"""Parse the section. """Parse the section.
@ -47,17 +57,6 @@ def parse(keyword, argument, config):
argument = {'name': argument} argument = {'name': argument}
return ContentList([Section(keyword, **argument)]) return ContentList([Section(keyword, **argument)])
parse.rxschema = """
type: //any
of:
- type: //str
- type: //rec
required:
name: //str
optional:
short: //str
"""
CONTENT_PLUGINS = dict([ CONTENT_PLUGINS = dict([
(word, parse) (word, parse)
for word for word

14
patacrep/content/song.py

@ -7,7 +7,7 @@ import textwrap
import jinja2 import jinja2
from patacrep.content import process_content from patacrep.content import process_content, validate_parser_argument
from patacrep.content import ContentError, ContentItem, ContentList from patacrep.content import ContentError, ContentItem, ContentList
from patacrep import files, errors from patacrep import files, errors
@ -58,6 +58,14 @@ class SongRenderer(ContentItem):
return self.song.fullpath < other.song.fullpath return self.song.fullpath < other.song.fullpath
#pylint: disable=unused-argument #pylint: disable=unused-argument
@validate_parser_argument("""
type: //any
of:
- type: //nil
- type: //str
- type: //arr
contents: //str
""")
def parse(keyword, argument, config): def parse(keyword, argument, config):
"""Parse data associated with keyword 'song'. """Parse data associated with keyword 'song'.
@ -121,10 +129,6 @@ def parse(keyword, argument, config):
)) ))
return sorted(songlist) return sorted(songlist)
parse.rxschema = """
type: //str
"""
CONTENT_PLUGINS = {'song': parse} CONTENT_PLUGINS = {'song': parse}

9
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, ContentList from patacrep.content import ContentItem, ContentList, validate_parser_argument
KEYWORDS = [ KEYWORDS = [
"songchapter", "songchapter",
@ -20,6 +20,9 @@ class SongSection(ContentItem):
return r'\{}{{{}}}'.format(self.keyword, self.name) return r'\{}{{{}}}'.format(self.keyword, self.name)
#pylint: disable=unused-argument #pylint: disable=unused-argument
@validate_parser_argument("""
//str
""")
def parse(keyword, argument, config): def parse(keyword, argument, config):
"""Parse the songsection. """Parse the songsection.
@ -30,10 +33,6 @@ def parse(keyword, argument, config):
""" """
return ContentList([SongSection(keyword, argument)]) return ContentList([SongSection(keyword, argument)])
parse.rxschema = """
//str
"""
CONTENT_PLUGINS = dict([ CONTENT_PLUGINS = dict([
(keyword, parse) (keyword, parse)
for keyword for keyword

31
patacrep/content/sorted.py

@ -9,7 +9,7 @@ import unidecode
from patacrep import files from patacrep import files
from patacrep.content import ContentError, EmptyContentList from patacrep.content import ContentError, EmptyContentList
from patacrep.content import process_content from patacrep.content import process_content, validate_parser_argument
from patacrep.content.song import OnlySongsError from patacrep.content.song import OnlySongsError
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -67,6 +67,20 @@ def key_generator(sort):
return ordered_song_keys return ordered_song_keys
#pylint: disable=unused-argument #pylint: disable=unused-argument
@validate_parser_argument("""
type: //any
of:
- type: //nil
- type: //rec
optional:
key:
type: //any
of:
- //str
- type: //arr
contents: //str
content: //any
""")
def parse(keyword, config, argument): def parse(keyword, config, argument):
"""Return a sorted list of songs. """Return a sorted list of songs.
@ -94,19 +108,4 @@ def parse(keyword, config, argument):
))]) ))])
return sorted(songlist, key=key_generator(sort)) return sorted(songlist, key=key_generator(sort))
parse.rxschema = """
type: //any
of:
- type: //nil
- type: //rec
optional:
key:
type: //any
of:
- //str
- type: //arr
contents: //str
content: //any
"""
CONTENT_PLUGINS = {'sorted': parse} CONTENT_PLUGINS = {'sorted': parse}

17
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, ContentError from patacrep.content import ContentItem, ContentList, ContentError, validate_parser_argument
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -22,6 +22,13 @@ class LaTeX(ContentItem):
))) )))
#pylint: disable=unused-argument #pylint: disable=unused-argument
@validate_parser_argument("""
type: //any
of:
- type: //arr
contents: //str
- type: //str
""")
def parse(keyword, argument, config): def parse(keyword, argument, config):
"""Parse the tex files. """Parse the tex files.
@ -62,12 +69,4 @@ def parse(keyword, argument, config):
return filelist return filelist
parse.rxschema = """
type: //any
of:
- type: //arr
contents: //str
- type: //str
"""
CONTENT_PLUGINS = {'tex': parse} CONTENT_PLUGINS = {'tex': parse}

Loading…
Cancel
Save