1.1 --- a/branching.py Mon Oct 17 15:39:07 2016 +0200
1.2 +++ b/branching.py Mon Oct 17 18:56:34 2016 +0200
1.3 @@ -77,15 +77,17 @@
1.4 else:
1.5 return [b for b in self.get_all_suppliers(name) if name in b.assignments]
1.6
1.7 - def set_usage(self, name, attrname, invocation=False):
1.8 + def set_usage(self, name, attrname, invocation=False, assignment=False):
1.9
1.10 """
1.11 Record usage on the given 'name' of the attribute 'attrname', noting the
1.12 - invocation of the attribute if 'invocation' is set to a true value.
1.13 + invocation of the attribute if 'invocation' is set to a true value, or
1.14 + noting the assignment of the attribute if 'assignment' is set to a true
1.15 + value.
1.16 """
1.17
1.18 if self.usage.has_key(name):
1.19 - self.usage[name].add((attrname, invocation))
1.20 + self.usage[name].add((attrname, invocation, assignment))
1.21
1.22 def get_usage(self):
1.23
1.24 @@ -483,12 +485,13 @@
1.25
1.26 return branch
1.27
1.28 - def use_attribute(self, name, attrname, invocation=False):
1.29 + def use_attribute(self, name, attrname, invocation=False, assignment=False):
1.30
1.31 """
1.32 Indicate the use on the given 'name' of an attribute with the given
1.33 'attrname', optionally involving an invocation of the attribute if
1.34 - 'invocation' is set to a true value.
1.35 + 'invocation' is set to a true value, or involving an assignment of the
1.36 + attribute if 'assignment' is set to a true value.
1.37
1.38 Return all branches that support 'name'.
1.39 """
1.40 @@ -499,7 +502,7 @@
1.41
1.42 if branches.has_key(name):
1.43 for branch in branches[name]:
1.44 - branch.set_usage(name, attrname, invocation)
1.45 + branch.set_usage(name, attrname, invocation, assignment)
1.46 return branches[name]
1.47 else:
1.48 return None
3.1 --- a/deducer.py Mon Oct 17 15:39:07 2016 +0200
3.2 +++ b/deducer.py Mon Oct 17 18:56:34 2016 +0200
3.3 @@ -19,7 +19,8 @@
3.4 this program. If not, see <http://www.gnu.org/licenses/>.
3.5 """
3.6
3.7 -from common import first, get_attrname_from_location, get_attrnames, \
3.8 +from common import first, get_assigned_attributes, \
3.9 + get_attrname_from_location, get_attrnames, \
3.10 get_invoked_attributes, get_name_path, init_item, \
3.11 sorted_output, CommonOutput
3.12 from encoders import encode_attrnames, encode_access_location, \
3.13 @@ -708,7 +709,7 @@
3.14 for attrnames in all_attrnames:
3.15 attrname = get_attrnames(attrnames)[-1]
3.16 access_location = (location, None, attrnames, 0)
3.17 - self.add_usage_term(access_location, ((attrname, False),))
3.18 + self.add_usage_term(access_location, ((attrname, False, False),))
3.19
3.20 def add_usage(self, assignments, path):
3.21
3.22 @@ -985,10 +986,11 @@
3.23 "Identify the types that can support each attribute name."
3.24
3.25 self._init_attr_type_index(self.attr_class_types, self.importer.all_class_attrs)
3.26 - self._init_attr_type_index(self.attr_instance_types, self.importer.all_combined_attrs)
3.27 + self._init_attr_type_index(self.attr_instance_types, self.importer.all_instance_attrs, True)
3.28 + self._init_attr_type_index(self.attr_instance_types, self.importer.all_combined_attrs, False)
3.29 self._init_attr_type_index(self.attr_module_types, self.importer.all_module_attrs)
3.30
3.31 - def _init_attr_type_index(self, attr_types, attrs):
3.32 + def _init_attr_type_index(self, attr_types, attrs, assignment=None):
3.33
3.34 """
3.35 Initialise the 'attr_types' attribute-to-types mapping using the given
3.36 @@ -997,8 +999,20 @@
3.37
3.38 for name, attrnames in attrs.items():
3.39 for attrname in attrnames:
3.40 - init_item(attr_types, attrname, set)
3.41 - attr_types[attrname].add(name)
3.42 +
3.43 + # Permit general access for certain kinds of object.
3.44 +
3.45 + if assignment is None:
3.46 + init_item(attr_types, (attrname, False), set)
3.47 + init_item(attr_types, (attrname, True), set)
3.48 + attr_types[(attrname, False)].add(name)
3.49 + attr_types[(attrname, True)].add(name)
3.50 +
3.51 + # Restrict attribute assignment for instances.
3.52 +
3.53 + else:
3.54 + init_item(attr_types, (attrname, assignment), set)
3.55 + attr_types[(attrname, assignment)].add(name)
3.56
3.57 def get_class_types_for_usage(self, usage):
3.58
3.59 @@ -1035,21 +1049,19 @@
3.60 if not usage:
3.61 return attrs.keys()
3.62
3.63 - attrnames = []
3.64 - for attrname, invocation in usage:
3.65 - attrnames.append(attrname)
3.66 -
3.67 - types = []
3.68 -
3.69 - # Obtain types supporting the first attribute name...
3.70 -
3.71 - for name in attr_types.get(attrnames[0]) or []:
3.72 -
3.73 + keys = []
3.74 + for attrname, invocation, assignment in usage:
3.75 + keys.append((attrname, assignment))
3.76 +
3.77 + # Obtain types supporting the first (attribute name, assignment) key...
3.78 +
3.79 + types = set(attr_types.get(keys[0]) or [])
3.80 +
3.81 + for key in keys[1:]:
3.82 +
3.83 # Record types that support all of the other attributes as well.
3.84
3.85 - _attrnames = attrs[name]
3.86 - if set(attrnames).issubset(_attrnames):
3.87 - types.append(name)
3.88 + types.intersection_update(attr_types.get(key) or [])
3.89
3.90 return types
3.91
3.92 @@ -1241,6 +1253,7 @@
3.93 """
3.94
3.95 unit_path, name, attrnames, version = location
3.96 + have_assignments = get_assigned_attributes(usage)
3.97
3.98 # Detect any initialised name for the location.
3.99
3.100 @@ -1249,7 +1262,7 @@
3.101 if ref:
3.102 (class_types, only_instance_types, module_types,
3.103 _function_types, _var_types) = separate_types([ref])
3.104 - return class_types, only_instance_types, module_types, True, False
3.105 + return class_types, only_instance_types, module_types, True, have_assignments
3.106
3.107 # Retrieve the recorded types for the usage.
3.108
3.109 @@ -1261,7 +1274,7 @@
3.110 # for names involved with attribute accesses.
3.111
3.112 if not name:
3.113 - return class_types, only_instance_types, module_types, False, False
3.114 + return class_types, only_instance_types, module_types, False, have_assignments
3.115
3.116 # Obtain references to known objects.
3.117
3.118 @@ -1271,7 +1284,8 @@
3.119 self.constrain_types(path, class_types, only_instance_types, module_types)
3.120
3.121 if constrained_specific:
3.122 - return class_types, only_instance_types, module_types, constrained_specific, constrained_specific
3.123 + return class_types, only_instance_types, module_types, constrained_specific, \
3.124 + constrained_specific or have_assignments
3.125
3.126 # Constrain "self" references.
3.127
3.128 @@ -1279,9 +1293,9 @@
3.129 t = self.constrain_self_reference(unit_path, class_types, only_instance_types)
3.130 if t:
3.131 class_types, only_instance_types, module_types, constrained = t
3.132 - return class_types, only_instance_types, module_types, constrained, False
3.133 -
3.134 - return class_types, only_instance_types, module_types, False, False
3.135 + return class_types, only_instance_types, module_types, constrained, have_assignments
3.136 +
3.137 + return class_types, only_instance_types, module_types, False, have_assignments
3.138
3.139 def constrain_self_reference(self, unit_path, class_types, only_instance_types):
3.140
3.141 @@ -1381,7 +1395,7 @@
3.142
3.143 else:
3.144 self.init_definition_details(location)
3.145 - self.record_types_for_usage(location, [(attrname, False)])
3.146 + self.record_types_for_usage(location, [(attrname, False, False)])
3.147
3.148 constrained = location in self.accessor_constrained and constrained
3.149
3.150 @@ -1403,7 +1417,8 @@
3.151
3.152 invocations = get_invoked_attributes(usage)
3.153
3.154 - self.record_reference_types(accessor_location, class_types, instance_types, module_types, constrained, constrained_specific, invocations)
3.155 + self.record_reference_types(accessor_location, class_types, instance_types,
3.156 + module_types, constrained, constrained_specific, invocations)
3.157
3.158 def record_types_for_attribute(self, access_location, attrname):
3.159
3.160 @@ -1425,7 +1440,7 @@
3.161
3.162 "Return class, instance-only and module types supporting 'attrname'."
3.163
3.164 - usage = ((attrname, False),)
3.165 + usage = ((attrname, False, False),)
3.166
3.167 class_types = self.get_class_types_for_usage(usage)
3.168 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types)
5.1 --- a/inspector.py Mon Oct 17 15:39:07 2016 +0200
5.2 +++ b/inspector.py Mon Oct 17 18:56:34 2016 +0200
5.3 @@ -41,6 +41,7 @@
5.4
5.5 BasicModule.__init__(self, name, importer)
5.6
5.7 + self.in_assignment = False
5.8 self.in_class = False
5.9 self.in_conditional = False
5.10 self.in_invocation = False
5.11 @@ -342,7 +343,11 @@
5.12
5.13 elif isinstance(n, compiler.ast.AssAttr):
5.14 if expr: self.process_structure_node(expr)
5.15 +
5.16 + in_assignment = self.in_assignment
5.17 + self.in_assignment = True
5.18 self.process_attribute_access(n)
5.19 + self.in_assignment = in_assignment
5.20
5.21 # Lists and tuples are matched against the expression and their
5.22 # items assigned to expression items.
5.23 @@ -362,12 +367,19 @@
5.24
5.25 "Process the given attribute access node 'n'."
5.26
5.27 - # Obtain any completed chain and return the reference to it.
5.28 + # Parts of the attribute chain are neither invoked nor assigned.
5.29
5.30 in_invocation = self.in_invocation
5.31 self.in_invocation = False
5.32 + in_assignment = self.in_assignment
5.33 + self.in_assignment = False
5.34 +
5.35 + # Obtain any completed chain and return the reference to it.
5.36 +
5.37 name_ref = self.process_attribute_chain(n)
5.38 +
5.39 self.in_invocation = in_invocation
5.40 + self.in_assignment = in_assignment
5.41
5.42 if self.have_access_expression(n):
5.43 return name_ref
5.44 @@ -423,8 +435,6 @@
5.45 immediate_access = len(self.attrs) == 1
5.46 assignment = immediate_access and isinstance(n, compiler.ast.AssAttr)
5.47
5.48 - del self.attrs[0]
5.49 -
5.50 # Record global-based chains for subsequent resolution.
5.51
5.52 is_global = self.in_function and not self.function_locals[path].has_key(name) or \
5.53 @@ -445,7 +455,8 @@
5.54 # Record attribute usage in the tracker, and record the branch
5.55 # information for the access.
5.56
5.57 - branches = tracker.use_attribute(name, attrname, self.in_invocation)
5.58 + branches = tracker.use_attribute(name, attrname, self.in_invocation,
5.59 + self.in_assignment and immediate_access)
5.60
5.61 if not branches:
5.62 raise InspectError("Name %s is accessed using %s before an assignment." % (
5.63 @@ -453,6 +464,8 @@
5.64
5.65 self.record_branches_for_access(branches, name, attrnames)
5.66 access_number = self.record_access_details(name, attrnames, assignment)
5.67 +
5.68 + del self.attrs[0]
5.69 return AccessRef(name, attrnames, access_number)
5.70
5.71 def process_class_node(self, n):
5.72 @@ -486,10 +499,13 @@
5.73 bases.append(base_class)
5.74
5.75 # Record bases for the class and retain the class name.
5.76 + # Note that the function class does not inherit from the object class.
5.77
5.78 class_name = self.get_object_path(n.name)
5.79
5.80 - if not bases and class_name != "__builtins__.core.object":
5.81 + if not bases and class_name != "__builtins__.core.object" and \
5.82 + class_name != "__builtins__.core.function":
5.83 +
5.84 ref = self.get_object("__builtins__.object")
5.85 bases.append(ref)
5.86
5.87 @@ -509,8 +525,14 @@
5.88 self.in_class = class_name
5.89 self.set_instance_attr("__class__", Reference("<class>", class_name))
5.90 self.enter_namespace(n.name)
5.91 - self.set_name("__fn__") # special instantiator attribute
5.92 - self.set_name("__args__") # special instantiator attribute
5.93 +
5.94 + # Do not provide the special instantiator attributes on the function
5.95 + # class. Function instances provide these attributes.
5.96 +
5.97 + if class_name != "__builtins__.core.function":
5.98 + self.set_name("__fn__") # special instantiator attribute
5.99 + self.set_name("__args__") # special instantiator attribute
5.100 +
5.101 self.assign_general_local("__name__", self.get_constant("str", class_name))
5.102 self.process_structure_node(n.code)
5.103 self.exit_namespace()
5.104 @@ -710,10 +732,14 @@
5.105 self.allocate_arguments(path, n.args)
5.106
5.107 try:
5.108 - # Process the expression, obtaining any identified reference.
5.109 + # Communicate to the invocation target expression that it forms the
5.110 + # target of an invocation, potentially affecting attribute accesses.
5.111
5.112 in_invocation = self.in_invocation
5.113 self.in_invocation = True
5.114 +
5.115 + # Process the expression, obtaining any identified reference.
5.116 +
5.117 name_ref = self.process_structure_node(n.node)
5.118 self.in_invocation = in_invocation
5.119