def _makeEpilogue(self): """ Adds code that only runs at the end :return str: statements """ sa = StatementAccumulator() sa.add("") sa.add("%s.controller.startBlock('Epilogue')" % API_OBJECT) sa.add(self._table.getEpilogue().getFormula()) sa.add("%s.controller.endBlock()" % API_OBJECT) return sa.get()
def _makeVariablePrintStatements(self): """ Creates the print statements for all variables in the table. For each variable A: print ('A = %s' % str(A)) :return str statements: """ sa = StatementAccumulator() for column in self._table.getColumns(): statement = """print ('%%s = %%s' %% ("%s", str(%s))) """ % (column.getName(), column.getName()) sa.add(statement) return sa.get()
def _makeClosingException(self, is_absolute_linenumber=True): """ Raises exceptions that have been recorded. :param bool is_absolute_linenumber: """ sa = StatementAccumulator() statement = """ if %s.controller.getException() is not None: raise Exception(%s.controller.formatError(is_absolute_linenumber=%s))""" % ( API_OBJECT, API_OBJECT, is_absolute_linenumber, ) sa.add(statement) return sa.get()
def _makeClosingOfFormulaEvaluationLoop(self, **kwargs): """ Creates the statements at the end of the formula evaluation loop. 1. Statements that update objects 2. Assignments of variables to columns 3. Loop termination checks """ sa = StatementAccumulator() sa.add("") sa.add("%s.controller.endAnIteration()" % API_OBJECT) sa.add("") return sa.get()
def _makeFormulaImportStatements(self, directory, import_path=""): """ Construct import statements for the imports implied by the functions used in a formula. The approach taken isn't very robust: 1. Find all .py files in the user's python directory. The function name should be the same as the file name. 2. If a formula contains the file name (function name), then an import is generated. :param str directory: directory to search :param str import_path: path for the import :return str: import statements for files in the user directory """ formulas = [c.getFormula() for c in self._table.getColumns() if not (c.getFormula() is None)] formulas.append(self._table.getPrologue().getFormula()) formulas.append(self._table.getEpilogue().getFormula()) python_filenames = self._findFilenames(directory) # Determine which files are referenced in a formula referenced_filenames = [] for name in python_filenames: for formula in formulas: if name in formula: referenced_filenames.append(name) break # Construct the import statements sa = StatementAccumulator() for name in referenced_filenames: if len(import_path) == 0: sa.add("from %s import %s" % (name, name)) else: sa.add("from %s.%s import %s" % (import_path, name, name)) return sa.get()
def _makeColumnValuesAssignmentStatements(self, **kwargs): """ Creates statements that assign column values to variables. Note that the assumption is that the variable name is the same as the column name """ sa = StatementAccumulator() sa.add("\n") sa.add("# Assign program variables to columns values.") columns = self._getSelectedColumns(**kwargs) for column in columns: name = column.getName(is_global_name=False) statement = "%s.setColumnValue('%s', %s)" % (API_OBJECT, name, name) sa.add(statement) return sa.get()
def _makeVariableAssignmentStatements(self, prefix="", **kwargs): """ Creates statements that assign column values to variables. :param str prefix: prefix to construct full API object :return str: assignment statements """ sa = StatementAccumulator() sa.add("# Assign column values to program variables.") full_object = "%s%s" % (prefix, API_OBJECT) columns = self._getSelectedColumns(**kwargs) for column in columns: name = column.getName(is_global_name=False) statement = "%s = %s.getColumnValue('%s')" % (name, full_object, name) sa.add(statement) return sa.get()
def _makeAPIPluginInitializationStatements(self, function_name, prefix=""): """ :param str function_name: :param str prefix: prefix for API Object """ sa = StatementAccumulator() sa.add("from scisheets.core import api as api") full_object = "%s%s" % (prefix, API_OBJECT) filepath = api_util.getTableCopyFilepath(function_name, self._user_directory) statement = """%s = api.APIPlugin('%s') %s.initialize() _table = %s.getTable() """ % ( full_object, filepath, full_object, full_object, ) sa.add(statement) return sa.get()
def _makeAPIInitializationStatements(self, create_API_object=False): """ :param bool create_API_object: True means that code will be generated that creates the API object. """ sa = StatementAccumulator() sa.add("from scisheets.core import api as api") statement = """ _table = api.readTableFromFile('%s') _table.setNamespace(globals()) %s = api.APIFormulas(_table) """ % ( self._table.getFilepath(), API_OBJECT, ) if not create_API_object: new_statement = statement.replace("\n", "\n#") header = "# Uncomment the following to execute standalone" statement = "%s\n#%s" % (header, new_statement) sa.add(statement) return sa.get()
def _wrapProgram(self): """ Puts a wrapper around the program so that an exception can be caught and its location determined within the program. :returns str: program wrapped in try/catch blocks :sideeffects: puts CONTROLLER object in the namespace """ self._namespace[CONTROLLER] = self._controller sa = StatementAccumulator() sa.add("try:") sa.indent(1) statement = "%s.startBlock('%s')" % (CONTROLLER, self._program_name) sa.add(statement) sa.add(self._program) statement = "%s.endBlock()" % CONTROLLER sa.add(statement) sa.indent(-1) statement = """ except Exception as exc: %s.exceptionForBlock(exc)""" % CONTROLLER sa.add(statement) return sa.get()
def _makeFormulaEvaluationStatements(self, **kwargs): """ Constructs a script to evaluate table formulas. :return str: statements Notes: (1) Iterate N (#formulas) times to handle dependencies between formulas """ # Initializations sa = StatementAccumulator() formula_columns = self._formulaColumns() num_formulas = len(formula_columns) if num_formulas == 0: return [] # Statements that evaluate the formulas # Iteratively evaluate the formulas. The iteration # terminates under three conditions: # 1. No exception and no change in table data since the # last iteration. # 2. No exception and the iteration count is equal to the # number of columns. # 3. The iteration count exceeds a maximum value. statement = """ # Formula evaluation loop %s.controller.initializeLoop() while not %s.controller.isTerminateLoop(): """ % ( API_OBJECT, API_OBJECT, ) sa.add(statement) sa.indent(1) sa.add("%s.controller.startAnIteration()" % API_OBJECT) # Formula Evaluation block header sa.add("# Formula Execution Blocks") # Formula Evaluation block formulas for column in formula_columns: sa.add("try:") sa.indent(1) colnm = column.getName(is_global_name=False) sa.add("# Column %s" % colnm) statement = "%s.controller.startBlock('%s')" % (API_OBJECT, colnm) sa.add(statement) statement = column.getFormulaStatement() sa.add(statement) sa.add("%s.controller.endBlock()" % API_OBJECT) if column.isExpression(): sa.add("%s = %s.coerceValues('%s', %s)" % (colnm, API_OBJECT, colnm, colnm)) sa.indent(-1) sa.add("except Exception as exc:") sa.indent(1) sa.add("%s.controller.exceptionForBlock(exc)" % API_OBJECT) sa.indent(-1) sa.add(" ") # Formula Evaluation block footer # End of loop statement = self._makeClosingOfFormulaEvaluationLoop(**kwargs) sa.add(statement) return sa.get()
def _makePrologue(self, **kwargs): """ Creates the imports that go at the head of the file :return str: statements """ sa = StatementAccumulator() # Import the plugins if self._user_directory is not None: statement = self._makeFormulaImportStatements(self._user_directory) sa.add(statement) statement = self._makeFormulaImportStatements(self._plugin_directory, import_path=self._plugin_path) sa.add(statement) # Add the table prologue sa.add("%s.controller.startBlock('Prologue')" % API_OBJECT) sa.add(self._table.getPrologue().getFormula()) sa.add("%s.controller.endBlock()" % API_OBJECT) return sa.get()
def makeTestProgram(self, function_name=None, inputs=None, outputs=None): """ Creates a program that tests the function exported for the table :param function_name: string name of the function to be created :param inputs: list of column names that are input to the function :param outputs: list of columns that are output from the function :return (str error, str program): """ sa = StatementAccumulator() prefix = "self." output_str = _makeOutputStr(outputs) statement = '''""" Tests for %s """ from scisheets.core import api as api from %s import %s import unittest ############################# # Tests ############################# # pylint: disable=W0212,C0111,R0904 class Test%s(unittest.TestCase): ''' % ( function_name, function_name, function_name, function_name, ) sa.add(statement) # Construct setup method sa.indent(1) sa.add("def setUp(self):") sa.indent(1) statement = self._makeAPIPluginInitializationStatements(function_name, prefix=prefix) sa.add(statement) sa.indent(-1) # Construct the test function header sa.add("def testBasics(self):") sa.indent(1) # Assign values to the columns sa.add(self._makeVariableAssignmentStatements(prefix=prefix, only_includes=inputs)) # Construct the call to the function being tested statement = _makeOutputStr(outputs) statement += " = %s(" % function_name statement += ",".join(inputs) statement += ")" sa.add(statement) # Construct the tests for column_name in outputs: statement = "self.assertTrue(self.%s.compareToColumnValues('%s', %s))" % ( API_OBJECT, column_name, column_name, ) sa.add(statement) sa.indent(-2) # Construct the program epilogue statement = """ if __name__ == '__main__': unittest.main()""" sa.add(statement) return sa.get()
def makeFunctionProgram(self, function_name, inputs, outputs): """ Creates a function for the table :param function_name: string name of the function to be created :param inputs: list of column names that are input to the function :param outputs: list of columns that are output from the function :return str program: Program as a string """ # Initializations sa = StatementAccumulator() # Program Prologue # TODO: This won't work with nested columns statement = """# Export of the table %s """ % self._table.getName( is_global_name=False ) sa.add(statement) sa.add("") # Make the function definition. This must enclose the prologue # and epilogue sa.add(_makeFunctionStatement(function_name, inputs)) sa.indent(1) sa.add(self._makeAPIPluginInitializationStatements(function_name)) excludes = list(inputs) excludes.extend(outputs) sa.add(self._makePrologue(excludes=excludes)) # Assign the column values to function variables. # Note that inputs and outputs are not assigned. # sa.add(self._makeFormulaEvaluationStatements(excludes=excludes)) sa.add(self._makeClosingException()) sa.add(self._makeEpilogue()) sa.add(_makeReturnStatement(outputs)) return sa.get()
def makeExportScriptProgram(self): """ Creates an exported python script. :return str program: Program as a string """ sa = StatementAccumulator() # TODO: This won't work with nested columns statement = """# Script that runs formulas in the table %s. """ % self._table.getName( is_global_name=False ) sa.add(statement) sa.add(self._makeAPIInitializationStatements(create_API_object=True)) sa.add(self._makePrologue()) sa.add(self._makeFormulaEvaluationStatements()) sa.add(self._makeClosingException()) sa.add(self._makeEpilogue()) sa.add(self._makeVariablePrintStatements()) return sa.get()
class TestStatementAccumulator(unittest.TestCase): def setUp(self): self.sa = StatementAccumulator() def testAdd(self): statement = "This is a test." self.sa.add(statement) self.assertEqual(self.sa._statements[0], statement) statement = "This is another test." self.sa.add(statement) self.assertEqual(self.sa._statements[1], statement) def testAddWithIndent(self): statement = "This is a test." self.sa.add(statement) self.sa.indent(1) statement = "This is another test." self.sa.add(statement) self.assertEqual(self.sa._statements[1], " %s" % statement) self.sa.indent(1) statement = "This is another test." self.sa.add(statement) self.assertEqual(self.sa._statements[2], " %s" % statement) self.sa.indent(-1) statement = "This is another test." self.sa.add(statement) self.assertEqual(self.sa._statements[3], " %s" % statement) def testGet(self): first_statement = "This is a test." self.sa.add(first_statement) self.assertEqual(self.sa.get(), first_statement) self.sa.indent(1) second_statement = "This is a test." self.sa.add(second_statement) expected_result ="%s\n %s" % (first_statement, second_statement) self.assertEqual(self.sa.get(), expected_result) def testExtra(self): statement = '''# Evaluation of the table %s. ''' % "aName" self.sa.add(statement)
def setUp(self): self.sa = StatementAccumulator()
def makeEvaluationScriptProgram(self, create_API_object=False): """ Creates a python script that evaluates the table formulas when there is a change to the scisheet :param bool create_API_object: True means that code will be generated that creates the API object. :return str program: Program as a string """ sa = StatementAccumulator() # TODO: This won't work with nested columns statement = """# Evaluation of the table %s. """ % self._table.getName( is_global_name=False ) sa.add(statement) sa.add(self._makeAPIInitializationStatements(create_API_object=create_API_object)) sa.add("try:") sa.indent(1) sa.add(self._makePrologue()) sa.add(self._makeFormulaEvaluationStatements()) statement = "if %s.controller.getException() is None:" % API_OBJECT sa.add(statement) sa.indent(1) sa.add(self._makeEpilogue()) sa.indent(-2) # Handle exceptions in the Prologue and Epilogue statement = ( """ except Exception as exc: %s.controller.exceptionForBlock(exc)""" % API_OBJECT ) sa.add(statement) sa.add(self._makeClosingException(is_absolute_linenumber=False)) return sa.get()