')
return ''.join(s)
+ @render_hook("footnote")
def r_footnote(self, block):
s = []
s.append("")
s.append(self.render_children_blocks(block))
s.append("")
return ''.join(s)
########################################
#Code
+ @render_hook("lstlisting")
def r_lstlisting(self, block):
s = []
if "language" in block.options:
s.append(''.format(block.options["language"]))
else:
#check if there are any lstset block
query = self.parser_tree_explorer.query_block_by_name("lstset")
if len(query) == 0:
s.append('')
else:
s.append(''.format(query[0].options["language"]))
s.append(block.content)
s.append('')
return '\n'.join(s)
#########################################
#Theorems
+ @render_hook("theorem")
def r_theorem(self, block):
#the label in theorems is not working for now
th_definition = block.attributes['definition']
th_title = ''
if block.attributes['title'] != None:
th_title +=" "+ block.attributes['title']
s = []
#adding the theorem to the tree
self.theorem_number += 1
self.tree.addTheorem(str(self.theorem_number), th_definition)
#checking if the Environment template is used
environ = False
if self.configs['lang'] =='it':
if th_definition.lower() == 'teorema':
#adding content to page through a template
s.append("\n{{InizioTeorema|titolo=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{FineTeorema}}\n")
elif th_definition.lower() == 'definizione':
s.append("\n{{InizioDefinizione|titolo=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{FineDefinizione}}\n")
elif th_definition.lower() == 'proposizione':
s.append("\n{{InizioProposizione|titolo=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{FineProposizione}}\n")
elif th_definition.lower() == 'lemma':
s.append("\n{{InizioLemma|title=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{FineLemma}}\n")
elif th_definition.lower() == 'corollario':
s.append("\n{{InizioCorollario|titolo=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{FineCorollario}}\n")
elif th_definition.lower()[:-2] == 'eserciz':
s.append("\n{{InizioEsercizio|titolo=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{FineEsercizio}}\n")
elif th_definition.lower()[:-1] == 'osservazion':
s.append("\n{{InizioOsservazione|titolo=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{FineOsservazione}}\n")
elif th_definition.lower()[:-2] == 'esemp':
s.append("\n{{InizioEsempio|titolo=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{FineEsempio}}\n")
elif th_definition.lower() == 'dimostrazione':
s.append("\n{{InizioDimostrazione|titolo=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{FineDimostrazione}}\n")
else:
s.append("\n{{Environment|name="+ th_definition + \
"|title=" + th_title +\
"|content=")
s.append(self.render_children_blocks(block))
s.append("}}\n")
elif self.configs['lang'] =='en':
if th_definition.lower() == 'theorem':
#adding content to page through a template
s.append("\n{{BeginTheorem|title=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{EndTheorem}}\n")
elif th_definition.lower() == 'definition':
s.append("\n{{BeginDefinition|title=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{EndDefinition}}\n")
elif th_definition.lower() == 'proposition':
s.append("\n{{BeginProposition|title=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{EndProposition}}\n")
elif th_definition.lower() == 'lemma':
s.append("\n{{BeginLemma|title=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{EndLemma}}\n")
elif th_definition.lower() == 'corollary':
s.append("\n{{BeginCorollary|title=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{EndCorollary}}\n")
elif th_definition.lower() == 'exercise':
s.append("\n{{BeginExercise|title=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{EndExercise}}\n")
elif th_definition.lower() == 'observation':
s.append("\n{{BeginObservation|title=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{EndObservation}}\n")
elif th_definition.lower() == 'remark':
s.append("\n{{BeginRemark|title=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{EndRemark}}\n")
elif th_definition.lower() == 'example':
s.append("\n{{BeginExample|title=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{EndExample}}\n")
elif th_definition.lower() == 'demonstration':
s.append("\n{{BeginDemonstration|title=" + \
th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\
"|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}")
s.append(self.render_children_blocks(block))
s.append("{{EndDemonstration}}\n")
else:
s.append("\n{{Environment|name="+ th_definition + \
"|title=" + th_title +\
"|content=")
s.append(self.render_children_blocks(block))
s.append("}}\n")
#exit from theorem ambient
self.tree.exitTheorem()
return '\n'.join(s)
+ @render_hook("proof")
def r_proof(self, block):
s=[]
if self.configs['lang'] == 'it':
if block.title !=None:
s.append('\n{{InizioDimostrazione|titolo='+\
block.attributes['title']+ "}}")
s.append(self.render_children_blocks(block))
s.append("{{FineDimostrazione}}\n")
else:
s.append('\n{{InizioDimostrazione}}')
s.append(self.render_children_blocks(block))
s.append("{{FineDimostrazione}}\n")
elif self.configs['lang'] == 'en':
if block.title !=None:
s.append('\n{{BeginProof|title='+\
block.attributes['title']+"}}")
s.append(self.render_children_blocks(block))
s.append("{{EndProof}}\n")
else:
s.append('\n{{BeginProof}}')
s.append(self.render_children_blocks(block))
s.append("{{EndProof}}\n")
return '\n'.join(s)
#########################################
#ACCENTED letters
+
+ @render_hook("accented_letter")
def r_accented_letter(self, block):
if block.attributes["accent_type"] == '"' \
and block.attributes["letter"] == "a":
return "รค"
if block.attributes["accent_type"] in ["'","`"]:
return block.attributes["letter"]+\
block.attributes["accent_type"]
else:
return block.attributes["letter"]
diff --git a/texla/Renderers/Renderer.py b/texla/Renderers/Renderer.py
index a2a2ee9..c05fb3f 100644
--- a/texla/Renderers/Renderer.py
+++ b/texla/Renderers/Renderer.py
@@ -1,189 +1,225 @@
from ..Parser import Blocks
from ..Parser.TreeExplorer import TreeExplorer
import logging
import importlib
+from functools import wraps
class Renderer():
+ """ Base class for Renderers """
+
def __init__(self, configs, reporter):
self.configs = configs
self.reporter = reporter
#Parser TreeExplorer with parsed blocks tree. It will be filled at start
self.parser_tree_explorer = None
- #hooks implemented directly by the Renderer class.
+ #hooks dictionary
self.render_hooks = {}
+ #Read the render hooks of the Renderer. It reads the hook of the derived Renderer.
+ self.parse_render_hooks()
#plugins hooks
self.pre_render_hooks = {}
self.post_render_hooks = {}
self.start_hooks = []
self.end_hooks = []
self.loaded_plugins = {}
#registering plugins from the configs
self.register_plugins()
def register_plugins(self):
+ """This function loads the plugins declared in the configuration."""
for plugin in self.configs["plugins"]:
module = importlib.import_module("..plugins"+'.'+ plugin, __name__)
if hasattr(module, "plugin_render_hooks"):
self.loaded_plugins[plugin] = module
self.register_render_plugin_hooks(module.plugin_render_hooks)
logging.debug("Renderer.register_plugins "\
"@ Loaded plugin: {}".format(plugin))
logging.debug("Plugin {} render hooks: {}".format( plugin,
list(module.plugin_render_hooks.keys())))
if hasattr(module, "plugin_lifecycle_hooks"):
self.register_lifecyle_plugin_hooks(module.plugin_lifecycle_hooks)
logging.debug("Plugin {} lifecycle hooks: {}".format( plugin,
list(module.plugin_lifecycle_hooks.keys())))
#adding the configurations to the plugin
if "plugins_configs" in self.configs:
if plugin in self.configs["plugins_configs"]:
logging.debug("Plugin {} passing configs...".format(plugin))
module.configs = self.configs["plugins_configs"][plugin]
def register_render_plugin_hooks(self, hooks):
'''This function registers the hooks for renderer plugins.
The plugins can define hooks for pre and post render actions.
The pre hook receives the block before the rendering and can
only return the block itself, modified.
The post hook receive the block and the text from the renderer:
it has to return the final text only.
The keyword ALL creates a hooks for all the blocks.
Note that it is always called after all the other hooks.'''
for bl in hooks:
if "pre" in hooks[bl]:
self.register_pre_renderer_hook(bl, hooks[bl]["pre"])
if "post" in hooks[bl]:
self.register_post_renderer_hook(bl, hooks[bl]["post"])
#checking ALL keyword
if "ALL" in hooks:
if "pre" in hooks["ALL"]:
self.register_pre_renderer_hook(bl, hooks["ALL"]["pre"])
if "post" in hooks["ALL"]:
self.register_post_renderer_hook(bl, hooks["ALL"]["post"])
def register_lifecyle_plugin_hooks(self, hooks):
''' This function registers the hooks for the renderer lifecycle.
Plugins can register hooks for the start and end actions.
The start hook is called with the root_block of the chain.
The end hook is called without arguments. These hooks must be used
only to signal the actions to the plugins.'''
if "start" in hooks:
self.register_start_hook(hooks["start"])
if "end" in hooks:
self.register_end_hook(hooks["end"])
def register_pre_renderer_hook(self, block, hook):
if block not in self.pre_render_hooks:
self.pre_render_hooks[block] = []
self.pre_render_hooks[block].append(hook)
def register_post_renderer_hook(self, block, hook):
if block not in self.post_render_hooks:
self.post_render_hooks[block] = []
self.post_render_hooks[block].append(hook)
def register_start_hook(self, hook):
self.start_hooks.append(hook)
def register_end_hook(self, hook):
self.end_hooks.append(hook)
+ def parse_render_hooks(self):
+ """
+ The function scans the Renderer (sub)class to find the functions
+ annotated with @render_hooks(list_of_block_names). It inserts
+ the function in the render_hooks using the provided block_names.
+ """
+ for member_name in dir(self):
+ member = getattr(self, member_name)
+ if hasattr(member, "block_names"):
+ for hook in getattr(member, "block_names"):
+ logging.debug("Renderer @ render_hook registered: {} -> {}"
+ .format(hook, member_name))
+ self.render_hooks[hook] = member
+
def start_rendering(self, parser_tree_explorer):
'''
Entrypoing for the rendering process.
This function requests the TreeExplorer containing the parsed blocks
and passes it to the plugins that have the variable
needs_tree_explorer=True. Then it starts the plugins'''
self.parser_tree_explorer = parser_tree_explorer
#passing the tree_explorer
for pl in self.loaded_plugins.values():
if hasattr(pl, "needs_tree_explorer"):
if pl.needs_tree_explorer:
logging.debug("Renderer @ Inserting "\
"TreeExplorer into plugin {}".format(pl))
pl.tree_explorer = self.parser_tree_explorer
#starting the plugins
for hook in self.start_hooks:
hook()
def end_rendering(self):
#ending plugins
for hook in self.end_hooks:
hook()
def render_children_blocks(self, block, collapse=True):
'''This is one of the most important funciont
of the rendering process.
This function takes all the children blocks of
a block and get they rendering output.
If collapsed=True it returns a unique string,
otherwise it returns a list of tuples with[(block_name, output)]
'''
output = []
for bl in block.ch_blocks:
#it's not necessary checking for renderer_hook
#because default hook is mandatory
output.append((bl.block_name, self.render_block(bl)))
if collapse:
return ''.join([x[1] for x in output])
else:
return output
def render_block(self, bl):
'''This function calls the right render_hook for
the block. If there isn't an hook it calld the default,
that is mandatory'''
output = ""
######### pre hooks ############
#hooks executed in the order of inserction
#They receive the block and they can only modify the block object
if bl.block_name in self.pre_render_hooks:
for prehook in self.pre_render_hooks[bl.block_name]:
#calling prehook with the block
prehook(bl)
#calling after the others the ALL hooks
if "ALL" in self.pre_render_hooks:
for prehook in self.pre_render_hooks["ALL"]:
#calling prehook with the block
prehook(bl)
######## rendering #############
if bl.block_name in self.render_hooks:
logging.debug('Render @ block: ' + bl.block_name)
output = self.render_hooks[bl.block_name](bl)
else:
#default hook is mandatory
logging.debug('Render @ block: default@' + bl.block_name)
#reporting to the Reporter
if bl.block_name != "default":
self.reporter.add_not_rendered_block(bl)
output = self.render_hooks['default'](bl)
######## post hooks ###########
#hooks executed in the order of inserction.
#They receive the block and text. They have to return the
#output text, that is passed to the next posthook
if bl.block_name in self.post_render_hooks:
for posthook in self.post_render_hooks[bl.block_name]:
#calling posthook with the block and output
output = posthook(bl, output)
#calling ALL hooks after the others
if "ALL" in self.post_render_hooks:
for posthook in self.post_render_hooks["ALL"]:
#calling posthook with the block and output
output = posthook(bl, output)
#final output
return output
def render_blocks(self, bls, collapse=False):
'''This function renderes a list of blocks.
It's the same as render_children_blocks but
with a generic list'''
output = []
for bl in bls:
output.append((bl.block_name,self.render_block(bl)))
if collapse:
return ''.join([x[1] for x in output])
else:
return output
+
+
+###############################################################################
+# Decorators for renderers
+
+def render_hook(*block_names):
+ """This decorate assigns to a function the list of block_names
+ that it will handle as a render_hook."""
+ def decorate(func):
+ #adding the list of block names as an attribute of the function
+ setattr(func, "block_names", block_names)
+ @wraps(func)
+ def wrapper(*args,**kwargs):
+ return func(*args, **kwargs)
+ return wrapper
+ return decorate