1.1 --- a/deducer.py Mon Oct 17 15:39:07 2016 +0200
1.2 +++ b/deducer.py Mon Oct 17 18:56:34 2016 +0200
1.3 @@ -19,7 +19,8 @@
1.4 this program. If not, see <http://www.gnu.org/licenses/>.
1.5 """
1.6
1.7 -from common import first, get_attrname_from_location, get_attrnames, \
1.8 +from common import first, get_assigned_attributes, \
1.9 + get_attrname_from_location, get_attrnames, \
1.10 get_invoked_attributes, get_name_path, init_item, \
1.11 sorted_output, CommonOutput
1.12 from encoders import encode_attrnames, encode_access_location, \
1.13 @@ -708,7 +709,7 @@
1.14 for attrnames in all_attrnames:
1.15 attrname = get_attrnames(attrnames)[-1]
1.16 access_location = (location, None, attrnames, 0)
1.17 - self.add_usage_term(access_location, ((attrname, False),))
1.18 + self.add_usage_term(access_location, ((attrname, False, False),))
1.19
1.20 def add_usage(self, assignments, path):
1.21
1.22 @@ -985,10 +986,11 @@
1.23 "Identify the types that can support each attribute name."
1.24
1.25 self._init_attr_type_index(self.attr_class_types, self.importer.all_class_attrs)
1.26 - self._init_attr_type_index(self.attr_instance_types, self.importer.all_combined_attrs)
1.27 + self._init_attr_type_index(self.attr_instance_types, self.importer.all_instance_attrs, True)
1.28 + self._init_attr_type_index(self.attr_instance_types, self.importer.all_combined_attrs, False)
1.29 self._init_attr_type_index(self.attr_module_types, self.importer.all_module_attrs)
1.30
1.31 - def _init_attr_type_index(self, attr_types, attrs):
1.32 + def _init_attr_type_index(self, attr_types, attrs, assignment=None):
1.33
1.34 """
1.35 Initialise the 'attr_types' attribute-to-types mapping using the given
1.36 @@ -997,8 +999,20 @@
1.37
1.38 for name, attrnames in attrs.items():
1.39 for attrname in attrnames:
1.40 - init_item(attr_types, attrname, set)
1.41 - attr_types[attrname].add(name)
1.42 +
1.43 + # Permit general access for certain kinds of object.
1.44 +
1.45 + if assignment is None:
1.46 + init_item(attr_types, (attrname, False), set)
1.47 + init_item(attr_types, (attrname, True), set)
1.48 + attr_types[(attrname, False)].add(name)
1.49 + attr_types[(attrname, True)].add(name)
1.50 +
1.51 + # Restrict attribute assignment for instances.
1.52 +
1.53 + else:
1.54 + init_item(attr_types, (attrname, assignment), set)
1.55 + attr_types[(attrname, assignment)].add(name)
1.56
1.57 def get_class_types_for_usage(self, usage):
1.58
1.59 @@ -1035,21 +1049,19 @@
1.60 if not usage:
1.61 return attrs.keys()
1.62
1.63 - attrnames = []
1.64 - for attrname, invocation in usage:
1.65 - attrnames.append(attrname)
1.66 -
1.67 - types = []
1.68 -
1.69 - # Obtain types supporting the first attribute name...
1.70 -
1.71 - for name in attr_types.get(attrnames[0]) or []:
1.72 -
1.73 + keys = []
1.74 + for attrname, invocation, assignment in usage:
1.75 + keys.append((attrname, assignment))
1.76 +
1.77 + # Obtain types supporting the first (attribute name, assignment) key...
1.78 +
1.79 + types = set(attr_types.get(keys[0]) or [])
1.80 +
1.81 + for key in keys[1:]:
1.82 +
1.83 # Record types that support all of the other attributes as well.
1.84
1.85 - _attrnames = attrs[name]
1.86 - if set(attrnames).issubset(_attrnames):
1.87 - types.append(name)
1.88 + types.intersection_update(attr_types.get(key) or [])
1.89
1.90 return types
1.91
1.92 @@ -1241,6 +1253,7 @@
1.93 """
1.94
1.95 unit_path, name, attrnames, version = location
1.96 + have_assignments = get_assigned_attributes(usage)
1.97
1.98 # Detect any initialised name for the location.
1.99
1.100 @@ -1249,7 +1262,7 @@
1.101 if ref:
1.102 (class_types, only_instance_types, module_types,
1.103 _function_types, _var_types) = separate_types([ref])
1.104 - return class_types, only_instance_types, module_types, True, False
1.105 + return class_types, only_instance_types, module_types, True, have_assignments
1.106
1.107 # Retrieve the recorded types for the usage.
1.108
1.109 @@ -1261,7 +1274,7 @@
1.110 # for names involved with attribute accesses.
1.111
1.112 if not name:
1.113 - return class_types, only_instance_types, module_types, False, False
1.114 + return class_types, only_instance_types, module_types, False, have_assignments
1.115
1.116 # Obtain references to known objects.
1.117
1.118 @@ -1271,7 +1284,8 @@
1.119 self.constrain_types(path, class_types, only_instance_types, module_types)
1.120
1.121 if constrained_specific:
1.122 - return class_types, only_instance_types, module_types, constrained_specific, constrained_specific
1.123 + return class_types, only_instance_types, module_types, constrained_specific, \
1.124 + constrained_specific or have_assignments
1.125
1.126 # Constrain "self" references.
1.127
1.128 @@ -1279,9 +1293,9 @@
1.129 t = self.constrain_self_reference(unit_path, class_types, only_instance_types)
1.130 if t:
1.131 class_types, only_instance_types, module_types, constrained = t
1.132 - return class_types, only_instance_types, module_types, constrained, False
1.133 -
1.134 - return class_types, only_instance_types, module_types, False, False
1.135 + return class_types, only_instance_types, module_types, constrained, have_assignments
1.136 +
1.137 + return class_types, only_instance_types, module_types, False, have_assignments
1.138
1.139 def constrain_self_reference(self, unit_path, class_types, only_instance_types):
1.140
1.141 @@ -1381,7 +1395,7 @@
1.142
1.143 else:
1.144 self.init_definition_details(location)
1.145 - self.record_types_for_usage(location, [(attrname, False)])
1.146 + self.record_types_for_usage(location, [(attrname, False, False)])
1.147
1.148 constrained = location in self.accessor_constrained and constrained
1.149
1.150 @@ -1403,7 +1417,8 @@
1.151
1.152 invocations = get_invoked_attributes(usage)
1.153
1.154 - self.record_reference_types(accessor_location, class_types, instance_types, module_types, constrained, constrained_specific, invocations)
1.155 + self.record_reference_types(accessor_location, class_types, instance_types,
1.156 + module_types, constrained, constrained_specific, invocations)
1.157
1.158 def record_types_for_attribute(self, access_location, attrname):
1.159
1.160 @@ -1425,7 +1440,7 @@
1.161
1.162 "Return class, instance-only and module types supporting 'attrname'."
1.163
1.164 - usage = ((attrname, False),)
1.165 + usage = ((attrname, False, False),)
1.166
1.167 class_types = self.get_class_types_for_usage(usage)
1.168 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types)