# HG changeset patch # User Paul Boddie # Date 1271804309 -7200 # Node ID 10506f500308ffad12c04f34650b5245ddc76236 # Parent 84a1131131c25596f372337faf2b1b61b8fe98b8 Added node qualification of name usage, since some name usage really involves attributes and not globally recognised names. Tidied up a test, replacing previous commentary. diff -r 84a1131131c2 -r 10506f500308 micropython/inspect.py --- a/micropython/inspect.py Tue Apr 20 01:11:41 2010 +0200 +++ b/micropython/inspect.py Wed Apr 21 00:58:29 2010 +0200 @@ -315,12 +315,23 @@ return (self.namespaces[-1:] or [self])[0] - def use_name(self, name): + def use_name(self, name, node=None): + + """ + Use the given 'name' within the current namespace/unit, either in + conjunction with a particular object (if 'node' is specified and not + None) or unconditionally. + """ - "Use the given 'name' within the current namespace/unit." + if node is not None and isinstance(node, compiler.ast.Name): + self.use_attribute(node.name, name) - unit = self.get_namespace() - self.importer.use_name(name, unit.full_name()) + # For general name usage, declare usage of the given name from this + # particular unit. + + else: + unit = self.get_namespace() + self.importer.use_name(name, unit.full_name()) # Attribute usage methods. # These are convenience methods which refer to the specific namespace's @@ -351,12 +362,21 @@ self.get_namespace()._define_attribute_user(node) def use_attribute(self, name, attrname): + + "Note usage on the attribute user 'name' of the attribute 'attrname'." + return self.get_namespace()._use_attribute(name, attrname) - # Specific attribute usage, nominating specific attributes which can be - # resolved during inspection. + def use_specific_attribute(self, objname, attrname): - def use_specific_attribute(self, objname, attrname): + """ + Note attribute usage specifically on 'objname' - an object which is + known at inspection time - or in the current unit if 'objname' is None, + nominating a specific attribute 'attrname'. + + This bypasses attribute user mechanisms. + """ + from_name = self.get_namespace().full_name() objname = objname or from_name self.importer.use_specific_name(objname, attrname, from_name) @@ -387,7 +407,7 @@ "Accounting method for the unary operator 'node'." method = unary_methods[node.__class__.__name__] - self.use_name(method) + self.use_name(method, node) return self.OP(node) def _visitBinary(self, node): @@ -395,8 +415,8 @@ "Accounting method for the binary operator 'node'." left_method, right_method = binary_methods[node.__class__.__name__] - self.use_name(left_method) - self.use_name(right_method) + self.use_name(left_method, node) + self.use_name(right_method, node) return self.OP(node) def _visitFunction(self, node, name): @@ -505,7 +525,7 @@ # Declare names which will be used by generated code. - self.use_name("__getitem__") + self.use_name("__getitem__", node) # Process the assignment. @@ -534,9 +554,9 @@ # Accounting. aug_method, (left_method, right_method) = augassign_methods[node.op] - self.use_name(aug_method) - self.use_name(left_method) - self.use_name(right_method) + self.use_name(aug_method, node) + self.use_name(left_method, node) + self.use_name(right_method, node) # Process the assignment. @@ -624,14 +644,24 @@ # Accounting. # NOTE: Replicates some code in micropython.ast.visitCompare. + this_node = node + for op in node.ops: op_name, next_node = op + + # Get the applicable methods. + methods = comparison_methods[op_name] + + # Define name/attribute usage. + if methods is not None: - self.use_name(methods[0]) - self.use_name(methods[1]) + self.use_name(methods[0], this_node) + self.use_name(methods[1], next_node) elif op_name.endswith("in"): - self.use_name("__contains__") + self.use_name("__contains__", next_node) + + this_node = next_node return self.OP(node) @@ -639,7 +669,7 @@ # Register the constant, if necessary, returning the resulting object. - self.use_name(self.importer.get_constant_type_name(node.value)) + self.use_name(self.importer.get_constant_type_name(node.value), node) return self.importer.make_constant(node.value) visitContinue = NOP_ABANDON @@ -665,7 +695,7 @@ # Declare names which will be used by generated code. - self.use_name("__iter__") + self.use_name("__iter__", node.list) self.use_name("next") self.in_loop = 1 @@ -757,7 +787,7 @@ node._attrusers = self.use_attribute(expr.name, attrname) node._username = expr.name else: - self.use_name(attrname) + self.use_name(attrname, node.expr) elif self.builtins is not None: attr = self.builtins.get(attrname) @@ -916,7 +946,7 @@ visitSub = _visitBinary def visitSubscript(self, node): - self.use_name("__getitem__") + self.use_name("__getitem__", node) self.OP(node) def visitTryExcept(self, node): diff -r 84a1131131c2 -r 10506f500308 tests/attribute_access_type_restriction_conditional.py --- a/tests/attribute_access_type_restriction_conditional.py Tue Apr 20 01:11:41 2010 +0200 +++ b/tests/attribute_access_type_restriction_conditional.py Wed Apr 21 00:58:29 2010 +0200 @@ -1,7 +1,13 @@ #!/usr/bin/env python +""" +This test attempts to record the usage of 'D' in 'test_conditional' since 'f' +and 'g' are both normally required, and only 'D' provides these attributes. +Classes 'C' and 'E' should lose their methods, even 'E.f'. +""" + class C: - def f(self): + def f(self): # unused return 1 class D: @@ -12,19 +18,15 @@ return 3 class E: - def f(self): + def f(self): # unused return 4 - def h(self): + def h(self): # unused return 5 def test_conditional(obj): - # obj: C, D, E (f) - if obj.f(): # C, D, E (f) - obj.g() # D (f, g) - # else: - # ... # obj: C, D, E (f) - # # (f, g) ^ (f) + if obj.f(): + obj.g() return 2 c = C()