# HG changeset patch # User paulb@localhost.localdomain # Date 1182124071 -7200 # Node ID 4baf44a943b12c6f57cfba06dc3ecd0587c43a0d # Parent 73351400ca6dff58043fa84a7081011275c109b8 Redefined the get_distinct_instances method on _Class to return a dictionary mapping instances to distinct instances. Added a program fixer which replaces instances with those from a distinct set for each class. Updated the viewer and test program to show only distinct instances when requested. diff -r 73351400ca6d -r 4baf44a943b1 simplify/fixinstances.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simplify/fixinstances.py Mon Jun 18 01:47:51 2007 +0200 @@ -0,0 +1,197 @@ +#!/usr/bin/env python + +""" +Fix instances, removing those which are not part of the distinct set for a given +class. + +Copyright (C) 2006, 2007 Paul Boddie + +This software is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. + +This software is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public +License along with this library; see the file LICENCE.txt +If not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +-------- + +To use this module, the easiest approach is to use the fix function: + +fix(module) + +The more complicated approach involves instantiating a Fixer object: + +fixer = Fixer() + +Then, applying the fixer to an existing module: + +fixer.process(module) +""" + +from simplify.simplified import * + +# Fixing of instance information. + +class Fixer(Visitor): + + """ + The name fixer which traverses the program nodes in a module, typically + depth-first, and eliminates references to superfluous instances, replacing + them with those from each class's distinct list, if necessary. + + See the simplify.fixnames.Fixer class for a description of the + """ + + def __init__(self): + + "Initialise the name fixer." + + Visitor.__init__(self) + + # Satisfy visitor issues. + + self.visitor = self + + def process(self, module): + + "Process the given 'module'." + + # The fixer maintains a list of transformed subprograms (added for each + # of the processing "roots" and also for each invoked internal + # subprogram), along with a list of current subprograms (used to avoid + # recursion issues) and a list of current namespaces (used to recall + # namespaces upon invoking internal subprograms). + + self.subprograms = [] + self.current_subprograms = [] + + self.module = module + self.process_node(self.module) + + # Then, process all functions and methods. + + for subprogram in self.module.simplifier.subprograms: + + # Internal subprograms are skipped here and processed specially via + # Invoke nodes. + + if not getattr(subprogram, "internal", 0): + for specialised in subprogram.active(): + self.subprograms.append(self.process_node(specialised)) + + def process_node(self, node, namespace=None): + + """ + Process a subprogram or module 'node', discovering from attributes on + 'node' any initial locals. Return a modified subprogram or module. + """ + + # Do not process subprograms already being processed. + + if node in self.current_subprograms: + return None + + # Record the current subprogram. + + self.current_subprograms.append(node) + + # Dispatch to the code itself. + + result = self.dispatch(node) + + # Restore the previous subprogram and namespace. + + self.current_subprograms.pop() + + return node + + # Visitor methods. + + def default(self, node): + + """ + Process the given 'node', given that it does not have a specific + handler. + """ + + for name in ("non_accesses", "non_writes", "raises", "returns", "types"): + if hasattr(node, name): + attrs = getattr(node, name) + self._replace(attrs) + for name in ("accesses", "writes"): + if hasattr(node, name): + d = getattr(node, name) + for expr, attrs in d.items(): + self._replace(attrs, name) + + for attr in ("pos_args",): + if hasattr(node, attr): + self.dispatches(getattr(node, attr)) + for attr in ("kw_args",): + if hasattr(node, attr): + self.dispatch_dict(getattr(node, attr)) + for attr in ("expr", "lvalue", "test", "star", "dstar"): + if hasattr(node, attr): + self.dispatch(getattr(node, attr)) + for attr in ("body", "else_", "handler", "finally_", "code", "choices", "nodes"): + if hasattr(node, attr): + self.dispatches(getattr(node, attr)) + + return node + + def _replace(self, attrs, name=None): + to_replace = {} + for attr in attrs: + if name == "accesses": + _attr, accessor = attr + value = _attr.type + else: + value = attr.type + if isinstance(value, Instance) and not to_replace.has_key(value): + distinct_instances = value.get_class().get_distinct_instances() + if distinct_instances.has_key(value): + attr.type = distinct_instances[value] + + def dispatch(self, node, *args): + return Visitor.dispatch(self, node, *args) + + def visitInvokeFunction(self, invoke): + + "Transform the 'invoke' node, performing processing on subprograms." + + return self.default(invoke) + + def visitInvokeRef(self, invoke): + + "Transform the 'invoke' node, performing processing on subprograms." + + # The special case of internal subprogram invocation is addressed by + # propagating namespace information to the subprogram and processing it. + + if invoke.share_locals: + subprogram = self.process_node(invoke.ref, self.namespace) + else: + subprogram = self.process_node(invoke.ref) + + if subprogram is not None: + self.subprograms.append(subprogram) + return invoke + +# Convenience functions. + +def fix(module): + + "Fix the instances in the given 'module'." + + fixer = Fixer() + fixer.process(module) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 73351400ca6d -r 4baf44a943b1 simplify/simplified/data.py --- a/simplify/simplified/data.py Mon Jun 18 00:35:46 2007 +0200 +++ b/simplify/simplified/data.py Mon Jun 18 01:47:51 2007 +0200 @@ -52,7 +52,7 @@ names.add(name) return names - def get_names_to_instances(self): + def get_names_to_instances(self, distinct=0): """ Return a tuple containing a mapping from names to instances, and a list @@ -61,21 +61,29 @@ d = {} names = [] - for instance in self.instances.values(): + if distinct: + instances = set(self.get_distinct_instances().values()) + else: + instances = self.instances.values() + + for instance in instances: name = instance.full_name() names.append(name) d[name] = instance + names.sort() return d, names def get_distinct_instances(self): """ - Return a list of instances whose instance attribute types are distinct. + Return a dictionary mapping instances to a set of instances whose + attribute types are distinct. """ - instances = [] + instances = {} names_found = [] + instances_found = [] # Rather than use the instances directly, get them in name order in # order to favour those earlier according to the sorting. @@ -85,10 +93,13 @@ for instance_name in instance_names: instance = names_to_instances[instance_name] names = instance.namespace.names - if not names in names_found: - instances.append(instance) + try: + i = names_found.index(names) + instances[instance] = instances_found[i] + except ValueError: names_found.append(names) - + instances_found.append(instance) + return instances class SingleInstanceClass(_Class): diff -r 73351400ca6d -r 4baf44a943b1 simplify/viewer.py --- a/simplify/viewer.py Mon Jun 18 00:35:46 2007 +0200 +++ b/simplify/viewer.py Mon Jun 18 01:47:51 2007 +0200 @@ -185,8 +185,9 @@ class Summariser(Writer): - def __init__(self, stream): + def __init__(self, stream, distinct): self.stream = stream + self.distinct = distinct def process(self, module): self.module = module @@ -225,7 +226,7 @@ # Write instances for the class, along with type details for each attribute. - names_to_instances, instance_names = structure.get_names_to_instances() + names_to_instances, instance_names = structure.get_names_to_instances(self.distinct) for instance_name in instance_names: instance = names_to_instances[instance_name] @@ -1285,10 +1286,10 @@ browser = Browser(stream or sys.stdout) browser.process(module.original) -def makesummary(module, filename): +def makesummary(module, filename, distinct=0): stream = open(filename, "wb") try: - summariser = Summariser(stream) + summariser = Summariser(stream, distinct=distinct) summariser.process(module) finally: stream.close() @@ -1301,12 +1302,12 @@ finally: stream.close() -def makedocs(module, modules, builtins): +def makedocs(module, modules, builtins, distinct=0): dirname = "%s-docs" % module.name if not os.path.exists(dirname): os.mkdir(dirname) for m in [module, builtins] + modules: makedoc(m, os.path.join(dirname, "%s%sxhtml" % (m.name, os.path.extsep))) - makesummary(m, os.path.join(dirname, "%s%s%sxhtml" % (m.name, "-summary", os.path.extsep))) + makesummary(m, os.path.join(dirname, "%s%s%sxhtml" % (m.name, "-summary", os.path.extsep)), distinct=distinct) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 73351400ca6d -r 4baf44a943b1 test.py --- a/test.py Mon Jun 18 00:35:46 2007 +0200 +++ b/test.py Mon Jun 18 01:47:51 2007 +0200 @@ -16,6 +16,7 @@ simplified.set_prolific_multiple_instance_mode() import simplify.viewer + import simplify.fixinstances from simplify.annotate import AnnotationError, Importer, load importer = Importer(sys.path) @@ -25,7 +26,9 @@ except simplified.SimplifiedError, exc: raise else: + if "-i" in sys.argv: + simplify.fixinstances.fix(module) if "-d" in sys.argv: - simplify.viewer.makedocs(module, importer.modules.values(), builtins) + simplify.viewer.makedocs(module, importer.modules.values(), builtins, distinct=("-i" in sys.argv)) # vim: tabstop=4 expandtab shiftwidth=4