Source code for innoconv_mintmod.mintmod_filter.filter_action

"""Pandoc filter that transforms mintmod commands."""

from os import environ
import panflute as pf
from slugify import slugify

from innoconv_mintmod.errors import ParseError
from innoconv_mintmod.constants import (
    REGEX_PATTERNS, ELEMENT_CLASSES, EXERCISE_CMDS_ENVS)
from innoconv_mintmod.utils import (
    log, destringify, parse_cmd, parse_nested_args)
from innoconv_mintmod.mintmod_filter.environments import Environments
from innoconv_mintmod.mintmod_filter.commands import Commands
from innoconv_mintmod.mintmod_filter.math import handle_math


[docs]class MintmodFilterAction: """The Pandoc filter is defined in this class.""" def __init__(self, debug=False): self._debug = debug self._commands = Commands() self._environments = Environments()
[docs] def filter(self, elem, doc): """ Receive document elements. This method receives document elements from Pandoc and delegates handling of simple subtitutions, mintmod commands and .ments. :param elem: Element to handle :type elem: :class:`panflute.base.Element` :param doc: Document :type doc: :class:`panflute.elements.Doc` """ if elem is None: raise ValueError('elem must not be None!') if doc is None: raise ValueError('doc must not be None!') # simple command subtitutions in Math environments if isinstance(elem, pf.Math): return handle_math(elem) elif hasattr(elem, 'format') and elem.format == 'latex': # block commands and environments if isinstance(elem, pf.RawBlock): cmd_name, cmd_args = parse_cmd(elem.text) try: if cmd_name == 'begin': return self._handle_environment(elem) return self._handle_command(cmd_name, cmd_args, elem) except TypeError as err: self._handle_typeerror(err, cmd_name, cmd_args, elem) # inline commands (no inline environments!) elif isinstance(elem, pf.RawInline): cmd_name, cmd_args = parse_cmd(elem.text) try: return self._handle_command(cmd_name, cmd_args, elem) except TypeError as err: self._handle_typeerror(err, cmd_name, cmd_args, elem)
return None # element unchanged def _handle_command(self, cmd_name, cmd_args, elem): """Parse and handle mintmod commands.""" if (bool(environ.get('INNOCONV_REMOVE_EXERCISES', False)) and cmd_name in EXERCISE_CMDS_ENVS): return [] function_name = 'handle_%s' % slugify(cmd_name) func = getattr(self._commands, function_name, None) if callable(func): return func(cmd_args, elem) if (not bool(environ.get('INNOCONV_IGNORE_EXERCISES', False)) or cmd_name not in EXERCISE_CMDS_ENVS): if len(cmd_name) == 1: log("1-character-command '{}': {}".format(cmd_name, elem), level='WARNING') log("Parent: {}".format(elem.parent)) else: log("Could not handle command %s." % cmd_name, level='WARNING') if self._debug: return self._unknown_command_debug(cmd_name, elem) return None @staticmethod def _unknown_command_debug(cmd_name, elem): """Handle unknown latex commands. Output visual feedback about the unknown command. """ classes = ELEMENT_CLASSES['DEBUG_UNKNOWN_CMD'] + [slugify(cmd_name)] msg_prefix = pf.Strong(*destringify('Unhandled command:')) if isinstance(elem, pf.Block): div = pf.Div(classes=classes) div.content.extend([pf.Para(msg_prefix), pf.CodeBlock(elem.text)]) return div # RawInline span = pf.Span(classes=classes) span.content.extend([msg_prefix, pf.Space(), pf.Code(elem.text)]) return span def _handle_environment(self, elem): """Parse and handle mintmod environments.""" match = REGEX_PATTERNS['ENV'].search(elem.text) if match is None: raise ParseError( 'Could not parse LaTeX environment: %s...' % elem.text[:50]) env_name = match.group('env_name') inner_code = match.groups()[1] if (bool(environ.get('INNOCONV_REMOVE_EXERCISES', False)) and env_name in EXERCISE_CMDS_ENVS): return [] # Parse optional arguments env_args, rest = parse_nested_args(inner_code) function_name = 'handle_%s' % slugify(env_name) func = getattr(self._environments, function_name, None) if callable(func): return func(rest, env_args, elem) if (not bool(environ.get('INNOCONV_IGNORE_EXERCISES', False)) or env_name not in EXERCISE_CMDS_ENVS): log("Could not handle environment %s." % env_name, level='WARNING') if self._debug: return self._unknown_environment_debug(env_name, elem) return None @staticmethod def _unknown_environment_debug(env_name, elem): """Handle unknown latex environment. Output visual feedback about the unknown environment. """ classes = ELEMENT_CLASSES['DEBUG_UNKNOWN_ENV'] + [slugify(env_name)] div = pf.Div(classes=classes) div.content.extend([ pf.Para(pf.Strong(*destringify('Unhandled environment:'))), pf.CodeBlock(elem.text), ]) return div @staticmethod def _handle_typeerror(err, name, args, elem): log('TypeError at command name={} args={} elem={}: {}'.format( name, args, elem.__class__.__name__, err)) import traceback
traceback.print_tb(err.__traceback__)