# HG changeset patch # User Paul Boddie # Date 1475879612 -7200 # Node ID 674502205b57c4c4861326cd6550a68c82c36521 # Parent 7be57177c47f4fa49358f4b7c59f6560ad924489 Combined attribute invocation information with attribute usage. diff -r 7be57177c47f -r 674502205b57 branching.py --- a/branching.py Fri Oct 07 21:36:08 2016 +0200 +++ b/branching.py Sat Oct 08 00:33:32 2016 +0200 @@ -77,14 +77,15 @@ else: return [b for b in self.get_all_suppliers(name) if name in b.assignments] - def set_usage(self, name, attrname): + def set_usage(self, name, attrname, invocation=False): """ - Record usage on the given 'name' of the attribute 'attrname'. + Record usage on the given 'name' of the attribute 'attrname', noting the + invocation of the attribute if 'invocation' is set to a true value. """ if self.usage.has_key(name): - self.usage[name].add(attrname) + self.usage[name].add((attrname, invocation)) def get_usage(self): @@ -482,11 +483,12 @@ return branch - def use_attribute(self, name, attrname): + def use_attribute(self, name, attrname, invocation=False): """ Indicate the use on the given 'name' of an attribute with the given - 'attrname'. + 'attrname', optionally involving an invocation of the attribute if + 'invocation' is set to a true value. Return all branches that support 'name'. """ @@ -497,7 +499,7 @@ if branches.has_key(name): for branch in branches[name]: - branch.set_usage(name, attrname) + branch.set_usage(name, attrname, invocation) return branches[name] else: return None diff -r 7be57177c47f -r 674502205b57 deducer.py --- a/deducer.py Fri Oct 07 21:36:08 2016 +0200 +++ b/deducer.py Sat Oct 08 00:33:32 2016 +0200 @@ -20,7 +20,7 @@ """ from common import first, get_attrname_from_location, get_attrnames, \ - get_name_path, init_item, make_key, sorted_output, \ + get_name_path, init_item, sorted_output, \ CommonOutput from encoders import encode_attrnames, encode_access_location, \ encode_constrained, encode_location, encode_usage, \ @@ -699,7 +699,7 @@ for attrnames in all_attrnames: attrname = get_attrnames(attrnames)[-1] access_location = (location, None, attrnames, 0) - self.add_usage_term(access_location, [attrname]) + self.add_usage_term(access_location, ((attrname, False),)) def add_usage(self, assignments, path): @@ -713,21 +713,19 @@ for i, usages in enumerate(versions): location = (path, name, None, i) - for attrnames in usages: - self.add_usage_term(location, attrnames) - - def add_usage_term(self, location, attrnames): + for usage in usages: + self.add_usage_term(location, usage) + + def add_usage_term(self, location, usage): """ - For 'location' and using 'attrnames' as a description of usage, record + For 'location' and using 'usage' as a description of usage, record in the usage index a mapping from the usage to 'location', and record in the location index a mapping from 'location' to the usage. """ - key = make_key(attrnames) - init_item(self.location_index, location, set) - self.location_index[location].add(key) + self.location_index[location].add(usage) def init_accessors(self): @@ -803,11 +801,9 @@ for location in accessor_locations: for usage in self.location_index[location]: - key = make_key(usage) - if assignment: - init_item(self.assigned_attrs, key, set) - self.assigned_attrs[key].add((path, name, attrnames)) + init_item(self.assigned_attrs, usage, set) + self.assigned_attrs[usage].add((path, name, attrnames)) def init_aliases(self): @@ -1010,31 +1006,31 @@ init_item(attr_types, attrname, set) attr_types[attrname].add(name) - def get_class_types_for_usage(self, attrnames): - - "Return names of classes supporting the given 'attrnames'." - - return self._get_types_for_usage(attrnames, self.attr_class_types, self.importer.all_class_attrs) - - def get_instance_types_for_usage(self, attrnames): + def get_class_types_for_usage(self, usage): + + "Return names of classes supporting the given 'usage'." + + return self._get_types_for_usage(usage, self.attr_class_types, self.importer.all_class_attrs) + + def get_instance_types_for_usage(self, usage): """ - Return names of classes whose instances support the given 'attrnames' + Return names of classes whose instances support the given 'usage' (as either class or instance attributes). """ - return self._get_types_for_usage(attrnames, self.attr_instance_types, self.importer.all_combined_attrs) - - def get_module_types_for_usage(self, attrnames): - - "Return names of modules supporting the given 'attrnames'." - - return self._get_types_for_usage(attrnames, self.attr_module_types, self.importer.all_module_attrs) - - def _get_types_for_usage(self, attrnames, attr_types, attrs): + return self._get_types_for_usage(usage, self.attr_instance_types, self.importer.all_combined_attrs) + + def get_module_types_for_usage(self, usage): + + "Return names of modules supporting the given 'usage'." + + return self._get_types_for_usage(usage, self.attr_module_types, self.importer.all_module_attrs) + + def _get_types_for_usage(self, usage, attr_types, attrs): """ - For the given 'attrnames' representing attribute usage, return types + For the given 'usage' representing attribute usage, return types recorded in the 'attr_types' attribute-to-types mapping that support such usage, with the given 'attrs' type-to-attributes mapping used to quickly assess whether a type supports all of the stated attributes. @@ -1042,9 +1038,13 @@ # Where no attributes are used, any type would be acceptable. - if not attrnames: + if not usage: return attrs.keys() + attrnames = [] + for attrname, invocation in usage: + attrnames.append(attrname) + types = [] # Obtain types supporting the first attribute name... @@ -1389,7 +1389,7 @@ else: self.init_definition_details(location) - self.record_types_for_usage(location, [attrname]) + self.record_types_for_usage(location, [(attrname, False)]) constrained = location in self.accessor_constrained and constrained @@ -1418,7 +1418,7 @@ 'attrname' for type deduction. """ - usage = [attrname] + usage = ((attrname, False),) class_types = self.get_class_types_for_usage(usage) only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types) diff -r 7be57177c47f -r 674502205b57 encoders.py --- a/encoders.py Fri Oct 07 21:36:08 2016 +0200 +++ b/encoders.py Sat Oct 08 00:33:32 2016 +0200 @@ -41,9 +41,22 @@ all_attrnames = [] for t in usage: - all_attrnames.append(t) + attrname, invocation = t + all_attrnames.append("%s%s" % (attrname, invocation and "!" or "")) return ", ".join(all_attrnames) or "{}" +def decode_usage(s): + + "Decode attribute details from 's'." + + all_attrnames = set() + for attrname_str in s.split(", "): + all_attrnames.add((attrname_str.rstrip("!"), attrname_str.endswith("!"))) + + all_attrnames = list(all_attrnames) + all_attrnames.sort() + return tuple(all_attrnames) + def encode_access_location(t): "Encode the access location 't'." diff -r 7be57177c47f -r 674502205b57 inspector.py --- a/inspector.py Fri Oct 07 21:36:08 2016 +0200 +++ b/inspector.py Sat Oct 08 00:33:32 2016 +0200 @@ -43,6 +43,7 @@ self.in_class = False self.in_conditional = False + self.in_invocation = False self.global_attr_accesses = {} # Usage tracking. @@ -363,7 +364,11 @@ # Obtain any completed chain and return the reference to it. + in_invocation = self.in_invocation + self.in_invocation = False name_ref = self.process_attribute_chain(n) + self.in_invocation = in_invocation + if self.have_access_expression(n): return name_ref @@ -440,7 +445,7 @@ # Record attribute usage in the tracker, and record the branch # information for the access. - branches = tracker.use_attribute(name, attrname) + branches = tracker.use_attribute(name, attrname, self.in_invocation) if not branches: raise InspectError("Name %s is accessed using %s before an assignment." % ( @@ -707,7 +712,10 @@ try: # Process the expression, obtaining any identified reference. + in_invocation = self.in_invocation + self.in_invocation = True name_ref = self.process_structure_node(n.node) + self.in_invocation = in_invocation # Process the arguments. diff -r 7be57177c47f -r 674502205b57 modules.py --- a/modules.py Fri Oct 07 21:36:08 2016 +0200 +++ b/modules.py Sat Oct 08 00:33:32 2016 +0200 @@ -21,7 +21,7 @@ """ from common import init_item, remove_items, CommonModule -from encoders import decode_modifier_term, encode_modifiers, encode_usage +from encoders import decode_modifier_term, decode_usage, encode_modifiers, encode_usage from referencing import decode_reference, Reference from results import ResolvedNameRef import sys @@ -636,16 +636,8 @@ if attrnames == "{}": all_attrnames = () else: - # Decode attribute details for each usage description. - - all_attrnames = set() - for attrname_str in attrnames.split(", "): - all_attrnames.add(attrname_str) - - all_attrnames = list(all_attrnames) - all_attrnames.sort() - - all_usages.add(tuple(all_attrnames)) + all_attrnames = decode_usage(attrnames) + all_usages.add(all_attrnames) d.append(all_usages)