1.1 --- a/annotate.py Sun Dec 03 22:04:21 2006 +0100
1.2 +++ b/annotate.py Sun Dec 03 23:47:12 2006 +0100
1.3 @@ -116,6 +116,13 @@
1.4 exchanged - this provides a means of distinguishing between the different
1.5 types possible when the means of constructing the namespace may depend on
1.6 run-time behaviour.
1.7 +
1.8 + Covered: Assign, CheckExc, Conditional, InvokeBlock, InvokeFunction,
1.9 + LoadAttr, LoadExc, LoadName, LoadRef, LoadTemp, Module, Not, Pass,
1.10 + Raise, ReleaseTemp, ReturnFromBlock, ReturnFromFunction, StoreAttr,
1.11 + StoreName, StoreTemp, Subprogram, Try.
1.12 +
1.13 + Missing: Keyword, Global, Import.
1.14 """
1.15
1.16 def __init__(self):
1.17 @@ -350,6 +357,127 @@
1.18
1.19 return conditional
1.20
1.21 + def _visitInvoke(self, invoke, invocation_types, have_args):
1.22 +
1.23 + """
1.24 + Return the processed 'invoke' node, using the given 'invocation_types'
1.25 + as the list of callables to be investigated for instantiation or for the
1.26 + invocation of functions or blocks. If 'have_args' is a true value, any
1.27 + invocation or instantiation will involve arguments.
1.28 + """
1.29 +
1.30 + # Now locate and invoke the subprogram. This can be complicated because
1.31 + # the target may be a class or object, and there may be many different
1.32 + # related subprograms.
1.33 +
1.34 + invocations = []
1.35 +
1.36 + # Visit each callable in turn, finding subprograms.
1.37 +
1.38 + for attr in invocation_types:
1.39 +
1.40 + # Deal with class invocations by providing instance objects.
1.41 + # Here, each class is queried for the __init__ method, which may
1.42 + # exist for some combinations of classes in a hierarchy but not for
1.43 + # others.
1.44 +
1.45 + if isinstance(attr.type, Class):
1.46 + attributes = get_attributes(attr.type, "__init__")
1.47 +
1.48 + # Deal with object invocations by using __call__ methods.
1.49 +
1.50 + elif isinstance(attr.type, Instance):
1.51 + attributes = get_attributes(attr.type, "__call__")
1.52 +
1.53 + # Normal functions or methods are more straightforward.
1.54 + # Here, we model them using an attribute with no context and with
1.55 + # no associated accessor.
1.56 +
1.57 + else:
1.58 + attributes = [(attr, None)]
1.59 +
1.60 + # Inspect each attribute and extract the subprogram.
1.61 +
1.62 + for attribute, accessor in attributes:
1.63 +
1.64 + # If a class is involved, presume that it must create a new
1.65 + # object.
1.66 +
1.67 + if isinstance(attr.type, Class):
1.68 +
1.69 + # Instantiate the class.
1.70 + # NOTE: Should probably only allocate a single instance.
1.71 +
1.72 + instance = self.new_instance(invoke, "new", attr.type.full_name(), attr.type)
1.73 +
1.74 + # For instantiations, switch the context.
1.75 +
1.76 + if attribute is not None:
1.77 + attribute = Attribute(instance, attribute.type)
1.78 +
1.79 + # Skip cases where no callable is found.
1.80 +
1.81 + if attribute is not None:
1.82 +
1.83 + # If a subprogram is defined, invoke it.
1.84 +
1.85 + self.invoke_subprogram(invoke, attribute)
1.86 + if attribute.type not in invocations:
1.87 + invocations.append(attribute.type)
1.88 +
1.89 + elif not isinstance(attr.type, Class):
1.90 + print "Invocation type is None for", accessor
1.91 +
1.92 + else:
1.93 +
1.94 + # Test to see if no arguments were supplied in cases where no
1.95 + # initialiser was found.
1.96 +
1.97 + if have_args:
1.98 + raise AnnotationMessage, "No initialiser found for '%s' with arguments." % attr.type
1.99 +
1.100 + # Special case: initialisation.
1.101 +
1.102 + if isinstance(attr.type, Class):
1.103 +
1.104 + # Associate the instance with the result of this invocation.
1.105 +
1.106 + self.namespace.set_types([Attribute(None, instance)])
1.107 + self.annotate(invoke)
1.108 +
1.109 + # Remember the invocations that were found, along with the return type
1.110 + # information.
1.111 +
1.112 + invoke.invocations = invocations
1.113 + self.namespace.set_types(getattr(invoke, "types", []))
1.114 + return invoke
1.115 +
1.116 + def visitInvokeBlock(self, invoke):
1.117 +
1.118 + """
1.119 + Return the processed 'invoke' node, first finding the callables
1.120 + indicated by the expression.
1.121 + """
1.122 +
1.123 + invoke.expr = self.dispatch(invoke.expr)
1.124 + invocation_types = self.namespace.types
1.125 + return self._visitInvoke(invoke, invocation_types, have_args=0)
1.126 +
1.127 + def visitInvokeFunction(self, invoke):
1.128 +
1.129 + """
1.130 + Return the processed 'invoke' node, first finding the callables
1.131 + indicated by the expression.
1.132 + """
1.133 +
1.134 + invoke.expr = self.dispatch(invoke.expr)
1.135 + invocation_types = self.namespace.types
1.136 +
1.137 + # Invocation processing starts with making sure that the arguments have
1.138 + # been processed.
1.139 +
1.140 + return self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke))
1.141 +
1.142 def visitLoadAttr(self, loadattr):
1.143
1.144 """
1.145 @@ -681,116 +809,6 @@
1.146 try_.finally_ = self.dispatches(try_.finally_)
1.147 return try_
1.148
1.149 - # Invocations are a chapter of their own.
1.150 -
1.151 - def visitInvokeBlock(self, invoke):
1.152 -
1.153 - # First find the callables.
1.154 -
1.155 - invoke.expr = self.dispatch(invoke.expr)
1.156 - invocation_types = self.namespace.types
1.157 - return self._visitInvoke(invoke, invocation_types, have_args=0)
1.158 -
1.159 - def visitInvokeFunction(self, invoke):
1.160 -
1.161 - # First find the callables.
1.162 -
1.163 - invoke.expr = self.dispatch(invoke.expr)
1.164 - invocation_types = self.namespace.types
1.165 -
1.166 - # Invocation processing starts with making sure that the arguments have
1.167 - # been processed.
1.168 -
1.169 - return self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke))
1.170 -
1.171 - def _visitInvoke(self, invoke, invocation_types, have_args):
1.172 -
1.173 - # Now locate and invoke the subprogram. This can be complicated because
1.174 - # the target may be a class or object, and there may be many different
1.175 - # related subprograms.
1.176 -
1.177 - invocations = []
1.178 -
1.179 - # Visit each callable in turn, finding subprograms.
1.180 -
1.181 - for attr in invocation_types:
1.182 -
1.183 - # Deal with class invocations by providing instance objects.
1.184 - # Here, each class is queried for the __init__ method, which may
1.185 - # exist for some combinations of classes in a hierarchy but not for
1.186 - # others.
1.187 -
1.188 - if isinstance(attr.type, Class):
1.189 - attributes = get_attributes(attr.type, "__init__")
1.190 -
1.191 - # Deal with object invocations by using __call__ methods.
1.192 -
1.193 - elif isinstance(attr.type, Instance):
1.194 - attributes = get_attributes(attr.type, "__call__")
1.195 -
1.196 - # Normal functions or methods are more straightforward.
1.197 - # Here, we model them using an attribute with no context and with
1.198 - # no associated accessor.
1.199 -
1.200 - else:
1.201 - attributes = [(attr, None)]
1.202 -
1.203 - # Inspect each attribute and extract the subprogram.
1.204 -
1.205 - for attribute, accessor in attributes:
1.206 -
1.207 - # If a class is involved, presume that it must create a new
1.208 - # object.
1.209 -
1.210 - if isinstance(attr.type, Class):
1.211 -
1.212 - # Instantiate the class.
1.213 - # NOTE: Should probably only allocate a single instance.
1.214 -
1.215 - instance = self.new_instance(invoke, "new", attr.type.full_name(), attr.type)
1.216 -
1.217 - # For instantiations, switch the context.
1.218 -
1.219 - if attribute is not None:
1.220 - attribute = Attribute(instance, attribute.type)
1.221 -
1.222 - # Skip cases where no callable is found.
1.223 -
1.224 - if attribute is not None:
1.225 -
1.226 - # If a subprogram is defined, invoke it.
1.227 -
1.228 - self.invoke_subprogram(invoke, attribute)
1.229 - if attribute.type not in invocations:
1.230 - invocations.append(attribute.type)
1.231 -
1.232 - elif not isinstance(attr.type, Class):
1.233 - print "Invocation type is None for", accessor
1.234 -
1.235 - else:
1.236 -
1.237 - # Test to see if no arguments were supplied in cases where no
1.238 - # initialiser was found.
1.239 -
1.240 - if have_args:
1.241 - raise AnnotationMessage, "No initialiser found for '%s' with arguments." % attr.type
1.242 -
1.243 - # Special case: initialisation.
1.244 -
1.245 - if isinstance(attr.type, Class):
1.246 -
1.247 - # Associate the instance with the result of this invocation.
1.248 -
1.249 - self.namespace.set_types([Attribute(None, instance)])
1.250 - self.annotate(invoke)
1.251 -
1.252 - # Remember the invocations that were found, along with the return type
1.253 - # information.
1.254 -
1.255 - invoke.invocations = invocations
1.256 - self.namespace.set_types(getattr(invoke, "types", []))
1.257 - return invoke
1.258 -
1.259 # Utility methods.
1.260
1.261 def new_instance(self, node, reason, target, type):
1.262 @@ -817,18 +835,21 @@
1.263
1.264 return node.instances[(reason, target, type)]
1.265
1.266 - def invoke_subprogram(self, invoke, subprogram):
1.267 + def invoke_subprogram(self, invoke, attribute):
1.268
1.269 - "Invoke using the given 'invoke' node the given 'subprogram'."
1.270 + """
1.271 + Invoke using the given 'invoke' node the subprogram represented by the
1.272 + given 'attribute'.
1.273 + """
1.274
1.275 # Test for context information, making it into a real attribute.
1.276
1.277 - if subprogram.context is not None:
1.278 - context = Attribute(None, subprogram.context)
1.279 - target = subprogram.type
1.280 + if attribute.context is not None:
1.281 + context = Attribute(None, attribute.context)
1.282 + target = attribute.type
1.283 else:
1.284 context = None
1.285 - target = subprogram.type
1.286 + target = attribute.type
1.287
1.288 # Provide the correct namespace for the invocation.
1.289