1.1 --- a/deducer.py Tue Feb 07 23:45:16 2017 +0100
1.2 +++ b/deducer.py Wed Feb 08 01:17:38 2017 +0100
1.3 @@ -105,7 +105,6 @@
1.4 self.access_constrained = set()
1.5 self.referenced_attrs = {}
1.6 self.referenced_objects = {}
1.7 - self.accessor_invocation_types = {}
1.8
1.9 # Details of access operations.
1.10
1.11 @@ -141,7 +140,6 @@
1.12 self.init_aliases()
1.13 self.modify_mutated_attributes()
1.14 self.identify_references()
1.15 - self.classify_invocations()
1.16 self.classify_accessors()
1.17 self.classify_accesses()
1.18 self.initialise_access_plans()
1.19 @@ -444,55 +442,6 @@
1.20 finally:
1.21 f_attrs.close()
1.22
1.23 - def classify_invocations(self):
1.24 -
1.25 - """
1.26 - Classify invocations, suggesting guard opportunities where accessors
1.27 - providing attributes involved in invocations can be sufficiently
1.28 - determined, recording invocation candidates that would not be suitable.
1.29 - """
1.30 -
1.31 - for access_location, arguments in self.reference_invocations.items():
1.32 -
1.33 - # Obtain the possible attributes.
1.34 -
1.35 - referenced_attrs = self.referenced_attrs[access_location]
1.36 -
1.37 - if referenced_attrs:
1.38 - object_types = set()
1.39 - unsuitable = set()
1.40 -
1.41 - for attrtype, object_type, attr in referenced_attrs:
1.42 -
1.43 - # Obtain identifiable callables.
1.44 -
1.45 - objpath = attr.get_origin()
1.46 - if objpath:
1.47 - parameters = self.importer.function_parameters.get(objpath)
1.48 - if parameters:
1.49 - defaults = self.importer.function_defaults.get(objpath)
1.50 -
1.51 - # Determine whether the specified arguments are
1.52 - # compatible with the callable signature.
1.53 -
1.54 - if arguments >= len(parameters) - len(defaults) and \
1.55 - arguments <= len(parameters):
1.56 -
1.57 - object_types.add(object_type)
1.58 - else:
1.59 - unsuitable.add(attr)
1.60 -
1.61 - # Record the unsuitable candidates for the invocation.
1.62 -
1.63 - if unsuitable:
1.64 - self.reference_invocations_unsuitable[access_location] = unsuitable
1.65 -
1.66 - # Record the possible accessor types for the invocation.
1.67 -
1.68 - for accessor_location in self.get_accessors_for_access(access_location):
1.69 - init_item(self.accessor_invocation_types, accessor_location, set)
1.70 - self.accessor_invocation_types[accessor_location].update(object_types)
1.71 -
1.72 def classify_accessors(self):
1.73
1.74 "For each program location, classify accessors."
1.75 @@ -640,8 +589,10 @@
1.76
1.77 if guarded:
1.78 guard_attrs = set()
1.79 +
1.80 for _attrtype, object_type, attr in \
1.81 - self._identify_reference_attribute(attrname, guard_class_types, guard_instance_types, guard_module_types):
1.82 + self._identify_reference_attribute(location, attrname, guard_class_types, guard_instance_types, guard_module_types):
1.83 +
1.84 guard_attrs.add(attr)
1.85 else:
1.86 guard_attrs = None
1.87 @@ -1849,16 +1800,17 @@
1.88 # Record the referenced objects.
1.89
1.90 self.referenced_attrs[location] = \
1.91 - self._identify_reference_attribute(attrname, class_types, instance_types, module_types)
1.92 + self._identify_reference_attribute(location, attrname, class_types, instance_types, module_types)
1.93
1.94 if constrained:
1.95 self.access_constrained.add(location)
1.96
1.97 - def _identify_reference_attribute(self, attrname, class_types, instance_types, module_types):
1.98 + def _identify_reference_attribute(self, location, attrname, class_types, instance_types, module_types):
1.99
1.100 """
1.101 - Identify the reference attribute with the given 'attrname', employing
1.102 - the given 'class_types', 'instance_types' and 'module_types'.
1.103 + Identify the reference attribute at the given access 'location', using
1.104 + the given 'attrname', and employing the given 'class_types',
1.105 + 'instance_types' and 'module_types'.
1.106 """
1.107
1.108 attrs = set()
1.109 @@ -1868,14 +1820,15 @@
1.110
1.111 for object_type in class_types:
1.112 ref = self.importer.get_class_attribute(object_type, attrname)
1.113 - if ref:
1.114 + if ref and self.is_compatible_callable(location, object_type, ref):
1.115 attrs.add(("<class>", object_type, ref))
1.116
1.117 # Add any distinct instance attributes that would be provided
1.118 # by instances also providing indirect class attribute access.
1.119
1.120 for ref in self.importer.get_instance_attributes(object_type, attrname):
1.121 - attrs.add(("<instance>", object_type, ref))
1.122 + if self.is_compatible_callable(location, object_type, ref):
1.123 + attrs.add(("<instance>", object_type, ref))
1.124
1.125 # The instance-only types expose instance attributes, but although
1.126 # classes are excluded as potential accessors (since they do not provide
1.127 @@ -1887,21 +1840,55 @@
1.128
1.129 if instance_attrs:
1.130 for ref in instance_attrs:
1.131 - attrs.add(("<instance>", object_type, ref))
1.132 + if self.is_compatible_callable(location, object_type, ref):
1.133 + attrs.add(("<instance>", object_type, ref))
1.134 else:
1.135 ref = self.importer.get_class_attribute(object_type, attrname)
1.136 - if ref:
1.137 + if ref and self.is_compatible_callable(location, object_type, ref):
1.138 attrs.add(("<class>", object_type, ref))
1.139
1.140 # Module types expose module attributes for module accessors.
1.141
1.142 for object_type in module_types:
1.143 ref = self.importer.get_module_attribute(object_type, attrname)
1.144 - if ref:
1.145 + if ref and self.is_compatible_callable(location, object_type, ref):
1.146 attrs.add(("<module>", object_type, ref))
1.147
1.148 return attrs
1.149
1.150 + def is_compatible_callable(self, location, object_type, ref):
1.151 +
1.152 + """
1.153 + Return whether any invocation at 'location' involving an attribute of
1.154 + 'object_type' identified by 'ref' is compatible with any arguments used.
1.155 + """
1.156 +
1.157 + arguments = self.reference_invocations.get(location)
1.158 + if arguments is None:
1.159 + return True
1.160 +
1.161 + objpath = ref.get_origin()
1.162 + if not objpath:
1.163 + return True
1.164 +
1.165 + parameters = self.importer.function_parameters.get(objpath)
1.166 + if not parameters:
1.167 + return True
1.168 +
1.169 + defaults = self.importer.function_defaults.get(objpath)
1.170 +
1.171 + # Determine whether the specified arguments are
1.172 + # compatible with the callable signature.
1.173 +
1.174 + if arguments >= len(parameters) - len(defaults) and \
1.175 + arguments <= len(parameters):
1.176 +
1.177 + return True
1.178 + else:
1.179 + init_item(self.reference_invocations_unsuitable, location, set)
1.180 + self.reference_invocations_unsuitable[location].add(ref)
1.181 + return False
1.182 +
1.183 # Attribute access plan formulation.
1.184
1.185 class_tests = (