@ -3,8 +3,8 @@
Content that can be included in a songbook is controlled by plugins . From the
user ( or . sb file ) point of view , each piece of content is introduced by a
keyword . This keywold is associated with a plugin ( a submodule of this very
module ) , which parses the content , and return a list of instances of the
Content class .
module ) , which parses the content , and return a ContentList object , which is
little more than a list of instances of the ContentItem class .
# Plugin definition
@ -27,8 +27,8 @@ A parser is a function which takes as arguments:
- config : the configuration object of the current songbook . Plugins can
change it .
A parser returns a list of instances of the Content class , defined in
this module ( or of subclasses of this class ) .
A parser returns a ContentList object ( a list of instances of the ContentItem
class ) , defined in this module ( or of subclasses of this class ) .
Example : When the following piece of content is met
@ -55,13 +55,13 @@ surrounded by parenthesis. It is up to the plugin to parse this argument. For
intance , keyword " foo()(( bar() " is a perfectly valid keyword , and the parser
associated to " foo " will get as argument the string " )(( bar( " .
# Content class
# ContentItem class
The content classes are subclasses of class Content defined in this module .
Content is a perfectly valid class , but instances of it will not generate
The content classes are subclasses of class ContentItem defined in this module .
ContentItem is a perfectly valid class , but instances of it will not generate
anything in the resulting . tex .
More documentation in the docstring of Content .
More documentation in the docstring of ContentItem .
"""
@ -79,7 +79,7 @@ LOGGER = logging.getLogger(__name__)
EOL = ' \n '
#pylint: disable=no-self-use
class Content :
class ContentItem :
""" Content item. Will render to something in the .tex file.
The current jinja2 . runtime . Context is passed to all function defined
@ -100,8 +100,8 @@ class Content:
# Arguments
- __previous : the songbook . content . Content object of the previous item .
- __context : see Content ( ) documentation .
- __previous : the songbook . content . ContentItem object of the previous item .
- __context : see ContentItem ( ) documentation .
# Return
@ -122,13 +122,37 @@ class Content:
class ContentError ( SongbookError ) :
""" Error in a content plugin. """
def __init__ ( self , keyword , message ) :
def __init__ ( self , keyword = None , message = Non e ) :
super ( ContentError , self ) . __init__ ( )
self . keyword = keyword
self . message = message
def __str__ ( self ) :
return " Content: {} : {} " . format ( self . keyword , self . message )
text = " Content "
if self . keyword is not None :
text + = " : " + self . keyword
if self . message is not None :
text + = " : " + self . message
return text
class ContentList :
""" List of content items """
def __init__ ( self , * args , * * kwargs ) :
self . _content = list ( * args , * * kwargs )
self . errors = [ ]
def __iter__ ( self ) :
yield from self . _content
def extend ( self , iterator ) :
return self . _content . extend ( iterator )
def append ( self , item ) :
return self . _content . append ( item )
def __len__ ( self ) :
return len ( self . _content )
@jinja2 . contextfunction
def render ( context , content ) :
@ -137,14 +161,14 @@ def render(context, content):
Arguments :
- context : the jinja2 . runtime . context of the current template
compilation .
- content : a list of Content ( ) instances , as the one that was returned by
- content : a list of ContentItem ( ) instances , as the one that was returned by
process_content ( ) .
"""
rendered = " "
previous = None
last = None
for elem in content :
if not isinstance ( elem , Content ) :
if not isinstance ( elem , ContentItem ) :
LOGGER . warning ( " Ignoring bad content item ' {} ' . " . format ( elem ) )
continue
@ -156,23 +180,23 @@ def render(context, content):
rendered + = elem . render ( context ) + EOL
previous = elem
if isinstance ( last , Content ) :
if isinstance ( last , ContentItem ) :
rendered + = last . end_block ( context ) + EOL
return rendered
def process_content ( content , config = None ) :
""" Process content, and return a list of Content() objects.
""" Process content, and return a list of ContentItem () objects.
Arguments are :
- content : the content field of the . sb file , which should be a list , and
describe what is to be included in the songbook ;
- config : the configuration dictionary of the current songbook .
Return : a list of Content objects , corresponding to the content to be
Return : a list of ContentItem objects , corresponding to the content to be
included in the . tex file .
"""
contentlist = [ ]
contentlist = ContentList ( )
plugins = config . get ( ' _content_plugins ' , { } )
keyword_re = re . compile ( r ' ^ *(?P<keyword> \ w*) *( \ ((?P<argument>.*) \ ))? *$ ' )
if not content :
@ -185,10 +209,12 @@ def process_content(content, config=None):
try :
match = keyword_re . match ( elem [ 0 ] ) . groupdict ( )
except AttributeError :
raise ContentError ( elem [ 0 ] , " Cannot parse content type. " )
contentlist . errors . append ( ContentError ( elem [ 0 ] , " Cannot parse content type. " ) )
continue
( keyword , argument ) = ( match [ ' keyword ' ] , match [ ' argument ' ] )
if keyword not in plugins :
raise ContentError ( keyword , " Unknown content type. " )
contentlist . errors . append ( ContentError ( keyword , " Unknown content type. " ) )
continue
contentlist . extend ( plugins [ keyword ] (
keyword ,
argument = argument ,