Beispiel #1
0
    def post_execute(self):
        """This will build the provenance for the execution of the code block in the IPython Notebook"""
        # Initialize a provenance builder
        pb = ProvBuilder()

        # Get the description (i.e. the code) from the code we just executed
        description = self.hist_to_string(1) 
        position = len(get_ipython().user_ns.get('_ih')) -1
        name = "In [{}]".format(position)
        
        # Initialize inputs, outputs and dependencies dictionaries for activity generator in ProvBuilder
        inputs = {}
        outputs = {}
        dependencies = {}
        
        # For all nodes (variables/functions) that were recognized by the CodeVisitor
        # If the node is a *global* variable, add it to the inputs
        # If the node is a *function* name, add it to the dependencies
        for node in self.used:    
            # log.debug("Checking wether " + node + " is a variable or a function")
            try :
                evaluated_node = self.shell.ev(node)
                # log.debug("> Could evaluate {}".format(node) )
                if node in self.environment and not callable(evaluated_node) :
                    # log.debug(">> {} is in environment and not callable (it is a variable)".format(node))
                    # # log.debug("Used global variable {}".format(node))
                    # Set the input of the node to the value that it had prior to executing the code (if available)
                    if node in self.environment:
                        # log.debug("Global variable existed before, adding to inputs")
                        inputs[node] = self.environment[node]
                    # Otherwise, we do nothing, since the variable may have been used, but was first introduced in the code.
                    else :
                        # log.debug("Global variable was introduced here, not doing anything")
                        pass
               
                elif callable(evaluated_node):
                    # log.debug(">> {} is a function, adding to dependencies.".format(node))
                    try :
                        dependencies[node] = inspect.getsource(evaluated_node)
                    except Exception as e:
                        # print e
                        dependencies[node] = unicode(evaluated_node)
                else :
                    # log.debug(">> {} is not callable, and not in environment... it was a variable that was newly introduced here?".format(node))
                    pass
            except :
                ## log.debug("Used local {} variable or function".format(node))
                # log.debug("> Could not evaluate " + node)
                if node in self.environment :
                    # log.debug(">> Node is in environment, we'll use its evaluated value from the environment")
                    evaluated_node = self.environment[node]
                    
                    if not callable(evaluated_node) :
                        # log.debug(">>> {} is a variable".format(node))
                        inputs[node] = evaluated_node
                    else :
                        # log.debug(">>> {} is a function".format(node))
                        dependencies[node] = inspect.getsource(evaluated_node)
                else :
                    # log.debug(">> {} was introduced here, not doing anything".format(node))
                    pass
                
        # We'll loop through all known entities in the user namespace.
        for k,v in self.shell.user_ns.items():
            # Ignore any standard IPython/Python variables in the user namespace
            # if k.startswith('_') or k in ['In','Out','exit','quit','get_ipython'] :
            #     log.debug("'{}' skipped, because it is in ['In','Out','exit','quit','get_ipython'] or starts with '_'".format(k))
            #     pass
            
            # TEMPORARY: Test what happens if we don't exclude 'Out'
            if k.startswith('_') or k in ['In','exit','quit','get_ipython'] :
                # log.debug("'{}' skipped, because it is in ['In','exit','quit','get_ipython'] or starts with '_'".format(k))
                continue
                
            # For all other variables, see whether they were changed, and add them to the outputs
            ## This compares the value of the variable with the value it had previously, or
            ## checks that the variable did not exist previously.  
            # Need to do some exception handling to deal with ValueErrors for objects that have ambiguous truth values
            try :
                # if (k in self.environment and not ((numpy.array_equal(v,self.environment[k]) or v == self.environment[k]) and self.pre_ticker.setdefault(k,0) == pb.get_tick(k))) or (not k in self.environment) or k == 'Out':
                if (k in self.environment and not (v == self.environment[k] and self.pre_ticker.setdefault(k,0) == pb.get_tick(k))) or (not k in self.environment) or k == 'Out':
                    changed = True
                else:
                    changed = False
            except Exception as e:
                log.debug("Caught numpy array comparison exception")
                ## Special handling of Numpy silliness
                if k in self.environment:
                    if not numpy.asarray(v == self.environment[k]).all():
                        log.debug("Not the same (value-comparison)")
                        changed = True
                    elif self.pre_ticker.setdefault(k,0) == pb.get_tick(k):
                        log.debug("Not the same (tick-comparison)")
                        changed = True
                    else :
                        changed = False
                elif not k in self.environment :
                    log.debug("Newly added variable")
                    changed = True
                elif k == 'Out' :
                    log.debug("Out value")
                    changed = True
                else :
                    log.debug("Not changed")
                    changed = False
                
            
            if changed:
                log.debug("{} changed or was added".format(k))
                
                
                
                # If the object is not a function, we'll use the value as output value.
                if not callable(v):
                    # log.debug("{} is not a function, adding to outputs as value".format(k))
                    if k == 'Out':
                        if len(v) == 0:
                            log.debug("Output value is empty: skipping...")
                            continue
                            
                        kname = 'Out [{}]'.format(position)
                        log.debug("{}: {}".format(kname,v))
                    else :
                        kname = k
                    outputs[kname] = v
                    
                    # Increase the tick of the variable with name 'k'
                    # self.tick(k)
                    
                # If it is a PROV wrapped function, we'll retrieve its source and use it as output value.
                elif callable(v) and hasattr(v,'source') :
                    # log.debug("{} is a PROV wrapped function, its source is an output value".format(k))
                    outputs[k] = v.source
                # Otherwise (this shouldn't be the case, but anyway) we'll use its source directly.
                elif callable(v) :
                    # log.debug("{} is callable, but not wrapped... we'll try to retrieve its source and add it as an output".format(k))
                    try :
                        outputs[k] = inspect.getsource(v)
                    except:
                        # log.debug("could not get source of {}, just taking its value as an output".format(k))
                        outputs[k] = v
                # Finally, this is probably not were we'll end up anyway... we'll do nothing 
                else :
                    # log.debug("Unexpected!")
                    pass
                
                # log.debug("Just visited {}".format(k))
                self.environment[k] = v
            else :
                log.debug("'{}' skipped because it did not change (ticks: {} and {}).".format(k,self.pre_ticker[k],pb.get_tick(k)))
                
        
        # print dependencies.keys()
        pb.add_activity(name, description, inputs, outputs, dependencies, input_names=inputs.keys(), output_names=outputs.keys(), expand_output_dict=True, pre_ticker=self.pre_ticker)