def test_advanced_code(self): """ Check execute() can execute complex scripts. """ code = """ def returns_number(nb): str = "%s_kingdom" % nb if nb > 1: str += 's' return str param=Kingdom.objects.count() if param == 0: status='no_kingdom' else: status = returns_number(param) """ status, param = execute(code, param=False) self.assertEqual(status, 'no_kingdom') self.assertEqual(param, 0) Kingdom().save() status, param = execute(code, param=False) self.assertEqual(status, '1_kingdom') self.assertEqual(param, 1) Kingdom().save() status, param = execute(code, param=False) self.assertEqual(status, '2_kingdoms') self.assertEqual(param, 2)
def test_access_to_modules(self): """ Check execute() has access to the ORM """ code = """ param=Kingdom.objects.count() """ status, param = execute(code, param=False) self.assertEqual(param, 0) Kingdom().save() status, param = execute(code, param=False) self.assertEqual(param, 1)
def test_access_to_global_scripts(self): """ Check execute() has access to the function defined in scripts. """ code = """ param = fuzzy(10) """ status, param = execute(code, param=False) self.assertTrue(param > -10 and param < 10)
def test_params_changed(self): """ Check params is correctly retrieved """ code = """ param=2 """ status, param = execute(code, param=1) self.assertEqual(param, 2)
def test_params_unchanged(self): """ Check params is correctly retrieved """ code = """ # No real code here """ status, param = execute(code, param=1) self.assertEqual(param, 1)
def test_status_changed(self): """ Check status is correctly retrieved """ code = """ status="fine" """ status, param = execute(code) self.assertEqual(status, 'fine')
def test_float_division(self): """ Check division is done in float. This is not the default Python behavior. """ code = """ param = 1 / 2 """ status, param = execute(code, param=False) self.assertEqual(param, 0.5)
def test_code_stop_status(self): """ Check you can stop code execution and return a status """ code = """ status = "step1" stop("step2") """ status, param = execute(code, param=False) self.assertEqual(status, "step2")
def test_status_unchanged(self): """ Check status is 'ok' by default """ code = """ # No real code here. """ status, param = execute(code) self.assertEqual(status, 'ok')
def test_access_to_app_scripts(self): """ Check execute() has access to the function defined in scripts. """ code = """ k = Kingdom() k.save() k.message("foo") """ status, param = execute(code, param=False) self.assertEqual(Kingdom.objects.get().message_set.get().content, "foo")
def test_code_stop(self): """ Check you can stop code execution """ code = """ status = "step1" stop() # Never called status = "step2" """ status, param = execute(code, param=False) self.assertEqual(status, "step1")
def execute(self, model, attr, kingdom=None, raw_context=None): """ Execute the code stored in :attr on :model object, with :self as param. Context is built by default with :kingdom key, additional values can be passed with raw_context. """ from config.lib.execute import execute from reporting.models import ScriptLog # Build context object context = {} if kingdom is not None: context = { 'kingdom': kingdom, 'folks': kingdom.folk_set.all(), } if raw_context is not None: context.update(raw_context) _started_at = datetime.now() _started_query_count = len(connection.queries) ScriptedModel._stack.append(0) # Execute code code = getattr(model, attr) filename = "%s(%s).%s" % (model.__class__.__name__, model.pk, attr) try: status, param = execute(code, param=self, context=context, filename=filename) except Exception as e: # Reset the stack trace, lose data from current calls ScriptedModel._stack = [0] ScriptedModel._scriptlogs = [] raise if len(ScriptedModel._stack) == 0: # We've thrown an error somewhere, intercepted it and cleaned the log. However the exception was later catched by some script above, and we're now in an unknown state since we've lost datas while cleaning. # Conclusion : no metrics for this stack trace, let's skip this return # Retrieve metrics delay = (datetime.now() - _started_at).total_seconds() * 1000 queries = len(connection.queries) - _started_query_count child_queries = ScriptedModel._stack.pop() direct_queries = queries - child_queries parent_nested_queries = ScriptedModel._stack[-1] ScriptedModel._stack[-1] = parent_nested_queries + queries # Store log if code is not None and code.strip() != "": sl = ScriptLog( kingdom=kingdom, object_type=model.__class__.__name__, object_pk=model.pk, object_attr=attr, stack_level=len(ScriptedModel._stack), time=delay, queries=queries, direct_queries=direct_queries ) ScriptedModel._scriptlogs.append(sl) if len(ScriptedModel._stack) == 1: # We are at the root of a call-trace, let's save all script log ScriptLog.objects.bulk_create(ScriptedModel._scriptlogs) del ScriptedModel._scriptlogs[:] return status, param