# HG changeset patch # User paulb@localhost.localdomain # Date 1165186032 -3600 # Node ID 3d602e2bb217ffbe969de7c9c69b4018657cf7b6 # Parent 7bd67aabe8451a02e09bc802588a7385557fc117 Reordered methods. Changed subprogram to attribute in invoke_subprogram. Added node coverage lists. diff -r 7bd67aabe845 -r 3d602e2bb217 annotate.py --- a/annotate.py Sun Dec 03 22:04:21 2006 +0100 +++ b/annotate.py Sun Dec 03 23:47:12 2006 +0100 @@ -116,6 +116,13 @@ exchanged - this provides a means of distinguishing between the different types possible when the means of constructing the namespace may depend on run-time behaviour. + + Covered: Assign, CheckExc, Conditional, InvokeBlock, InvokeFunction, + LoadAttr, LoadExc, LoadName, LoadRef, LoadTemp, Module, Not, Pass, + Raise, ReleaseTemp, ReturnFromBlock, ReturnFromFunction, StoreAttr, + StoreName, StoreTemp, Subprogram, Try. + + Missing: Keyword, Global, Import. """ def __init__(self): @@ -350,6 +357,127 @@ return conditional + def _visitInvoke(self, invoke, invocation_types, have_args): + + """ + Return the processed 'invoke' node, using the given 'invocation_types' + as the list of callables to be investigated for instantiation or for the + invocation of functions or blocks. If 'have_args' is a true value, any + invocation or instantiation will involve arguments. + """ + + # Now locate and invoke the subprogram. This can be complicated because + # the target may be a class or object, and there may be many different + # related subprograms. + + invocations = [] + + # Visit each callable in turn, finding subprograms. + + for attr in invocation_types: + + # Deal with class invocations by providing instance objects. + # Here, each class is queried for the __init__ method, which may + # exist for some combinations of classes in a hierarchy but not for + # others. + + if isinstance(attr.type, Class): + attributes = get_attributes(attr.type, "__init__") + + # Deal with object invocations by using __call__ methods. + + elif isinstance(attr.type, Instance): + attributes = get_attributes(attr.type, "__call__") + + # Normal functions or methods are more straightforward. + # Here, we model them using an attribute with no context and with + # no associated accessor. + + else: + attributes = [(attr, None)] + + # Inspect each attribute and extract the subprogram. + + for attribute, accessor in attributes: + + # If a class is involved, presume that it must create a new + # object. + + if isinstance(attr.type, Class): + + # Instantiate the class. + # NOTE: Should probably only allocate a single instance. + + instance = self.new_instance(invoke, "new", attr.type.full_name(), attr.type) + + # For instantiations, switch the context. + + if attribute is not None: + attribute = Attribute(instance, attribute.type) + + # Skip cases where no callable is found. + + if attribute is not None: + + # If a subprogram is defined, invoke it. + + self.invoke_subprogram(invoke, attribute) + if attribute.type not in invocations: + invocations.append(attribute.type) + + elif not isinstance(attr.type, Class): + print "Invocation type is None for", accessor + + else: + + # Test to see if no arguments were supplied in cases where no + # initialiser was found. + + if have_args: + raise AnnotationMessage, "No initialiser found for '%s' with arguments." % attr.type + + # Special case: initialisation. + + if isinstance(attr.type, Class): + + # Associate the instance with the result of this invocation. + + self.namespace.set_types([Attribute(None, instance)]) + self.annotate(invoke) + + # Remember the invocations that were found, along with the return type + # information. + + invoke.invocations = invocations + self.namespace.set_types(getattr(invoke, "types", [])) + return invoke + + def visitInvokeBlock(self, invoke): + + """ + Return the processed 'invoke' node, first finding the callables + indicated by the expression. + """ + + invoke.expr = self.dispatch(invoke.expr) + invocation_types = self.namespace.types + return self._visitInvoke(invoke, invocation_types, have_args=0) + + def visitInvokeFunction(self, invoke): + + """ + Return the processed 'invoke' node, first finding the callables + indicated by the expression. + """ + + invoke.expr = self.dispatch(invoke.expr) + invocation_types = self.namespace.types + + # Invocation processing starts with making sure that the arguments have + # been processed. + + return self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke)) + def visitLoadAttr(self, loadattr): """ @@ -681,116 +809,6 @@ try_.finally_ = self.dispatches(try_.finally_) return try_ - # Invocations are a chapter of their own. - - def visitInvokeBlock(self, invoke): - - # First find the callables. - - invoke.expr = self.dispatch(invoke.expr) - invocation_types = self.namespace.types - return self._visitInvoke(invoke, invocation_types, have_args=0) - - def visitInvokeFunction(self, invoke): - - # First find the callables. - - invoke.expr = self.dispatch(invoke.expr) - invocation_types = self.namespace.types - - # Invocation processing starts with making sure that the arguments have - # been processed. - - return self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke)) - - def _visitInvoke(self, invoke, invocation_types, have_args): - - # Now locate and invoke the subprogram. This can be complicated because - # the target may be a class or object, and there may be many different - # related subprograms. - - invocations = [] - - # Visit each callable in turn, finding subprograms. - - for attr in invocation_types: - - # Deal with class invocations by providing instance objects. - # Here, each class is queried for the __init__ method, which may - # exist for some combinations of classes in a hierarchy but not for - # others. - - if isinstance(attr.type, Class): - attributes = get_attributes(attr.type, "__init__") - - # Deal with object invocations by using __call__ methods. - - elif isinstance(attr.type, Instance): - attributes = get_attributes(attr.type, "__call__") - - # Normal functions or methods are more straightforward. - # Here, we model them using an attribute with no context and with - # no associated accessor. - - else: - attributes = [(attr, None)] - - # Inspect each attribute and extract the subprogram. - - for attribute, accessor in attributes: - - # If a class is involved, presume that it must create a new - # object. - - if isinstance(attr.type, Class): - - # Instantiate the class. - # NOTE: Should probably only allocate a single instance. - - instance = self.new_instance(invoke, "new", attr.type.full_name(), attr.type) - - # For instantiations, switch the context. - - if attribute is not None: - attribute = Attribute(instance, attribute.type) - - # Skip cases where no callable is found. - - if attribute is not None: - - # If a subprogram is defined, invoke it. - - self.invoke_subprogram(invoke, attribute) - if attribute.type not in invocations: - invocations.append(attribute.type) - - elif not isinstance(attr.type, Class): - print "Invocation type is None for", accessor - - else: - - # Test to see if no arguments were supplied in cases where no - # initialiser was found. - - if have_args: - raise AnnotationMessage, "No initialiser found for '%s' with arguments." % attr.type - - # Special case: initialisation. - - if isinstance(attr.type, Class): - - # Associate the instance with the result of this invocation. - - self.namespace.set_types([Attribute(None, instance)]) - self.annotate(invoke) - - # Remember the invocations that were found, along with the return type - # information. - - invoke.invocations = invocations - self.namespace.set_types(getattr(invoke, "types", [])) - return invoke - # Utility methods. def new_instance(self, node, reason, target, type): @@ -817,18 +835,21 @@ return node.instances[(reason, target, type)] - def invoke_subprogram(self, invoke, subprogram): + def invoke_subprogram(self, invoke, attribute): - "Invoke using the given 'invoke' node the given 'subprogram'." + """ + Invoke using the given 'invoke' node the subprogram represented by the + given 'attribute'. + """ # Test for context information, making it into a real attribute. - if subprogram.context is not None: - context = Attribute(None, subprogram.context) - target = subprogram.type + if attribute.context is not None: + context = Attribute(None, attribute.context) + target = attribute.type else: context = None - target = subprogram.type + target = attribute.type # Provide the correct namespace for the invocation.