1.1 --- a/deducer.py Sat Oct 15 23:32:15 2016 +0200
1.2 +++ b/deducer.py Sat Oct 15 23:34:29 2016 +0200
1.3 @@ -399,7 +399,8 @@
1.4 locations.sort()
1.5
1.6 for location in locations:
1.7 - name, test, test_type, base, traversed, traversal_modes, attrnames, context, \
1.8 + name, test, test_type, base, traversed, traversal_modes, attrnames, \
1.9 + context, context_test, \
1.10 first_method, final_method, attr = self.access_plans[location]
1.11
1.12 print >>f_attrs, encode_access_location(location), \
1.13 @@ -408,7 +409,8 @@
1.14 ".".join(traversed) or "{}", \
1.15 ".".join(traversal_modes) or "{}", \
1.16 ".".join(attrnames) or "{}", \
1.17 - context, first_method, final_method, attr or "{}"
1.18 + context, context_test, \
1.19 + first_method, final_method, attr or "{}"
1.20
1.21 finally:
1.22 f_attrs.close()
1.23 @@ -1410,16 +1412,26 @@
1.24 'attrname' for type deduction.
1.25 """
1.26
1.27 + (class_types,
1.28 + only_instance_types,
1.29 + module_types) = self.get_types_for_attribute(attrname)
1.30 +
1.31 + self.init_reference_details(access_location)
1.32 +
1.33 + self.identify_reference_attributes(access_location, attrname, class_types, only_instance_types, module_types, False)
1.34 + self.record_reference_types(access_location, class_types, only_instance_types, module_types, False)
1.35 +
1.36 + def get_types_for_attribute(self, attrname):
1.37 +
1.38 + "Return class, instance-only and module types supporting 'attrname'."
1.39 +
1.40 usage = ((attrname, False),)
1.41
1.42 class_types = self.get_class_types_for_usage(usage)
1.43 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types)
1.44 module_types = self.get_module_types_for_usage(usage)
1.45
1.46 - self.init_reference_details(access_location)
1.47 -
1.48 - self.identify_reference_attributes(access_location, attrname, class_types, only_instance_types, module_types, False)
1.49 - self.record_reference_types(access_location, class_types, only_instance_types, module_types, False)
1.50 + return class_types, only_instance_types, module_types
1.51
1.52 def record_types_for_alias(self, accessor_location):
1.53
1.54 @@ -1761,6 +1773,7 @@
1.55 * access modes for each of the unambiguously-traversed attributes
1.56 * remaining attributes needing to be tested and traversed
1.57 * details of the context
1.58 + * any test to apply to the context
1.59 * the method of obtaining the final attribute
1.60 * any static final attribute
1.61 """
1.62 @@ -1855,19 +1868,25 @@
1.63 elif not base and ref:
1.64 dynamic_base = ref.get_origin()
1.65
1.66 + # Determine initial accessor details.
1.67 +
1.68 + accessor = base or dynamic_base
1.69 + accessor_kind = len(accessor_kinds) == 1 and first(accessor_kinds) or None
1.70 + provider_kind = len(provider_kinds) == 1 and first(provider_kinds) or None
1.71 +
1.72 + # Traverse remaining attributes.
1.73 +
1.74 traversed = []
1.75 traversal_modes = []
1.76 - provider_kind = first(provider_kinds)
1.77 -
1.78 - # Traverse remaining attributes.
1.79
1.80 while len(attrs) == 1:
1.81 attr = first(attrs)
1.82 - accessor_kind = attr.get_kind()
1.83
1.84 traversed.append(attrname)
1.85 traversal_modes.append(accessor_kind == provider_kind and "object" or "class")
1.86
1.87 + # Consume attribute names providing unambiguous attributes.
1.88 +
1.89 del remaining[0]
1.90
1.91 if not remaining:
1.92 @@ -1880,11 +1899,18 @@
1.93 traversed = []
1.94 traversal_modes = []
1.95
1.96 - # Get the next attribute.
1.97 + # Get the access details.
1.98
1.99 attrname = remaining[0]
1.100 + accessor = attr.get_origin()
1.101 + accessor_kind = attr.get_kind()
1.102 + provider_kind = self.importer.get_attribute_provider(attr, attrname)
1.103 + accessor_kinds = [accessor_kind]
1.104 + provider_kinds = [provider_kind]
1.105 +
1.106 + # Get the next attribute.
1.107 +
1.108 attrs = self.importer.get_attributes(attr, attrname)
1.109 - provider_kind = self.importer.get_attribute_provider(attr, attrname)
1.110
1.111 # Where many attributes are suggested, no single attribute identity can
1.112 # be loaded.
1.113 @@ -1925,13 +1951,111 @@
1.114 first_method = "check" + (object_relative and "-object" or "") + \
1.115 (class_relative and "-class" or "")
1.116
1.117 + # Determine whether an unbound method is being accessed via an instance,
1.118 + # requiring a context test.
1.119 +
1.120 + context_test = "ignore"
1.121 +
1.122 + # Assignments do not employ the context.
1.123 +
1.124 + if is_assignment:
1.125 + pass
1.126 +
1.127 + # Obtain a selection of possible attributes if no unambiguous attribute
1.128 + # was identified.
1.129 +
1.130 + elif not attr:
1.131 +
1.132 + # Use previously-deduced attributes for a simple ambiguous access.
1.133 + # Otherwise, use the final attribute name to obtain possible
1.134 + # attributes.
1.135 +
1.136 + if len(remaining) > 1:
1.137 + attrname = remaining[-1]
1.138 +
1.139 + (class_types,
1.140 + only_instance_types,
1.141 + module_types) = self.get_types_for_attribute(attrname)
1.142 +
1.143 + all_accessor_kinds = set()
1.144 + all_provider_kinds = set()
1.145 +
1.146 + if class_types:
1.147 + all_accessor_kinds.add("<class>")
1.148 + all_accessor_kinds.add("<instance>")
1.149 + all_provider_kinds.add("<class>")
1.150 + if only_instance_types:
1.151 + all_accessor_kinds.add("<instance>")
1.152 + all_provider_kinds.add("<instance>")
1.153 + if module_types:
1.154 + all_accessor_kinds.add("<module>")
1.155 + all_provider_kinds.add("<module>")
1.156 +
1.157 + attrs = set()
1.158 + for type in combine_types(class_types, only_instance_types, module_types):
1.159 + attrs.update(self.importer.get_attributes(type, attrname))
1.160 +
1.161 + always_unbound = True
1.162 + have_function = False
1.163 + have_var = False
1.164 +
1.165 + # Determine whether all attributes are unbound methods and whether
1.166 + # functions or unidentified attributes occur.
1.167 +
1.168 + for attr in attrs:
1.169 + always_unbound = always_unbound and attr.has_kind("<function>") and attr.name_parent() == attr.parent()
1.170 + have_function = have_function or attr.has_kind("<function>")
1.171 + have_var = have_var or attr.has_kind("<var>")
1.172 +
1.173 + # Test for class-via-instance accesses.
1.174 +
1.175 + if accessor_kind == "<instance>" and \
1.176 + provider_kind == "<class>":
1.177 +
1.178 + if always_unbound:
1.179 + context_test = "replace"
1.180 + else:
1.181 + context_test = "test"
1.182 +
1.183 + # Test for the presence of class-via-instance accesses.
1.184 +
1.185 + elif "<instance>" in accessor_kinds and \
1.186 + "<class>" in provider_kinds and \
1.187 + (have_function or have_var):
1.188 +
1.189 + context_test = "test"
1.190 +
1.191 + # With an unambiguous attribute, determine whether a test is needed.
1.192 +
1.193 + elif accessor_kind == "<instance>" and \
1.194 + provider_kind == "<class>" and \
1.195 + (attr.has_kind("<var>") or
1.196 + attr.has_kind("<function>") and
1.197 + attr.name_parent() == attr.parent()):
1.198 +
1.199 + if attr.has_kind("<var>"):
1.200 + context_test = "test"
1.201 + else:
1.202 + context_test = "replace"
1.203 +
1.204 + # With an unambiguous attribute with ambiguity in the access method,
1.205 + # generate a test.
1.206 +
1.207 + elif "<instance>" in accessor_kinds and \
1.208 + "<class>" in provider_kinds and \
1.209 + (attr.has_kind("<var>") or
1.210 + attr.has_kind("<function>") and
1.211 + attr.name_parent() == attr.parent()):
1.212 +
1.213 + context_test = "test"
1.214 +
1.215 # Determine the nature of the context.
1.216
1.217 - context = is_assignment and "unset" or \
1.218 + context = context_test == "ignore" and "unset" or \
1.219 len(traversed + remaining) == 1 and \
1.220 (base and "base" or "original-accessor") or \
1.221 "final-accessor"
1.222
1.223 - return name, test, test_type, base, traversed, traversal_modes, remaining, context, first_method, final_method, origin
1.224 + return name, test, test_type, base, traversed, traversal_modes, remaining, context, context_test, first_method, final_method, origin
1.225
1.226 # vim: tabstop=4 expandtab shiftwidth=4