1.1 --- a/common.py Sat Oct 08 00:34:34 2016 +0200
1.2 +++ b/common.py Sat Oct 08 00:43:13 2016 +0200
1.3 @@ -811,7 +811,7 @@
1.4 else:
1.5 return "%s.%s" % (path, name)
1.6
1.7 -# Type deduction for usage.
1.8 +# Usage-related functions.
1.9
1.10 def get_types_for_usage(attrnames, objects):
1.11
1.12 @@ -826,6 +826,17 @@
1.13 types.append(name)
1.14 return types
1.15
1.16 +def get_invoked_attributes(usage):
1.17 +
1.18 + "Obtain invoked attribute from the given 'usage'."
1.19 +
1.20 + invoked = []
1.21 + if usage:
1.22 + for attrname, invocation in usage:
1.23 + if invocation:
1.24 + invoked.append(attrname)
1.25 + return invoked
1.26 +
1.27 # Useful data.
1.28
1.29 predefined_constants = "False", "None", "NotImplemented", "True"
2.1 --- a/deducer.py Sat Oct 08 00:34:34 2016 +0200
2.2 +++ b/deducer.py Sat Oct 08 00:43:13 2016 +0200
2.3 @@ -20,8 +20,8 @@
2.4 """
2.5
2.6 from common import first, get_attrname_from_location, get_attrnames, \
2.7 - get_name_path, init_item, sorted_output, \
2.8 - CommonOutput
2.9 + get_invoked_attributes, get_name_path, init_item, \
2.10 + sorted_output, CommonOutput
2.11 from encoders import encode_attrnames, encode_access_location, \
2.12 encode_constrained, encode_location, encode_usage, \
2.13 get_kinds, test_for_kinds, test_for_type
2.14 @@ -1394,7 +1394,9 @@
2.15 constrained,
2.16 constrained_specific) = self.get_target_types(accessor_location, usage)
2.17
2.18 - self.record_reference_types(accessor_location, class_types, instance_types, module_types, constrained, constrained_specific)
2.19 + invocations = get_invoked_attributes(usage)
2.20 +
2.21 + self.record_reference_types(accessor_location, class_types, instance_types, module_types, constrained, constrained_specific, invocations)
2.22
2.23 def record_types_for_attribute(self, access_location, attrname):
2.24
2.25 @@ -1543,7 +1545,7 @@
2.26 return None
2.27
2.28 def record_reference_types(self, location, class_types, instance_types,
2.29 - module_types, constrained, constrained_specific=False):
2.30 + module_types, constrained, constrained_specific=False, invocations=None):
2.31
2.32 """
2.33 Associate attribute provider types with the given 'location', consisting
2.34 @@ -1572,12 +1574,18 @@
2.35 # Instance-only and module types support only their own kinds as
2.36 # accessors.
2.37
2.38 + path, name, version, attrnames = location
2.39 +
2.40 + if invocations:
2.41 + class_only_types = self.filter_for_invocations(class_types, invocations)
2.42 + else:
2.43 + class_only_types = class_types
2.44 +
2.45 # However, the nature of accessors can be further determined.
2.46 # Any self variable may only refer to an instance.
2.47
2.48 - path, name, version, attrnames = location
2.49 if name != "self" or not self.in_method(path):
2.50 - self.accessor_class_types[location].update(class_types)
2.51 + self.accessor_class_types[location].update(class_only_types)
2.52
2.53 if not constrained_specific:
2.54 self.accessor_instance_types[location].update(class_types)
2.55 @@ -1590,6 +1598,30 @@
2.56 if constrained:
2.57 self.accessor_constrained.add(location)
2.58
2.59 + def filter_for_invocations(self, class_types, attrnames):
2.60 +
2.61 + """
2.62 + From the given 'class_types', identify methods for the given
2.63 + 'attrnames' that are being invoked, returning a filtered collection of
2.64 + class types.
2.65 + """
2.66 +
2.67 + to_filter = set()
2.68 +
2.69 + for class_type in class_types:
2.70 + for attrname in attrnames:
2.71 + ref = self.importer.get_class_attribute(class_type, attrname)
2.72 + parent_class = ref and ref.parent()
2.73 +
2.74 + if ref and ref.has_kind("<function>") and (
2.75 + parent_class == class_type or
2.76 + class_type in self.descendants[parent_class]):
2.77 +
2.78 + to_filter.add(class_type)
2.79 + break
2.80 +
2.81 + return set(class_types).difference(to_filter)
2.82 +
2.83 def identify_reference_attributes(self, location, attrname, class_types, instance_types, module_types, constrained):
2.84
2.85 """