# HG changeset patch # User Paul Boddie # Date 1202257691 -3600 # Node ID 5d8f34d7ce7fb70927985e8de1fb1514dab537b2 # Parent dc9b0b8fa3af2068c1c9166fbfe5f8be83274e0b Changed namespaces to return Attr objects and the visitor to pass around such objects for use with base class resolution and attribute access. Namespaces still accept "bare" objects, not Attr objects. Removed the Self class, adding a name attribute to Attr objects to compensate. Moved assignment tracking into the Attr objects so that there is a possibility of certain characteristics of names being checked during module inspection; for example, base classes can be checked to see if they are sufficiently constant. Fixed locals, all_locals results in the Function class. diff -r dc9b0b8fa3af -r 5d8f34d7ce7f micropython/inspect.py --- a/micropython/inspect.py Tue Feb 05 23:54:52 2008 +0100 +++ b/micropython/inspect.py Wed Feb 06 01:28:11 2008 +0100 @@ -111,11 +111,7 @@ self.namespace = {} self.globals = set() self.global_namespace = global_namespace - - # Number of assignments per name. - - self.assignments = {} - self.assignment_values = {} + self.attr_position = 0 def __getitem__(self, name): return self.namespace[name] @@ -133,31 +129,24 @@ self.global_namespace.set(name, value, 0) def set(self, name, value, single_assignment=1): - self.namespace[name] = value + attr = self._set(name, value) - # Record the number of assignments to each name. # NOTE: Insist on assignments with known values. if value is not None: - if not self.assignments.has_key(name): - self.assignment_values[name] = set() - if single_assignment: - self.assignments[name] = 1 - else: - self.assignments[name] = AtLeast(1) - else: - if single_assignment: - self.assignments[name] += 1 - else: - self.assignments[name] += AtLeast(1) - self.assignment_values[name].add(value) + attr.update(value, single_assignment) def set_module(self, name, value): - self.namespace[name] = value - if not self.assignments.has_key(name): - self.assignment_values[name] = set() - self.assignments[name] = 1 - self.assignment_values[name].add(value) + attr = self._set(name, value) + if attr.assignments is None: + attr.assignments = 1 + attr.assignment_values.add(value) + + def _set(self, name, value): + if not self.namespace.has_key(name): + self.namespace[name] = Attr(self.attr_position, self, name, value) + self.attr_position += 1 + return self.namespace[name] def __delitem__(self, name): del self.namespace[name] @@ -202,14 +191,32 @@ "An attribute entry." - def __init__(self, position, parent, assignments=None, value=None): + def __init__(self, position, parent, name, value=None, assignments=None): self.position = position self.parent = parent - self.assignments = assignments + self.name = name self.value = value + # Number of assignments per name. + + self.assignments = assignments + self.assignment_values = set() + + def update(self, value, single_assignment): + if self.assignments is None: + if single_assignment: + self.assignments = 1 + else: + self.assignments = AtLeast(1) + else: + if single_assignment: + self.assignments += 1 + else: + self.assignments += AtLeast(1) + self.assignment_values.add(value) + def __repr__(self): - return "Attr(%d, %r, %r, %r)" % (self.position, self.parent, self.assignments, self.value) + return "Attr(%r, %r, %r, %r, %r)" % (self.position, self.parent, self.name, self.value, self.assignments) class Const: @@ -246,8 +253,6 @@ self.all_instattr = None # cache for instance_attributes self.all_instattr_names = None # from all_instattr - self.classattr = None # cache for class_attributes - self.classattr_names = None # from classattr self.all_classattr = None # cache for all_class_attributes self.all_classattr_names = None # from all_classattr self.allattr = None # cache for all_attributes @@ -267,26 +272,15 @@ def add_instance_attribute(self, name): self.instattr.add(name) - def class_attribute_names(self): - - "Return the attribute names provided by this class only." + "Return the attribute names provided by this class only." - if self.classattr_names is None: - self.class_attributes() - return self.classattr_names + class_attribute_names = NamespaceDict.keys def class_attributes(self): "Return class attributes provided by this class only." - if self.classattr is None: - self.classattr = {} - self.classattr_names = self.keys() - - for i, name in enumerate(self.classattr_names): - self.classattr[name] = Attr(i, self, self.get_assignments(name), self[name]) - - return self.classattr + return self def all_class_attribute_names(self): @@ -342,7 +336,7 @@ instattr.update(self.instattr) for i, name in enumerate(instattr): - self.all_instattr[name] = Attr(i, None) + self.all_instattr[name] = Attr(i, None, name) self.all_instattr_names = self.all_instattr.keys() @@ -370,7 +364,7 @@ for i, name in enumerate(self.instance_attribute_names()): if self.allattr.has_key(name): print "Instance attribute %r in %r overrides class attribute." % (name, self) - self.allattr[name] = Attr(i, None) + self.allattr[name] = Attr(i, None, name) return self.allattr class Function(NamespaceDict, Naming): @@ -386,8 +380,9 @@ self.has_dstar = has_dstar self.node = node + # Caches. + self.localnames = None # cache for locals - self.alllocalnames = None # cache for all_locals # Add parameters to the namespace. @@ -428,28 +423,23 @@ parameters[name] = i return parameters + def all_locals(self): + + "Return a dictionary mapping names to local and parameter details." + + return self + def locals(self): "Return a dictionary mapping names to local details." if self.localnames is None: self.localnames = {} - start = len(self.argnames) - for i, name in enumerate(self.keys()): - self.localnames[name] = Attr(start + i, None) + self.localnames.update(self.all_locals()) + for name in self.argnames: + del self.localnames[name] return self.localnames - def all_locals(self): - - "Return a dictionary mapping names to local and parameter details." - - if self.alllocalnames is None: - self.alllocalnames = {} - self.alllocalnames.update(self.locals()) - for i, name in enumerate(self.argnames): - self.alllocalnames[name] = Attr(i, None) - return self.alllocalnames - class UnresolvedName(NamespaceDict, Naming): "A module, class or function which was mentioned but could not be imported." @@ -476,11 +466,6 @@ NamespaceDict.__init__(self, self) self.name = name - # Module attributes. - - self.modattr = None # cache for module_attributes - self.modattr_names = None # from modattr - # Complete lists of classes and functions. self.all_objects = set() @@ -507,25 +492,15 @@ # Attribute methods. - def module_attribute_names(self): - - "Return the module attribute names provided by the module." + "Return the module attribute names provided by the module." - if self.modattr_names is None: - self.module_attributes() - return self.modattr_names + module_attribute_names = NamespaceDict.keys def module_attributes(self): "Return a dictionary mapping names to module attributes." - if self.modattr is None: - self.modattr = {} - self.modattr_names = self.keys() - for i, name in enumerate(self.modattr_names): - self.modattr[name] = Attr(i, self, self.get_assignments(name), self[name]) - - return self.modattr + return self def constants(self): @@ -654,7 +629,7 @@ def visitAssAttr(self, node): expr = self.dispatch(node.expr) - if expr is not None and isinstance(expr, Self): + if isinstance(expr, Attr) and expr.name == "self": self.store_instance_attr(node.attrname) return None @@ -664,7 +639,10 @@ return None def visitAssName(self, node): - self.store(node.name, self.expr) + if isinstance(self.expr, Attr): + self.store(node.name, self.expr.value) + else: + self.store(node.name, self.expr) return None visitAssTuple = visitAssList @@ -689,10 +667,14 @@ else: cls = Class(node.name, self.get_parent().full_name(), self, node) for base in node.bases: - base_ref = self.dispatch(base) - if base_ref is None: - raise InspectError, "Base class %r for class %r in %r is not found: it may be hidden in some way." % (base, self, cls) - cls.add_base(base_ref) + expr = self.dispatch(base) + if isinstance(expr, Attr): + if expr.assignments != 1: + print "Base class %r for class %r in %r may not be constant." % (base, cls, self) + else: + cls.add_base(expr.value) + else: # if expr is None: + raise InspectError, "Base class %r for class %r in %r is not found: it may be hidden in some way." % (base, cls, self) # Make a back reference from the node for code generation. @@ -751,7 +733,7 @@ if name != "*": if module is not None and module.namespace.has_key(name): attr = module[name] - self.store(alias or name, attr) + self.store(alias or name, attr.value) if isinstance(attr, Module) and not attr.loaded: self.importer.load(attr.name) @@ -763,7 +745,7 @@ if module is not None: for n in module.namespace.keys(): attr = module[n] - self.store(n, attr) + self.store(n, attr.value) if isinstance(attr, Module) and not attr.loaded: self.importer.load(attr.name) @@ -809,11 +791,12 @@ def visitGetattr(self, node): expr = self.dispatch(node.expr) - if expr is not None: - if isinstance(expr, Module): - return expr.namespace.get(node.attrname) - elif isinstance(expr, UnresolvedName): - return UnresolvedName(node.attrname, expr.full_name(), self) + if isinstance(expr, Attr): + value = expr.value + if isinstance(value, Module): + return value.namespace.get(node.attrname) + elif isinstance(value, UnresolvedName): + return UnresolvedName(node.attrname, value.full_name(), self) return builtins.get(node.attrname) def visitGlobal(self, node): @@ -869,9 +852,7 @@ def visitName(self, node): name = node.name - if name == "self": - return Self() - elif self.namespaces and self.namespaces[-1].has_key(name): + if self.namespaces and self.namespaces[-1].has_key(name): return self.namespaces[-1][name] elif self.has_key(name): return self[name] @@ -936,12 +917,6 @@ visitYield = NOP -class Self: - - "A reference to an object within a method." - - pass - class Global: """ diff -r dc9b0b8fa3af -r 5d8f34d7ce7f tests/classes.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/classes.py Wed Feb 06 01:28:11 2008 +0100 @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +class A: + pass + +class B: + pass + +for x in A, B: + class C(x): + pass + +# vim: tabstop=4 expandtab shiftwidth=4