class TestStatusMessage(unittest.TestCase): def setUp(self): self.extractor = LineExtractor("") self.message = StatusMessage(self.extractor) def testError(self): if IGNORE_TEST: return with self.assertRaises(ValueError): self.message.error("") def testWarning(self): if IGNORE_TEST: return with warnings.catch_warnings(record=True) as w: # Cause all warnings to always be triggered. self.message.warning("") # Verify some things assert len(w) == 1
class TemplateProcessor(object): """ This class processes an Antimony model written using template variable substitutions. See the project README for syntax details. """ def __init__(self, template_string): """ :param str template_string: string containing template variables and template escape statements to execute """ self._extractor = LineExtractor(template_string) self._message = StatusMessage(self._extractor) self._executor = Executor() self._expander = Expander(self._executor, self._message) self._command = None # Command being processed self._define_variable_statements = [] @classmethod def processFile(cls, inpath, outpath): """ Processes template strings in a file. :param str inpath: path to the file containing the templated model :param str outpath: path to the file where the flattened model is placed """ template = '' with open(inpath, 'r') as infile: for line in infile: template += "\n" + line processor = cls(template) expansion = processor.do() with open(outpath, 'w') as outfile: outfile.write(expansion) @staticmethod def _makeComment(line): return "%s%s" % (COMMENT_STG, line) def _processCommand(self): """ Handles command processing, either the current line is a command or in the midst of processing a paired command. :param list-of-str expansion: :return bool: True if processed line """ line = self._extractor.getCurrentLine() line_type = self._extractor.getCurrentLineType() is_processed = False # Current line is a command if line_type == LINE_COMMAND: is_processed = True # Check for nested commands if self._command is not None: new_command = Command(line) # Is this a paired command? if new_command.getCommandVerb() \ == self._command.getCommandVerb(): if new_command.isEnd(): pass else: self._message.error("Cannot nest commands") # Valid placement for a command. self._command = Command(line) # DefineVariables Command if self._command.isDefineVariables(): if self._command.isBegin(): self._define_variables_statements = [] elif self._command.isEnd(): try: program = '\n'.join(self._define_variables_statements) self._executor.doScript(program) except Exception as err: msg = "***Error %s executing in : \n%s" \ % (str(err), program) self._message.error(msg) self._command = None # SetVersion command elif self._command.isSetVersion(): version = self._command.getArguments()[0] if float(version) > float(VERSION): self._message.error("Unsupported version %s" % version) self._command = None # Other commands else: self._message.error("Unknown command") # Process statements occurring within paired commands elif self._command is not None: is_processed = True if self._command.isDefineVariables() and self._command.isBegin(): self._define_variables_statements.append(line) else: self._message.error("Invalid paired command.") return is_processed def do(self): """ Processes the template string and returns the expanded lines for input to road runner. Phases 1. Construct content lines (non-blank, not comments) 2. Extract the template variable definitions 3. Construct the substitution instances 4. Process the lines with template variables State used: reads: _definitions :return str expanded_string: :raises ValueError: errors encountered in the template string """ cls = TemplateProcessor expansion = [] line, line_type = self._extractor.do() statements = [] while line is not None: if self._processCommand(): expansion.append(cls._makeComment(line)) # No command being processed else: # Transparent line (comment) if line_type == LINE_TRAN: expansion.append(line) elif line_type == LINE_NONE: pass # Line to be substituted elif line_type == LINE_SUBS: # Do the variable substitutions try: substitutions = self._expander.do(line) except Exception as err: msg = "Runtime error in expression" self._message.error(msg) if len(substitutions) > 1: expansion.append(cls._makeComment(line)) expansion.extend(substitutions) else: import pdb pdb.set_trace() raise RuntimeError("Unexepcted state") line, line_type = self._extractor.do() if self._command is not None: msg = "Still processing command %s at EOF" % str(self._command) self._message.error(msg) return "\n".join(expansion)